How the Plane Admin Panel Manages Instance-Level Settings: Architecture and Implementation

The Plane admin panel uses a reactive MobX store (InstanceStore) paired with a thin REST service layer (InstanceService) to fetch, cache, and update instance-wide configuration through a dedicated UI in apps/admin.

The makeplane/plane repository provides a dedicated admin interface located at apps/admin that allows instance administrators to view and edit deployment-wide configuration. Understanding how this admin panel manages instance-level settings reveals a clean architectural separation between state management, API communication, and UI components that ensures a single source of truth across the entire administrative experience.

Architecture Overview

The admin panel follows a four-layer separation of concerns to handle instance-level settings:

  1. State & API Layer – instance.store.ts manages reactive state using MobX and coordinates with backend services.
  2. Service Layer – InstanceService wraps REST API endpoints under /api/instances/*.
  3. UI Components – Forms such as InstanceSetupForm and InstanceFormHeader render settings and handle user input.
  4. Context Provider – instance.provider.tsx injects the store into the React component tree, enabling reactive data access throughout the admin views.

State Management with InstanceStore

The InstanceStore class in apps/admin/store/instance.store.ts serves as the single source of truth for instance data. It holds the current instance record and loading state as MobX observables, ensuring that any UI component referencing these values automatically re-renders when changes occur.

The store exposes two primary methods for managing instance-level settings:

  • fetchInstance() – Retrieves the current instance metadata via GET request.
  • updateInstance(payload) – Persists partial updates to the instance configuration via PATCH request.
// apps/admin/store/instance.store.ts
export class InstanceStore {
  @observable instance?: IInstance = undefined;
  @observable isLoading = false;

  async fetchInstance() {
    this.isLoading = true;
    const data = await instanceService.get();
    runInAction(() => {
      this.instance = data;
      this.isLoading = false;
    });
  }

  async updateInstance(payload: Partial<IInstance>) {
    this.isLoading = true;
    const data = await instanceService.patch(payload);
    runInAction(() => {
      this.instance = data;
      this.isLoading = false;
    });
  }
}

Both methods wrap the asynchronous service calls in runInAction blocks to ensure MobX properly tracks state mutations and notifies all observers.

API Abstraction via InstanceService

The InstanceService class in packages/services/src/instance/instance.service.ts provides a thin abstraction over the REST API. It handles communication with four key endpoints that manage instance-level settings:

  • GET /api/instances/ – Retrieves current instance metadata.
  • PATCH /api/instances/ – Updates mutable fields such as instance name, email domain, or telemetry flags.
  • GET /api/instances/configurations/ – Fetches advanced configuration objects including email credentials.
  • PATCH /api/instances/configurations/ – Persists changes to advanced configuration objects.
// packages/services/src/instance/instance.service.ts
export class InstanceService extends APIService {
  async get() {
    return this.get("/api/instances/", { validateStatus: null });
  }

  async patch(data: Partial<IInstance>) {
    return this.patch("/api/instances/", data);
  }

  async getConfigurations() {
    return this.get("/api/instances/configurations/");
  }

  async patchConfigurations(data: Partial<IFormattedInstanceConfiguration>) {
    return this.patch("/api/instances/configurations/", data);
  }
}

This service layer centralizes API logic, making the store agnostic of HTTP implementation details and enabling easier testing and maintenance.

React Context and Dependency Injection

To ensure all admin panel components share the same reactive state, the InstanceProvider in apps/admin/providers/instance.provider.tsx creates a singleton InstanceStore instance and injects it into the React context tree. This provider fetches instance data immediately upon mounting, guaranteeing that settings are available before any child components render.

// apps/admin/providers/instance.provider.tsx
export const InstanceProvider = ({ children }: Props) => {
  const store = useMemo(() => new InstanceStore(), []);
  useEffect(() => {
    store.fetchInstance();
  }, [store]);

  return <InstanceContext.Provider value={store}>{children}</InstanceContext.Provider>;
};

Any component within the admin panel can access this store via the useInstanceStore hook, enabling reactive reads and writes to instance-level settings without prop drilling.

UI Forms and Components

The admin panel renders instance settings through specialized React components that bind to the MobX store. Two critical components handle distinct phases of instance management:

InstanceSetupForm (apps/admin/components/instance/setup-form.tsx) handles first-time administrator creation. Unlike standard settings updates, this form submits directly to POST /api/instances/admins/sign-up/ with a traditional HTML form POST to handle initial authentication setup, including CSRF token validation.

// apps/admin/components/instance/setup-form.tsx
<form
  method="POST"
  action={`${API_BASE_URL}/api/instances/admins/sign-up/`}
  onSubmit={() => setIsSubmitting(true)}
>
  <input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
  {/* email, password, company name fields */}
</form>

Settings Pages (email configuration, telemetry toggles, etc.) read current values from instanceStore.instance and invoke instanceStore.updateInstance() or configuration-specific service methods when users modify fields. Because the store is observable, these components instantly reflect state changes across the entire UI without manual refresh logic.

The InstanceFormHeader component (apps/admin/components/instance/form-header.tsx) provides consistent navigation and breadcrumb structure across all instance-level pages.

The Complete Update Flow

When an administrator modifies instance-level settings, the system executes the following sequence:

  1. Initial Load – InstanceProvider mounts and calls store.fetchInstance(), populating the observable store with current configuration.
  2. Render – UI components read instanceStore.instance to display current values in forms and toggles.
  3. Modification – Admin changes a value (e.g., disables telemetry).
  4. Submission – UI calls instanceStore.updateInstance({ is_telemetry_enabled: false }).
  5. API Request – Store forwards the payload to InstanceService.patch("/api/instances/", payload).
  6. State Update – On success, the store updates its instance observable within a runInAction block.
  7. UI Sync – MobX triggers re-renders in all observing components, immediately displaying the updated configuration.
  8. Error Handling – Any validation or permission errors returned by the API surface via toast notifications or banner components.

Summary

  • The InstanceStore in apps/admin/store/instance.store.ts uses MobX observables to maintain a reactive, single source of truth for instance configuration.
  • The InstanceService in packages/services/src/instance/instance.service.ts abstracts REST API calls to /api/instances/ and /api/instances/configurations/ endpoints.
  • The InstanceProvider in apps/admin/providers/instance.provider.tsx injects the store into the React tree, ensuring all admin components share live state.
  • UI components such as InstanceSetupForm handle both initial setup and ongoing settings management, binding form inputs to the reactive store.
  • Updates flow unidirectionally from UI → Store → Service → API → Store → UI, guaranteeing consistency across the admin panel.

Frequently Asked Questions

What is the role of InstanceStore in the Plane admin panel?

The InstanceStore acts as the central state container for instance-level settings within the admin panel. Implemented as a MobX store in apps/admin/store/instance.store.ts, it holds the current instance record, manages loading states, and exposes fetchInstance() and updateInstance() methods that synchronize the UI with the backend. Its observable properties ensure that any component reading from the store automatically re-renders when configuration changes occur.

Which API endpoints handle instance configuration updates?

According to the InstanceService implementation in packages/services/src/instance/instance.service.ts, instance-level settings are managed through two primary endpoint patterns: /api/instances/ for core metadata (name, telemetry flags) and /api/instances/configurations/ for advanced settings like email credentials. The service uses PATCH requests to persist updates and GET requests to retrieve current state.

How does the admin panel handle first-time instance setup?

First-time setup uses the InstanceSetupForm component in apps/admin/components/instance/setup-form.tsx, which submits a traditional HTML form POST to /api/instances/admins/sign-up/ rather than using the JavaScript API layer. This approach handles initial administrator account creation, CSRF token validation, and captures the company name and telemetry preferences during the instance initialization phase.

What ensures that all admin UI components stay synchronized?

The InstanceProvider in apps/admin/providers/instance.provider.tsx creates a singleton InstanceStore instance and provides it via React Context to the entire admin component tree. Because the store uses MobX observables, any mutation to the instance object—whether from initial fetch or subsequent updates—immediately propagates to all observing UI components without requiring manual state management or refresh logic.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →