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.
Environment variables
Section titled “Environment variables”| 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 |
Schema & seeds
Section titled “Schema & seeds”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:
brutor-mcp-registry # serve (default)brutor-mcp-registry list --namespace io.github --limit 20brutor-mcp-registry show io.github.acme/widgetbrutor-mcp-registry remove io.github.acme/widgetbrutor-mcp-registry statsbrutor-mcp-registry sync # run one aggregator cycle nowOperating it
Section titled “Operating it”- Health:
GET /health—503with 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 Rust port (0.11+)
Section titled “The Rust port (0.11+)”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,
_metashapes, 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.

