Skip to content

Deployment & operations

The registry ships as a single container image (ghcr.io/brutor-ai/brutor-mcp-registry) and is part of the trial bundle compose stack. It needs only PostgreSQL.

Variable Example Notes
DATABASE_URL postgresql://brutor:...@db:5432/brutor Required. Shares the platform database or its own — the registry owns only mcp_registry_* tables
JWT_SECRET change-me-in-prod Must match the platform’s so Console service tokens validate. Empty = dev mode (any bearer authenticates)
HOST / PORT 0.0.0.0 / 5001 Bind address
CORS_ALLOW_ALL false Enable only behind a trusted proxy
REGISTRY_SSRF_ALLOW_HOSTS registry.corp.internal Hostnames exempt from the SSRF public-address check on connections and verification fetches; host.docker.internal is always exempt
LOG_LEVEL / USE_JSON_LOGGING INFO / true JSON logs for shipping; a 500-entry ring buffer backs /logs either way
AGGREGATOR_ENABLED true Scheduled upstream sync
AGGREGATOR_INTERVAL_SECONDS 3600 First run ~15 s after boot
GOVERNANCE_ENABLED true Enrich records with gateway-governance posture (fail-open; default false)
GOVERNANCE_API_BASE_URL http://control-plane:5050 Control plane base URL. The lookup is GET {base}/v1/internal/governance/mcp-servers
GOVERNANCE_TENANT_ID default Required. The projection is tenant-scoped and a service token carries no tenant
GOVERNANCE_API_TOKEN Optional. The registry normally mints its own short-lived service token from the shared JWT_SECRET, so no admin credential is stored here
GOVERNANCE_CACHE_TTL_SECONDS 60 Posture cache; lookups are fail-open
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET Enables GitHub namespace verification
GITHUB_REDIRECT_URI https://registry.acme.com/auth/github/callback Defaults to http://localhost:5001/auth/github/callback

On boot the registry applies its idempotent SQL migrations under a PostgreSQL advisory lock (safe with multiple replicas racing at startup), then runs create-once seeds: three default connections (the official MCP Registry, a disabled Smithery entry, a disabled local-dev placeholder) and three example servers are inserted only when the respective tables are empty — operator edits are never overwritten by a restart.

The same binary is the CLI; every subcommand works against DATABASE_URL:

Terminal window
brutor-mcp-registry # serve (default)
brutor-mcp-registry list --namespace io.github --limit 20
brutor-mcp-registry show io.github.acme/widget
brutor-mcp-registry remove io.github.acme/widget
brutor-mcp-registry stats
brutor-mcp-registry sync # run one aggregator cycle now
  • Health: GET /health503 with a JSON body when the database is unreachable. The compose healthcheck allows a 120 s start period.
  • Telemetry: /metrics (counters + response-time stats) and /logs (ring buffer) — both require auth. See Console administration for the UI view.
  • Backups: the registry state is entirely in mcp_registry_* tables — covered by your normal PostgreSQL backup.

The service was reimplemented in Rust (Axum/SQLx), replacing the Python/FastAPI implementation. For operators this is a drop-in swap:

  • Same image name, port, env vars, healthcheck contract and database schema — existing databases are picked up as-is; no migration step.
  • Same wire surface, verified by a parity test suite (cursor formats, _meta shapes, status codes, auth semantics).

Two behaviors are strictly better: GitHub namespace verification completes (the Python flow lost the OAuth session), and DNS/HTTP challenge checking exists — previously challenges could be minted but never confirmed.