Skip to content

Deploy Mission Control

Mission Control supports deployment from source, reviewed release artifacts, or per-service container images. The Console and gateway share one PostgreSQL database and a reviewed YAML catalog. Managed Agents use a private Python runtime; standard OTLP uses the Collector and ClickHouse trace backend.

Choose a deployment path

Your environment Start here You provide
Local development or a source evaluation Run from source Required runtimes, databases and service environments
Existing immutable-release/systemd host Deploy to an existing VPS The documented host layout, TLS, networking and credentials
Your own container platform Container images Orchestration, storage, TLS and mounted configuration/secrets

Tagged releases publish signed images for gateway, Console, Agent runtime, Collector and migrations. Helm charts and cloud infrastructure modules are not included. The ClickHouse Compose fixture is a reference backend, not a complete production deployment.

An optional private ClickHouse host bootstrap prepares a resource-capped private node and the released telemetry role boundary. The bootstrap alone does not apply the Mission Control schema or start the Collector; after provisioning, follow the telemetry runbook to migrate and connect the current standard OTLP plane.

Supported topology

flowchart TB
    Proxy[Operator-managed TLS reverse proxy]
    Proxy --> Console[Console · Next.js · port 3000]
    Proxy --> Gateway[Gateway · Hono · port 4000 · one or several instances]
    Gateway --> Runtime[Agent runtime · PydanticAI · port 4100]
    Runtime --> Gateway
    External[External PydanticAI process] --> Gateway
    External --> Collector[Telemetry Collector · 4317/4318]
    Collector --> Gateway
    Gateway --> ClickHouse[(ClickHouse metadata + permitted observations)]
    Console --> ClickHouse
    Console --> Database[(PostgreSQL database)]
    Gateway --> Database
    Console --> Mail[SMTP]
    Gateway --> Catalog[Global + organization YAML]
    Gateway --> Provider[Configured model providers]
    Gateway --> Server[MCP Streamable HTTP server]

Use one DATABASE_URL for bootstrap, Console, gateway, the command-line tools, backup, and restore. Several gateway instances may share it behind a load balancer; Run several gateway instances gives the balancer contract and the rolling restart. The database holds governance records together with definition instructions and Chat transcripts, and must be protected as application content. The Console reaches private control through GATEWAY_MANAGEMENT_URL and inference through GATEWAY_PUBLIC_URL; browsers never call the gateway for Console Chat. External clients reach the public gateway API routes, including model discovery, Chat Completions, Responses, embeddings, reranking, MCP and deployment invoke. MCP ingress and Connector egress use only stateless revision 2026-07-28, with no legacy fallback. Browser MCP/deployment invoke origins use exact MCP_ALLOWED_ORIGINS and AGENT_ALLOWED_ORIGINS entries. Only the gateway reaches the private Agent runtime. OTLP clients reach only TLS Collector listeners; the Collector reaches signed private telemetry control/writer only, with no ClickHouse credential. Gateway owns the writer/cleanup identities.

Prerequisites

  • Node.js >=24 <25.
  • pnpm 11.22.0.
  • PostgreSQL 16 or newer, with a database created by the Postgres superuser with C collation and owned by a dedicated login role (see Bootstrap and first sign-in), and psql, pg_dump and pg_restore for backups.
  • Read access to the global and organization YAML files.
  • SMTP for magic-link and invitation delivery outside development.
  • Network routes from the gateway to configured model providers and MCP servers.
  • Python >=3.12,<3.15 and uv for the agent runtime service (required for test chat) and for the optional reference adapter, with network access from that process to the gateway.
  • Operator-managed TLS, ingress restrictions, egress restrictions, process supervision, and backup scheduling.
  • For standard OTLP: Go 1.25.14, a supported Collector build/runtime, ClickHouse 26.3.21.7 for the reference contract, bounded synchronous ingestion and separate TLS/ClickHouse identities.

Install and build

Run from platform/:

pnpm install --frozen-lockfile
pnpm build
pnpm --filter @mission-control/database db:migrate

db:migrate needs DATABASE_URL and applies the release's migrations; the gateway, the Console and connector:rotate-credentials refuse to start with STORAGE_MIGRATION_REQUIRED: run pnpm --filter @mission-control/database db:migrate until it has run. It refuses, before applying anything, a database not created with the libc C collation and character type (storage_collation_refused). The lockfile is authoritative. Do not deploy with a different Node.js major version or pnpm version; the workspace declares both in platform/package.json.

Deploy to an existing VPS

The existing-VPS runbook covers the repository deployment helper, its required host layout, configurable defaults, verification, and failure recovery. It applies only to an already provisioned immutable-release/systemd host.

Purpose

See Purpose.

Prerequisites

See Prerequisites.

Procedure

See Procedure.

Configurable defaults

See Configurable defaults.

Verification

See Verification.

Failure recovery

See Failure recovery.

Prepare durable paths

One example layout is:

/srv/mission-control/
  config/mission-control.yaml
  config/organizations/acme.yaml
  backups/

The service account needs read access to config/ and write access to backups/. The data lives in the PostgreSQL database, not in this directory. Configuration validation resolves organization files relative to the global configuration directory and rejects organization paths that escape it.

Configure, bootstrap, and start

  1. Define the Console, split gateway, Agent runtime, Collector, and ClickHouse settings in Configure the services.
  2. Create the database and first owner using Bootstrap and first sign-in.
  3. Start the gateway process, which binds inference and private control, from platform/:

    pnpm --filter @mission-control/gateway start
    
  4. Start the Collector, then start the Agent runtime from runtime/agent-runtime/ (see Configure the services):

    uv run --locked mission-control-agent-runtime
    
  5. Start the Console from platform/:

    pnpm --filter @mission-control/console start
    
  6. Wait for Agent runtime, Collector, and Console heartbeats, then require private http://127.0.0.1:4001/readyz to return 200.

The production start commands require a successful pnpm build. Development uses each package's dev command and is not a substitute for process supervision.

Network exposure

  • Expose the console only through authenticated TLS ingress.
  • Expose public gateway routes only to intended agent/client networks.
  • Do not expose private control /healthz, /readyz, or /internal/* routes. They are operator or signed service-to-service endpoints.
  • Do not expose gateway telemetry-internal routes. The Collector signs them with a separate secret.
  • Do not expose the Agent runtime service; keep it on loopback or a private network.
  • Expose Collector OTLP listeners only through TLS to intended Agent networks; keep its health listener loopback/private.
  • Keep ClickHouse native/HTTP ports private and enforce the separate insert/read/delete/backup roles.
  • Restrict gateway egress to configured provider and MCP destinations.
  • Set TRUSTED_ORIGINS, MCP_ALLOWED_ORIGINS, and AGENT_ALLOWED_ORIGINS to exact externally used origins. Verify allowed and denied MCP preflights after every gateway restart; never use a wildcard.
  • Keep the database port private to the service hosts. Keep YAML, mail outbox, backups, and service environment outside static web roots.

Test databases for contributors

The package test suites and the Playwright journeys need a PostgreSQL server of their own, never the production one. From platform/, pnpm db:test:up starts a throwaway server in Docker on 127.0.0.1:5433 with durability off; pnpm db:test:down discards it. The tests read MISSION_CONTROL_TEST_DATABASE_URL (default postgres://postgres@localhost:5433/postgres) and clone one migrated template database per test; the journeys use MISSION_CONTROL_E2E_DATABASE_URL (default postgres://postgres@127.0.0.1:5433/mission_control_e2e), which they drop and recreate, and they refuse a database whose name does not contain e2e. The suites and the journeys run slower on Docker Desktop, because every statement pays a network round trip into the container: a full journey run takes about 30 minutes. See Environment variables.

Verify the deployment

From the private control host, require liveness/readiness:

curl --fail http://127.0.0.1:4001/healthz
curl --fail http://127.0.0.1:4001/readyz

Expected JSON is {"status":"ok"} and {"status":"ready"}. From an allowed inference host, omit a key and require the boundary to authenticate:

curl --output /dev/null --write-out '%{http_code}\n' \
  https://gateway.example.test/v1/models

Expected status: 401. Then request a magic link, inspect the loaded organization catalog, allow one model in Default Team, create an ordinary virtual key, and complete a bounded model call. No Agent registration is required.

Production checklist

  • Secrets are generated independently and stored outside the repository.
  • Console and gateway use the same DATABASE_URL, and the connection string is stored as a secret.
  • Console and gateway use the same API_KEY_PEPPER and INTERNAL_SERVICE_SECRET; Collector uses internal transport as MISSION_CONTROL_INTERNAL_SERVICE_SECRET; runtime uses separate inbound/heartbeat secrets and never the gateway-only derivation master.
  • The database was created with C collation and every migration is applied (db:migrate).
  • SMTP is selected; console mail is disabled by NODE_ENV=production.
  • TLS and ingress/egress restrictions are active.
  • pg_dump and YAML backups are scheduled and a pg_restore into a separate database has been rehearsed.
  • Both processes restart automatically and preserve the durable paths.
  • Private control /healthz and /readyz are monitored without exposing them through public inference.
  • When enabled, Collector health, writer availability, bounded batches/retries, ClickHouse health, content scans, retention, and encrypted backup/restore are monitored and rehearsed.
  • The loaded configuration and governed-call behavior are verified after restart; upstream health is diagnostic.

See Environment variables and Organization YAML. Telemetry operations documents Collector/ClickHouse deployment, backup, recovery, and rollback.