Skip to content

feat(relay): stateless pair-mux WebSocket relay skeleton#30

Open
Codename-11 wants to merge 1 commit into
feature/v3-foundationfrom
feature/relay-server-skeleton
Open

feat(relay): stateless pair-mux WebSocket relay skeleton#30
Codename-11 wants to merge 1 commit into
feature/v3-foundationfrom
feature/relay-server-skeleton

Conversation

@Codename-11
Copy link
Copy Markdown
Owner

Summary

  • New packages/relay workspace: stateless WebSocket multiplexer that routes opaque bytes between two endpoints sharing a ?pair=<code> query param.
  • First connection registers as side A, second as B, third is rejected with 409. Binary frames forward verbatim; text frames drop; either disconnect tears the pair down; pairs with one-sided connections beyond 5 min are swept.
  • Zero crypto awareness — NaCl box wiring stays in daemon/client (Phase 10 task). The relay never inspects, decrypts, or logs payload content.

What ships

  • startRelay({ port, host, pairTtlMs?, sweepIntervalMs? }) + RelayHandle public API.
  • arc-relay start [--port 8765] [--host 0.0.0.0] Commander CLI; installed as bin for the workspace.
  • GET /health returns { ok: true, pairs: <count> } for container probes.
  • Multi-stage Dockerfile pruned to the relay workspace, exposes :8765.
  • docker-compose.yml with commented nginx + certbot and Caddy TLS-proxy options.
  • Updated README.md with protocol table, run recipes, and smoke-test snippet.
  • Root tsconfig.json paths + vitest.config.ts alias wired up for cross-workspace imports.

Test plan

  • npx tsc --noEmit passes (strict, ESM, Node16 module).
  • npx vitest run packages/relay/tests/relay.test.ts — 5/5 green:
    • binary bytes round-trip A → B and B → A
    • closing A closes B with a sensible close code
    • third connection to a full pair is rejected
    • connection without ?pair= rejected at upgrade (400)
    • pairs with different codes are independent
  • Manual smoke: started arc-relay start --port 8775, opened two ws clients, confirmed b got: hi-from-a, confirmed GET /health returns {"ok":true,"pairs":0} after both closed.

Notes

  • TLS is intentionally a compose-layer concern (terminate at nginx/Caddy/LB).
  • No user accounts, tokens, or persistence — pair codes are a rendezvous primitive, not a security one.
  • The actual NaCl box encryption (endpoints, not relay) is Unit 9's primitives + future wiring.

Part of the v3 daemon pivot — see docs/plans/arc-v3-daemon.md Phase 10.

🤖 Generated with Claude Code

Minimal self-hostable relay that routes opaque ciphertext bytes between
two endpoints sharing a `?pair=<code>` query param. First connection
becomes side A, second becomes side B, third is rejected. Binary frames
forward verbatim; text frames drop. Either disconnect tears the pair
down. Pairs with one-sided connections beyond 5 minutes are swept.

Zero crypto awareness by design — NaCl box wiring lives in the daemon
and client, never in the relay (Phase 10 of arc-v3-daemon plan).

Ships:
- `startRelay({ port, host })` API + `arc-relay start` Commander CLI
- Multi-stage Dockerfile pruned to the relay workspace (port 8765)
- docker-compose.yml with commented nginx/Caddy TLS-proxy options
- README with protocol table, run recipes, and smoke-test snippet
- 5 Vitest cases: A↔B round-trip, disconnect teardown, 3rd-conn 409,
  missing-pair 400, pair-independence

Wires the new package into root tsconfig paths + vitest alias so
cross-workspace imports resolve in TS and tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant