Skip to main content

WebSocket API

The backend does not handle WebSocket connections directly. Real-time communication goes through the Cloudflare office-router Worker, which uses Durable Objects to maintain persistent WebSocket connections with clients.

Durable Object connection token

Status: The backend endpoint for minting the office-router JWT is implemented but currently not routed — the route registration is commented out in cmd/api/main.go. The service (internal/platform/cloudflare/auth/) and handler (GenerateDOUrl) are complete and ready to be wired.

Intended flow

Client
│ 1. POST /api/v1/cloudflare/request-access (authenticated, currently disabled)

Backend
│ 2. Mints a short-lived JWT signed with RSA-2048 private key
│ Subject: WorkOS user ID
│ Claims: userId, orgId, role
│ TTL: 60 seconds

Client
│ 3. WebSocket upgrade to office-router Worker, JWT in header

office-router Worker (Cloudflare)
│ 4. Validates JWT against backend RSA public key (CONN_JWT_PUBLIC_KEY in wrangler.jsonc)
│ 5. Opens WebSocketServer Durable Object for the org

Token details

FieldValue
AlgorithmRS256
IssuerJWT_ISSUER env var
AudienceJWT_AUDIENCE env var
SubjectWorkOS user ID
Custom claimsuserId, orgId, role
TTL60 seconds (handshake only)
Signing keyJWT_PRIVATE_KEY env var (RSA-2048 PEM, backend only)
Verification keyCONN_JWT_PUBLIC_KEY in office-router wrangler.jsonc

Supabase Realtime token

A separate active endpoint generates JWTs for Supabase Realtime subscriptions (not for the office-router). See ../../features/realtime.md.

FieldValue
EndpointPOST /api/v1/realtime/token
AlgorithmHS256
Signing keySUPABASE_JWT_SECRET env var
Issuerqubital-backend (hardcoded)
Audienceauthenticated (Supabase RLS requirement)
TTL3600 seconds
  • ../../features/realtime.md — Supabase Realtime token feature
  • ../../platform/services/cloudflare/ — office-router Worker and Durable Objects architecture
  • ../../configuration.mdJWT_ISSUER, JWT_AUDIENCE, JWT_PRIVATE_KEY, WEBSOCKET_BASE_URL, SUPABASE_JWT_SECRET