From fbdc6dda80d0a8812b79f34cefcce932a7092f54 Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 16 Mar 2026 00:22:43 +0100 Subject: [PATCH] docs(dashboard): add Connecting to the Hub reference page Covers local setup, remote (COULOMBCORE) one-liner registration, ops-bridge tunnel config, bridge states, MCP transport modes, and adding new remote hosts. Registered in Reference nav + reference.md index. Co-Authored-By: Claude Sonnet 4.6 --- dashboard/observablehq.config.js | 1 + dashboard/src/docs/connecting.md | 189 +++++++++++++++++++++++++++++++ dashboard/src/reference.md | 1 + 3 files changed, 191 insertions(+) create mode 100644 dashboard/src/docs/connecting.md diff --git a/dashboard/observablehq.config.js b/dashboard/observablehq.config.js index 1790329..54e316a 100644 --- a/dashboard/observablehq.config.js +++ b/dashboard/observablehq.config.js @@ -43,6 +43,7 @@ export default { collapsible: true, open: false, pages: [ + { name: "Connecting to the Hub", path: "/docs/connecting" }, { name: "Contributions", path: "/docs/contributions" }, { name: "Decision Health", path: "/docs/decisions-kpi" }, { name: "Decisions", path: "/docs/decisions" }, diff --git a/dashboard/src/docs/connecting.md b/dashboard/src/docs/connecting.md new file mode 100644 index 0000000..f2f095e --- /dev/null +++ b/dashboard/src/docs/connecting.md @@ -0,0 +1,189 @@ +--- +title: Connecting to the State Hub — Reference +--- + +# Connecting to the State Hub — Reference + +How Claude Code agents on local and remote machines connect to the State Hub API +and its MCP server. + +--- + +## Architecture overview + +The State Hub runs on the **work laptop** only. Remote machines (COULOMBCORE, +Railiance nodes) never run their own copy — they connect to the single source +of truth via an encrypted SSH reverse tunnel managed by **ops-bridge**. + +``` +Work laptop Remote machine (e.g. COULOMBCORE) +───────────────────── ───────────────────────────────── +PostgreSQL :5432 Claude Code session + ↑ │ +FastAPI :8000 ←── ops-bridge ────→ :18000 (API health / tools) +MCP SSE :8001 ←── ops-bridge ────→ :18001 (MCP for Claude Code) +``` + +Two services are exposed through the tunnel: + +| Service | Local port | Remote port | Purpose | +|---------|-----------|-------------|---------| +| State Hub API | `8000` | `18000` | Health checks, direct curl queries | +| MCP SSE server | `8001` | `18001` | Claude Code MCP integration | + +--- + +## Local setup (work laptop) + +### Start the services + +```bash +cd ~/the-custodian/state-hub + +make api # FastAPI on :8000 +make mcp-http # MCP SSE server on :8001 (separate terminal) +``` + +`make mcp-http` sets `MCP_TRANSPORT=sse MCP_PORT=8001` and starts the same +MCP server that Claude Code uses locally in stdio mode. The SSE mode exposes +the identical tool surface over HTTP. + +### Start the tunnels + +```bash +bridge up # brings up both tunnels defined in ~/.config/bridge/tunnels.yaml +bridge status +``` + +Both tunnels must show `connected` (or `degraded` — see below) before remote +agents can connect. + +### Local Claude Code registration (stdio, default) + +Claude Code on the work laptop uses the stdio MCP server. Registration is in +`~/.claude.json` via `.mcp.json` at the repo root. No changes needed for local +use. + +--- + +## Remote setup (COULOMBCORE or any remote host) + +### Prerequisites + +- ops-bridge tunnels running on the work laptop (`bridge status`) +- SSH key `~/.ssh/id_ops` authorised on the remote host for user `tegwick` + +### One-time MCP registration on the remote + +```bash +claude mcp add-json -s user state-hub \ + '{"type":"sse","url":"http://127.0.0.1:18001/sse"}' +``` + +Restart Claude Code after running this. That's the only setup required — no +Python, no repo clone, no local services. + +### Verify connectivity + +```bash +# API reachable through tunnel? +curl -s http://127.0.0.1:18000/state/health +# → {"status":"ok","db":"connected"} + +# MCP SSE endpoint reachable? +curl -s --max-time 2 http://127.0.0.1:18001/sse | head -2 +# → event: endpoint +# → data: /messages/?session_id=... +``` + +--- + +## ops-bridge tunnel config + +Tunnels are defined in `~/.config/bridge/tunnels.yaml` on the work laptop: + +```yaml +tunnels: + state-hub-coulombcore: # API tunnel + host: 92.205.130.254 + remote_port: 18000 + local_port: 8000 + ssh_user: tegwick + ssh_key: ~/.ssh/id_ops + actor: agent.claude-coulombcore + health_check: + url: http://127.0.0.1:18000/state/health + interval_seconds: 30 + timeout_seconds: 5 + reconnect: + max_attempts: 0 # retry forever + backoff_initial: 5 + backoff_max: 60 + + state-hub-mcp-coulombcore: # MCP SSE tunnel + host: 92.205.130.254 + remote_port: 18001 + local_port: 8001 + ssh_user: tegwick + ssh_key: ~/.ssh/id_ops + actor: agent.claude-coulombcore + health_check: + url: http://127.0.0.1:18001/sse + interval_seconds: 30 + timeout_seconds: 5 + reconnect: + max_attempts: 0 + backoff_initial: 5 + backoff_max: 60 +``` + +ops-bridge source: `~/ops-bridge` · SSH key: `~/.ssh/id_ops` + +--- + +## Bridge states + +| State | Meaning | +|-------|---------| +| `connected` | SSH process alive, health check passing | +| `degraded` | SSH process alive, health check failing (SSE endpoint streams — not always a real error) | +| `reconnecting` | SSH dropped, backoff loop active | +| `stopped` | Not started or manually stopped | + +The MCP SSE tunnel often shows `degraded` because the `/sse` health check +receives a streaming response rather than a clean 200 — the tunnel is still +functional. Confirm with `curl http://127.0.0.1:18001/sse` from the remote. + +--- + +## MCP transport modes + +The MCP server (`state-hub/mcp_server/server.py`) supports two transports +selected by environment variable: + +| Variable | Default | Effect | +|----------|---------|--------| +| `MCP_TRANSPORT` | `stdio` | stdio transport (local Claude Code) | +| `MCP_TRANSPORT=sse` | — | SSE/HTTP transport for remote clients | +| `MCP_PORT` | `8001` | Port for SSE mode | +| `API_BASE` | `http://127.0.0.1:8000` | State Hub API URL the MCP server calls | + +On a remote machine the MCP server process runs locally inside Claude Code — it +does not run on the remote host. The transport layer (SSE over the tunnel) +handles the connection. + +--- + +## Adding a new remote host + +1. Generate or reuse an SSH key pair (recommend `~/.ssh/id_ops`) +2. Add the public key to the remote host's `~/.ssh/authorized_keys` +3. Check port availability on the remote: `ssh user@host "ss -tlnp | grep 18001"` +4. Add two tunnel entries to `~/.config/bridge/tunnels.yaml` (API + MCP) +5. `bridge up ` for each +6. On the remote: `claude mcp add-json -s user state-hub '{"type":"sse","url":"http://127.0.0.1:18001/sse"}'` + +--- + +*The State Hub runs on the work laptop only. Remote machines are read-only +consumers connected via tunnel — they never own a copy of the database.* diff --git a/dashboard/src/reference.md b/dashboard/src/reference.md index 5fcc56d..ef28788 100644 --- a/dashboard/src/reference.md +++ b/dashboard/src/reference.md @@ -20,6 +20,7 @@ convention used in the Custodian State Hub. | Page | What it covers | |------|---------------| +| [Connecting to the Hub](/docs/connecting) | Local and remote connection setup, ops-bridge tunnels, MCP transports | | [Contributions](/docs/contributions) | Contribution types, lifecycle, third-party todo workflow | | [Decision Health](/docs/decisions-kpi) | KPI formula, avg resolve time, open-age colour thresholds | | [Decisions](/docs/decisions) | Decision types, statuses, escalation rules, filter bar |