The Termopus Relay is a Cloudflare Workers application that forwards encrypted messages between the phone and bridge. It operates on a strict zero-access model — all messages are AES-256-GCM encrypted blobs that the relay cannot decrypt. It simply routes bytes between endpoints.
Cloudflare Edge (200+ cities)
┌──────────────────────────────────────────────────────┐
│ │
│ Phone ──WebSocket──► SessionRelay ──WebSocket──► Bridge
│ (Durable Object) │
│ │
│ • Cannot decrypt messages │
│ • Forwards opaque blobs only │
│ • Session state lives at the edge │
│ │
└──────────────────────────────────────────────────────┘
Each pairing session gets its own Durable Object instance, providing:
- Up to 3 phones connected simultaneously per session
- LRU eviction when limit is reached — oldest phone disconnected gracefully
- Each phone identified by device ID from mTLS certificate
When Claude asks for permission and multiple phones are connected:
- First response (Allow/Deny) wins and is forwarded to bridge
- Duplicate responses tracked by
requestIdand silently dropped - Prevents conflicting approvals from different devices
- FCM silent push sent when Claude waits for user input
- Wakes the app even when backgrounded or phone is locked
- Notification includes session context for quick triage
| Role | Authentication |
|---|---|
| Bridge (computer) | Bearer token — first connection stores token, subsequent connections must match |
| Phone | mTLS client certificate — CF-Access-Client-Id verified against provisioned devices KV |
4-tier phased rollout controlled by SUBSCRIPTION_ENFORCEMENT env var:
| Mode | Behavior |
|---|---|
off |
No enforcement (development) |
log |
Log expired subscriptions, allow through |
new_only |
Block new connections with expired subs, allow existing |
on |
Full enforcement — valid subscription required |
WebSocket wss://relay.termopus.com/{sessionId}?role=computer
WebSocket wss://relay.termopus.com/{sessionId}?role=phone
That's it. Two WebSocket endpoints. Everything else is handled by the Durable Object.
| Component | Service |
|---|---|
| Runtime | Cloudflare Workers |
| State | Durable Objects (per-session) |
| Storage | Cloudflare KV (tokens, devices, subscriptions) |
| Push | Firebase Cloud Messaging (FCM) |
| Auth | Cloudflare Access (mTLS) |
Each device has an active_sessions:{deviceId} key in KV with 24-hour TTL, tracking how many active bridge connections exist. This enforces the MAX_BRIDGES limit per subscription tier.
