Nexus Hub Deployment Guide¶
This guide shows how to run a Nexus hub — a shared MCP server that many agents connect to with individual bearer tokens. Hub mode is the deployment pattern introduced by issue #3784; the underlying runtime is unchanged from the standard NEXUS_PROFILE=full stack.
0. Architecture¶
The reference compose ships two Nexus services:
nexus—nexusdRPC server on port 2026, backed by postgres (auth) + redis (metrics). This is the source of truth for auth, zones, and files.mcp-frontend—nexus mcp serve --transport http --url http://nexus:2026on port 8081. It extracts the bearer from each request and opens a per-requestNexusFSremote connection to the RPC server with that token as the api key, so the RPC server'sDatabaseAPIKeyAuthenforces per-token identity/zone on every tool call. Missing/invalid/expired/ revoked tokens are rejected with 401 at the RPC layer.
mcp-frontend deliberately does not set NEXUS_API_KEY; unauthenticated requests therefore fail at the RPC layer rather than falling through to an ambient frontend identity.
As a belt-and-braces safeguard, the reference compose also sets NEXUS_MCP_REQUIRE_BEARER=true on mcp-frontend. That flag makes the MCP HTTP middleware reject every request that arrives without a bearer (Authorization: Bearer sk-… or X-Nexus-API-Key: sk-…) with 401 before the tool layer runs — so even a later operator misconfiguration that sets NEXUS_API_KEY on this container cannot silently promote unauthenticated traffic to the frontend's ambient identity. Keep NEXUS_MCP_REQUIRE_BEARER=true whenever this container is reachable from untrusted clients.
1. Quickstart¶
# 1. Get the repo and the reference compose file.
git clone https://github.com/nexi-lab/nexus.git
cd nexus
# 2. Set the Postgres password (use a secret manager in production).
export POSTGRES_PASSWORD="$(openssl rand -base64 32)"
# 3. Start the stack.
docker compose -f docker-compose.hub.yml up -d
# 4. Create the first admin token (bootstrap).
# Run inside the `nexus` (RPC) container — it has direct Postgres access.
docker compose -f docker-compose.hub.yml exec nexus \
nexus hub token create --name root --admin --zone root
# → prints the raw token once. Save it immediately; it cannot be retrieved.
The MCP endpoint is now at http://<host>:8081/mcp. Terminate TLS at a reverse proxy before exposing it to the internet (see §6).
2. Token lifecycle¶
- Create locally —
nexus hub token create --name <name> --zone <zone> [--admin] [--expires 90d]. Use--zones eng:rw,ops:rfor multi-zone tokens or--zones-glob 'team-*'to resolve active zones at mint time. The raw token (sk-…) is printed once. - List locally —
nexus hub token list [--show-revoked] [--json]. - Revoke locally —
nexus hub token revoke <name|key_id>(soft-delete). - Remote admin — after creating a bootstrap admin token on the hub host, pass
--remote <hub-url>and--admin-token <sk-admin>totoken create,token list,token revoke, orstatus. Host URLs are normalized to/mcp, sohttps://nexus.example.comandhttps://nexus.example.com/mcpboth work.NEXUS_HUB_ADMIN_TOKENmay be used instead of repeating--admin-token.
The auth identity cache has a 60-second TTL, so a revoked token may remain usable for up to 60 seconds. Plan rotations around this window.
Example remote flow:
# On the hub host:
nexus hub token create --name root --admin --zone root
# From an operator workstation:
export NEXUS_HUB_ADMIN_TOKEN=sk-...
nexus hub token list --remote https://nexus.example.com --json
nexus hub status --remote https://nexus.example.com
3. Zone model (MVP)¶
Each token is scoped to one or more zones. Requests made with the token only see files and indexes in its allowed zones. --zone is kept as a single-zone alias; prefer --zones <zone[:perms],...> for new scripts.
List zones: nexus hub zone list.
4. Agent client config¶
Clients authenticate with either header:
Client-side configuration depends on the agent framework — consult the MCP client docs for Claude Code, Codex, or Goose and supply the endpoint URL + one of the headers above.
5. Operations¶
nexus hub status— endpoint, profile, postgres/redis health, token counts, connections, and 5-minute QPS. JSON output via--json.- Audit logs — every MCP request emits a JSON line to stdout (
docker compose logs -f nexus) and publishes to Redis channelnexus:audit:mcpfor external consumers. - Prometheus metrics — the reference hub compose enables
NEXUS_MCP_METRICS_ENABLED=true; scrapehttp://<hub-host>:8081/metricsfor MCP request counts, latency, active clients, and errors. - Rate limits — configured per tier in the existing rate-limit middleware. See
src/nexus/bricks/mcp/middleware_ratelimit.py.
6. TLS¶
Hub mode does not ship with built-in TLS. Run a reverse proxy.
Caddy (minimal):
Nginx (excerpt):
server {
listen 443 ssl http2;
server_name nexus.example.com;
ssl_certificate /etc/letsencrypt/live/nexus.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/nexus.example.com/privkey.pem;
location /mcp {
proxy_pass http://127.0.0.1:8081/mcp;
proxy_set_header Host $host;
proxy_http_version 1.1;
}
}
7. Backup & restore¶
Postgres is the system of record (tokens, zones, index metadata). Run pg_dump on a schedule:
The nexus-data volume holds local indexes and transient state; back it up with any standard volume backup tool.
8. Troubleshooting¶
| Symptom | Likely cause |
|---|---|
nexus hub status shows postgres: err | NEXUS_DATABASE_URL mis-set or Postgres unhealthy. Check docker compose ps. |
redis: n/a in status | Redis unreachable. Metrics degrade (qps/connections become n/a); serving continues. |
| Client gets 401 just after revoke | Auth cache TTL — wait 60 s or restart the MCP server. |
| 429 from client | Rate limit tier; see middleware_ratelimit.py. |
CLI exits RuntimeError: NEXUS_DATABASE_URL not set | Run nexus hub … inside the container (docker compose exec nexus nexus hub …) or export the env var manually. |
9. What's next¶
Tracked as follow-up issues to #3784:
- Prometheus
/metricsendpoint - Richer
hub status(Zoekt/txtai queue depth, per-zone breakdown) - Kubernetes/Helm deploy