Claude Relay Architecture

architecturemcprelaynetworkingClaude Code

Claude Relay — Architecture Reference

Inter-Claude knowledge relay. Multiple Claude Code sessions (or a human director + Claude worker) share context through a lightweight server. Think Google Docs but for a coding IDE.

The compute asymmetry insight: A $20 plan directs a $200 plan through the relay — force multiplier for capability asymmetry between Claude tiers.


System Overview

┌─────────────────────────────────────────────────────────┐
│ MONOREPO (Bun workspaces) │
│ │
│ packages/shared/ Zod schemas, types, constants │
│ packages/relay-server/ Hono HTTP + dashboard UI │
│ packages/mcp-server/ 6 MCP tools for Claude Code │
└─────────────────────────────────────────────────────────┘

Stack: Bun 1.3 runtime, Hono framework, Zod validation
Transport: HTTP + SSE (Server-Sent Events)
Storage: In-memory (sessions expire via TTL)
Auth: Bearer tokens per session
Deploy: Docker (oven/bun:1.3-alpine) or bare Bun

Data Flow

Claude Code (worker)


MCP Tools (stdio transport)
 │ relay_send / relay_poll / relay_status

relay-client (HTTP calls)


┌──────────────────────────────┐
│ Hono HTTP Server (:4190) │
│ ┌────────────────────────┐ │
│ │ Approval Queue │ │ ← Messages staged here first
│ │ Sensitive Scanner │ │ ← Checks for API keys, tokens
│ │ Rate Limiter (30/min) │ │
│ └────────────────────────┘ │
│ ┌────────────────────────┐ │
│ │ In-Memory Store │ │ ← Sessions, messages, cursors
│ │ TTL Sweep (60s) │ │ ← Expired sessions cleaned up
│ └────────────────────────┘ │
│ ┌────────────────────────┐ │
│ │ SSE Subscribers │ │ ← Live push to dashboards
│ └────────────────────────┘ │
└──────────────────────────────┘
 │ │
 ▼ ▼
 GET /poll GET /stream (SSE)
 │ │
 ▼ ▼
Claude Code Browser Dashboard
(another session)

Security Model

Five layers of defense:

1. Bearer Token Auth

Every session generates a unique token on creation. All relay and session endpoints require Authorization: Bearer <token>. No token = 401.

2. Approval Queue

Messages from Claude Code are staged, not sent directly. The director (human or lead Claude) reviews and approves/rejects via relay_approve. This prevents workers from sending unchecked content.

3. Sensitive Content Scanner

Before a message enters the queue, it is scanned for patterns matching:

  • API keys and tokens (AWS, GitHub, Stripe, etc.)
  • Private keys and certificates
  • Connection strings with credentials
  • File paths that might leak system info

Matches are flagged and blocked.

4. CORS Restriction

Allowed origins: localhost, configured RELAY_ORIGIN env var, and *.ngrok-free.app domains. Everything else is rejected.

5. Rate Limiting

30 requests per minute per token. Prevents abuse from runaway loops.

6. XSS Protection

Dashboard uses escapeHtml with quote escaping on all rendered content. No raw HTML injection.


Session Lifecycle

CREATE JOIN ACTIVE EXPIRE
 │ │ │ │
 ▼ ▼ ▼ ▼
POST /sessions POST /sessions/:id/join POST /relay/:id TTL sweep
 │ │ GET /relay/:id (60s interval)
 │ │ GET /relay/:id/stream │
 ▼ ▼ ▼ ▼
Returns: Requires: Messages flow. Session + all
- session_id - invite_token Approval queue messages deleted
- director_token Returns: for gated sends. from memory.
- invite_token - participant_token
 14 message types Default: 60min
 available. Max: 1440min (24h)

Limits per session:

  • 200 messages max
  • 10 participants max
  • 100KB max message size
  • 50 sessions server-wide

MCP Tools (6)

The MCP server exposes six tools via stdio transport, registered in Claude Code settings:

ToolPurposeFlow
relay_create_sessionCreate a new relay sessionReturns session_id + tokens
relay_join_sessionJoin with invite tokenReturns participant token
relay_sendStage a messageGoes to approval queue first
relay_approveApprove/reject/list pendingDirector-only operation
relay_pollFetch new messagesCursor auto-advances
relay_statusHealth + session overviewSessions, pending count
relay_share_workspaceShare file tree snapshotWorkspace context sharing

Message Types (14)

Core (6) — exposed in relay_send: architecture, api-docs, patterns, conventions, question, answer

Extended (3): context, insight, task

Workspace (5) — Phase 3 file/terminal sharing: file_tree, file_change, file_read, terminal, status_update


Dashboard Modes

Director Mode

Human types instructions, Claude worker responds. Three-panel layout:

  • File tree sidebar — expandable workspace view (from relay_share_workspace)
  • File viewer — click a file to see contents
  • Input box — type messages, see conversation thread

Peer Mode

Two Claude sessions exchange knowledge autonomously. Split-panel view with four built-in simulation demos:

  1. Security Audit — one Claude audits, another implements fixes
  2. Code Review — reviewer + author collaboration
  3. Bug Hunt — debugger + fixer pair
  4. Workspace — file tree sharing demo

Deployment

Dockerfile: Multi-stage oven/bun:1.3-alpine
docker-compose.yml: Single service, port 4190
.dockerignore: Excludes mcp-server, hooks, scripts, docs

docker compose up -d # start
docker compose down # stop
docker compose logs -f # tail

Networking

  • Server binds 0.0.0.0:4190 (LAN-accessible, not localhost-only)
  • Tailscale: accessible across machines (MacBook Air <-> Mac mini)
  • ngrok: bash scripts/setup.sh https://xxx.ngrok-free.app for remote workers
  • CORS allows ngrok domains automatically

Worker Setup

bash scripts/setup.sh # local worker (localhost:4190)
bash scripts/setup.sh https://xxx.ngrok # remote worker

This registers the MCP server in Claude Code settings and installs the poll hook.


File Structure

claude-relay/
├── packages/
│ ├── shared/
│ │ └── src/
│ │ ├── constants.ts # Ports, limits, message types, sensitive patterns
│ │ ├── schema.ts # Zod validation for all payloads
│ │ └── types.ts # TypeScript interfaces (Session, Message, etc.)
│ ├── relay-server/
│ │ ├── src/
│ │ │ ├── index.ts # Hono app, middleware, route mounting
│ │ │ ├── store/memory.ts # In-memory sessions + SSE subscriber mgmt
│ │ │ └── routes/
│ │ │ ├── relay.ts # POST send, GET poll, GET /stream SSE
│ │ │ └── sessions.ts # Create, join, info endpoints
│ │ └── public/ # Dashboard (HTML + CSS + JS)
│ └── mcp-server/
│ └── src/
│ ├── tools/ # 6 MCP tool definitions
│ └── approval/ # Approval queue + sensitive scanner
├── hooks/
│ ├── relay-poll.sh # Auto-poll on Claude Code Stop events
│ └── install.sh # Add hook to ~/.claude/settings.json
├── scripts/
│ └── setup.sh # One-command install (deps + MCP + hooks)
├── Dockerfile # Multi-stage Bun Alpine
├── docker-compose.yml # Single-service compose
└── .dockerignore

Key Design Decisions

Why in-memory, not SQLite? Phase 1-3 priority was speed of iteration. Sessions are ephemeral by nature (coding sessions last hours, not days). SQLite persistence is Phase 4 roadmap.

Why approval queue? Without it, a worker Claude could leak secrets, send hallucinated content, or spam the relay. The queue gives the director veto power over every outbound message.

Why SSE over WebSocket? SSE is simpler (one-directional push), works through more proxies/firewalls, and the dashboard only needs to receive — it sends via POST. No bidirectional channel needed.

Why MCP over direct HTTP? MCP tools appear natively in Claude Code tool palette. Claude can call relay_send as naturally as it calls Read or Bash. No wrapper scripts needed.

Why Bun? Fast startup (~50ms), built-in TypeScript, workspace support, single binary. Docker image is tiny on Alpine.


The Compute Asymmetry Pattern

┌────────────────────┐ ┌────────────────────┐
│ $20 Plan (Sonnet) │ │ $200 Plan (Opus) │
│ "Director" │────────▶│ "Worker" │
│ │ relay │ │
│ - Sets strategy │◀────────│ - Executes tasks │
│ - Reviews output │ │ - Deep analysis │
│ - Approves msgs │ │ - Code generation │
│ - Manages session │ │ - Multi-file edits │
└────────────────────┘ └────────────────────┘

The cheap plan does the thinking.
The expensive plan does the heavy lifting.
Relay is the coordination layer.

This inverts the normal pattern where you need the most expensive tier for everything. The director only needs enough capability to evaluate and steer — not to execute.


Roadmap

PhaseStatusWhat
1DoneCore relay — create, join, send, poll
2DoneDashboard UI — director + peer modes
3DoneShared workspace — file tree, file viewer, sidebar
3+DoneDocker, security hardening, ngrok remote support
4NextPersistence (SQLite), session history, multi-worker
5FutureCloud deploy, encryption, URL-based session sharing