Skip to content

Bridge protocol guide

The bridge supports one registered-server control channel and two frontend transport families. This page describes the current operator and integrator contract.

Roles and endpoints

Role Endpoint
Registered server /ws/websocket?type=server, channel server:<server_id>
Legacy frontend /ws/websocket?type=app, channel app:<client_id>
HTTP frontend /bridge/v1/*
Health monitor GET /health

Phoenix messages use topic, event, payload, and ref. Clients must join their channel with phx_join before sending bridge events. The legacy frontend transport also uses Phoenix heartbeats.

Registration and discovery

The server sends register with:

{
  "server_id": "stable-id",
  "display_name": "My Crystal Lattice",
  "computer_name": "host.local",
  "location": "Office"
}

WebSocket frontends use list_servers. HTTP frontends use:

GET /bridge/v1/servers

Both expose only public server metadata. Registration ownership, node, PID, and connection timestamps remain internal.

Pairing

WebSocket pairing uses pair_request followed by pair_submit. Success returns a fresh ephemeral session_id and signed pairing_token. resume_session verifies that token and returns another ephemeral session.

HTTP pairing uses:

POST /bridge/v1/pairings/request
POST /bridge/v1/pairings/confirm
POST /bridge/v1/pairings/resume

The request route accepts server_id and client_id and returns an opaque signed request handle. The six-digit code is delivered only to the registered server. Confirmation accepts the handle and code and returns the signed pairing token.

HTTP resume validates a pairing token from:

X-Crystal-Bridge-Token: <token>

It confirms current server availability without creating a frontend WebSocket session.

Legacy WebSocket proxy

Small UTF-8 requests use http_request; large requests use http_request_init, ordered http_request_chunk, and http_request_end. The server replies with http_response or the corresponding chunked response events.

The legacy path is text-oriented. It remains supported unchanged and is the frontend compatibility default.

HTTP proxy

An authenticated frontend sends any supported method to:

/bridge/v1/proxy/<application-path-and-query>

with X-Crystal-Bridge-Token. A caller's Authorization header is preserved for the local application.

The bridge sends http_request_v2 metadata to the registered server. When a request body is present, the server downloads it from the supplied request-body path using its direction-scoped bearer token. The server sends http_response_v2_init, then uploads raw response bytes to the supplied response-body path.

No request or response body bytes are placed in v2 WebSocket messages. JSON, text, and binary content all use the same raw byte path.

Transfer endpoints

The paths supplied in http_request_v2 have these forms:

GET /bridge/v1/transfers/<request_id>/request-body
PUT /bridge/v1/transfers/<request_id>/response-body

They authenticate with Authorization: Bearer <transfer-token>. Credentials are signed, short-lived, direction-scoped, and checked against the transfer's request and server identity. One consumer/uploader may attach in each direction.

Common HTTP behavior

  • Protocol capability: GET /bridge/v1/capabilities
  • Common response cache policy: Cache-Control: no-store
  • Error body: {"error": "<reason>"}
  • Explicit empty request body: X-Crystal-Bridge-Body-Present: 1
  • Frontend authentication is never forwarded to the local application.
  • Hop-by-hop and stale framing/encoding headers are removed.

The origin negotiates gzip for eligible viewer responses. Brotli depends on the selected deployment edge.

Compatibility and rollback

HTTP clients check protocol version 1 before using the new routes. There is no automatic HTTP-to-WebSocket fallback. A frontend rolls back by reconstructing its connection in WebSocket mode.