KB LabsDocs

Studio

Last updated April 7, 2026


The KB Labs web UI — a React app that hosts plugin pages via Module Federation.

Studio is the web-based dashboard for KB Labs. It's a React SPA built with rspack and Ant Design, fronted by the platform's data client and federation layer. Studio talks to the REST API, the workflow daemon, and the marketplace service over HTTP and WebSocket; it also dynamically loads plugin-authored pages at runtime via Module Federation.

Source lives in platform/kb-labs-studio/.

What it does

  • Workflow runs — browse, inspect, and trigger runs; watch jobs and steps as they execute; resolve approvals.
  • Plugin pages — every plugin that declares a studio block in its manifest gets its own page or menu entry. Studio loads those pages dynamically as Module Federation remotes.
  • System status — health of services, adapter registration, log streams, metrics dashboards.
  • Marketplace — browse installed entities, install new ones, enable/disable plugins.
  • Analytics dashboards — charts and time-series views over the analytics adapter (events per day, LLM cost trends, cache hit rates, etc.).

Stack

The app lives under apps/studio/. Core dependencies:

  • React + Ant Design (antd, @ant-design/charts, @ant-design/plots, @ant-design/icons) — component library and charts.
  • rspack — the build tool (fast Rust-based bundler, webpack-compatible).
  • TanStack Query (@tanstack/react-query) — server state caching and synchronization.
  • dayjs, clsx, dompurify — utility libraries.

The app is a SPA — there's no SSR. The dev server runs via rspack serve; production is rspack build + static hosting.

Packages

The Studio monorepo has several supporting packages:

PackagePurpose
@kb-labs/studio-data-clientHTTP + WebSocket client for the REST API, workflow daemon, and marketplace. Wraps fetch with correlation IDs, retries, and typed responses.
@kb-labs/studio-federationModule Federation runtime — loads plugin remotes at startup based on the manifest registry.
@kb-labs/studio-hooksReact hooks for data fetching: useData, useMutate, useSSE, useWebSocket, etc.
@kb-labs/studio-event-busIn-app event bus for decoupled component communication.
@kb-labs/studio-ui-coreCore shell — layout, navigation, theme provider, auth.
@kb-labs/studio-ui-kitHigher-level UI primitives built on Ant Design: tables, forms, metric cards, diff viewers.
@kb-labs/studio-plugin-toolsHelpers plugins use when building their own Studio pages.
@kb-labs/studio-devtoolsDeveloper tools panel — inspect federation, data cache, event bus.

Module Federation

The plugin system and Studio are integrated via Module Federation. Every plugin that declares studio in its manifest becomes a remote:

TypeScript
studio: {
  version: 2,
  remoteName: 'commitPlugin',
  pages: [
    {
      id: 'commit.overview',
      title: 'Commit',
      icon: 'GitlabOutlined',
      route: '/p/commit',
      entry: './CommitOverview',
      order: 1,
    },
  ],
  menus: [
    {
      id: 'commit',
      label: 'Commit',
      icon: 'GitlabOutlined',
      target: 'commit.overview',
      order: 30,
    },
  ],
}

At Studio startup, the federation layer discovers installed plugins via the REST API's plugin listing, reads each plugin's studio config, and loads the corresponding remote bundle. Pages are mounted at the declared routes; menus are added to the sidebar.

remoteName is the Module Federation remote identifier — what Studio uses to load the bundle. entry is the exposed module path inside that remote. route is the URL path where the page mounts in the Studio SPA.

See Plugins → Studio Pages for the authoring side.

Hooks

Plugins that build Studio pages import from @kb-labs/studio-hooks. The common ones:

  • useData(endpoint, options?) — GET with React Query caching.
  • useMutate(endpoint, options?) — POST / PUT / PATCH / DELETE with optimistic updates.
  • useSSE(endpoint) — subscribe to a server-sent events stream.
  • useWebSocket(url, options?) — bidirectional WebSocket with auto-reconnect.
  • useEventBus(topic, handler) — subscribe to the in-app event bus.

These hooks handle the cross-cutting concerns: correlation IDs in headers, auth tokens, retry policy, loading states, error boundaries. Plugin pages just call the hook and render the result.

Plugin SDK for Studio

Plugin pages get a scoped SDK via @kb-labs/studio-plugin-tools and @kb-labs/sdk. The available imports are intentionally narrow:

  • @kb-labs/sdk — plugin contracts, SDK types, Studio config types (StudioConfig, StudioPageEntry, StudioMenuEntry).
  • UI kit data contracts re-exported from @kb-labs/sdkTableData, SelectData, MetricData, ListData, etc. REST endpoints that return these shapes can be rendered directly by the matching UI kit components.
  • @kb-labs/studio-plugin-tools — utilities specifically for plugin pages (lifecycle, event wiring, isolated state).

Plugin Studio pages are not allowed to import internal Studio packages. @kb-labs/studio-ui-core, @kb-labs/studio-federation, @kb-labs/studio-data-client are internal implementation packages — plugins must use @kb-labs/sdk plus whatever public contracts are re-exported. This is a deliberate seam: internal Studio packages evolve independently, and coupling plugin code to them would break every plugin on every internal refactor.

Running Studio

Dev mode

Bash
cd platform/kb-labs-studio/apps/studio
pnpm dev

rspack starts a dev server with HMR. The default port is 3000 (configurable in rspack.config.*). The app connects to the gateway at http://localhost:4000 by default — edit the dev server config or set env vars to point at a different gateway.

Or via kb-dev:

Bash
kb-dev start studio

Production build

Bash
cd platform/kb-labs-studio/apps/studio
pnpm build

Outputs a static bundle to dist/. Serve with any static host (nginx, Caddy, Vercel, or — in development — pnpm preview).

Talking to services

Studio doesn't talk to the REST API, workflow daemon, and marketplace directly — it goes through the gateway. The gateway aggregates upstream services under a single origin, handles authentication, and terminates CORS.

This is important for plugin pages too: when a plugin page calls useData('/api/v1/plugins/mind/search'), it's calling the gateway, which routes to the REST API, which executes the plugin's REST handler. Everything stays behind one origin from the browser's perspective.

See Gateway for the routing rules.

Studio — KB Labs Docs