Handshake
Every WebSocket connection starts with a handshake that identifies the peer and establishes what it is allowed to do.
Flow
Connect payload
connect.init.payload includes:
protocol_rev: numberrole: "client" | "node"device: { device_id, pubkey, label?, platform?, version?, mode? }capabilities: CapabilityDescriptor[]
device_id is derived from device.pubkey and is validated by the gateway. The derivation is:
device_id = "dev_" + base32_lower_nopad(sha256(pubkey_der_bytes))
Where base32_lower_nopad uses the RFC 4648 alphabet (a-z2-7), rendered lowercase, with no padding, and pubkey_der_bytes is device.pubkey decoded from base64url (DER SPKI).
connect.init returns:
connection_id: string(ephemeral, per WebSocket connection)challenge: string(a fresh nonce)
connect.proof.payload.proof is an Ed25519 signature (base64url) that proves possession of the device private key. The signature is over a stable transcript that binds the connection challenge and identifiers so it cannot be replayed across connections:
tyrum-connect-proof
protocol_rev=<number>
role=<client|node>
device_id=<dev_...>
connection_id=<uuid>
challenge=<base64url>
Auth
The gateway validates the gateway access token during the WS upgrade.
Preferred transports (in order)
Authorization: Bearer <token>header when the client can set headers on the WebSocket upgrade request.- Secure cookie for browser-based clients where cookie auth is appropriate for the deployment.
- WebSocket subprotocol fallback for constrained clients that cannot set headers.
Tokens MUST NOT be placed in URLs.
Subprotocol fallback
When using the fallback, the token is conveyed in the Sec-WebSocket-Protocol header. Clients should offer both:
tyrum-v1tyrum-auth.<base64url(token)>
The gateway selects tyrum-v1 as the negotiated subprotocol and reads the token from the tyrum-auth.* entry.
The access token should be short-lived, revocable, and scoped to the peer role (client vs node) and least-privilege permissions.
Operational hygiene (TLS + redaction)
- Always use TLS (
wss://) in any deployment where tokens transit a network. - Treat WebSocket upgrade headers as secrets: ensure infra and application logs do not record
AuthorizationorSec-WebSocket-Protocolvalues. - Ensure any telemetry/trace exporters redact these headers before egress.
- When using cookie auth, validate
Originfor the WebSocket upgrade so cookies cannot be replayed cross-site. - The gateway MUST NOT echo secret-bearing subprotocol entries (it should negotiate
tyrum-v1, nottyrum-auth.*).
Operational note: some intermediaries log Sec-WebSocket-Protocol. Treat it as sensitive (redact in gateway/proxy logs). For peers that can set headers (non-browser clients/nodes), deployments may prefer an Authorization: Bearer … header rather than embedding the token in subprotocol metadata.
Authorization notes
After a connection is authenticated, the gateway authorizes what the peer can do based on its role and (for operator clients) its granted scopes. The gateway can also issue device-bound tokens to reduce bootstrap-token usage during normal operation.
Details: Gateway authN/authZ.
Pairing hook (nodes)
Nodes require pairing approval before they can execute capabilities. Pairing binds a node device identity to a trust level and a scoped capability allowlist, and it can be revoked at any time.