Actor transport protocol
wabe-disk/actor implements this protocol on one broker-to-Worker WebSocket per generation. Browser clients exchange ordinary text/binary application messages with wabe Cloud; they do not send these envelopes. The endpoint runs on the developer’s host. Register its public HTTPS URL through the Actor API. The endpoint accepts the broker’s WebSocket upgrade through Actor.upgradeEndpoint(request, endpointSecret) or an implementation of this contract. On each activation the broker connects directly to a validated public address and verifies TLS for the registered hostname.
The direct upgrade includes X-WabeCloud-Endpoint-Secret and X-WabeCloud-Activation. Compare the secret against the handler’s configured value before decoding or trusting the context. The context is unpadded base64url of UTF-8 JSON with wabecloud and wabedisk properties. wabecloud has version:1, actorId, invocationId, instanceName and actor:{instanceId,generation,protocol,clientFrameBytes}. wabedisk carries the SDK’s existing version 1 disk context and rotating session credentials. Treat both upgrade headers as sensitive.
All envelopes are JSON text frames with type and integer generation. Generation must equal the current trusted actor context. Reserved routing/context data comes from the broker, not from client payloads. Text application payloads are strings with binary:false; binary application payloads use standard base64 with binary:true. The application-frame ceiling is clientFrameBytes, initially 65,536 decoded bytes. HTTP chunks remain at most 65,536 bytes; the internal envelope ceiling is 163,840 UTF-8 bytes including encoding overhead.
Startup
Section titled “Startup”After receiving 101, the broker sends:
{"type":"init","generation":1,"protocol":"wabecloud.actor.v1"}The SDK constructs one handler, completes onStart and sends {"type":"ready","generation":1}. The broker validates current ownership/generation, marks it running and sends ready-ack. Application events wait for that barrier. Heartbeats and transport control can run while initialization awaits disk I/O.
Connections and messages
Section titled “Connections and messages”| Direction/type | Additional fields and effect |
|---|---|
Broker connect |
connectionId, request:{url,method,headers}; headers are string pairs from the sanitized bodyless client handshake |
Worker accept |
connectionId; permits completion of that pending client’s 101 |
Worker reject |
connectionId, HTTP status; rejects the pending admission without exposing application error details |
Broker message |
connectionId, payload, binary; identifies the actual sender independently of payload content |
Broker disconnect |
connectionId, observed code, reason; removes only that physical connection |
Worker send |
op, connectionId, payload, binary; targets that connection, including a bounded welcome queue during pending admission |
Worker broadcast |
op, payload, binary, optional except ID array; targets the currently accepted connection set |
Worker close |
op, connectionId, code, reason; closes one connection |
Broker ack |
matching op, Boolean ok; confirms operation/queue acceptance, not delivery |
Operation IDs are strings up to 128 bytes. The SDK generates UUIDs and bounds pending acknowledgements. A welcome send must be acknowledged before 101 without waiting for the client to receive it, otherwise an awaited send inside onConnect would deadlock admission. Rejection discards that pending connection’s queue.
Broadcast is not atomic. Some recipients may accept a frame while another exceeds its queue budget; ok:false does not undo accepted frames. No broadcast history is retained for disconnected/future clients. Unknown targeted send returns a failed acknowledgement. Client close is idempotent at the wire level; the SDK rejects use of stale/closed JS handles.
HTTP multiplexing
Section titled “HTTP multiplexing”The broker starts an instance HTTP call with requestId and request:{method,url,headers} in a request envelope. It enters the same JS object’s fetch callback as connected clients. A Worker returns a response envelope with requestId, integer status and header string pairs, followed by its body stream. Headers and application data remain subject to bounds and HTTP validation; hop-by-hop headers do not reach the external response.
Each request has independent body flow control in both directions:
- Worker
creditwithrequestIdpermits one more request-body chunk from the broker. - Broker
creditwithrequestIdpermits one more response-body chunk from the Worker. bodycarriesrequestIdand base64payloadof at most 65,536 bytes.endcloses that direction’s body. A request-body end does not finish the response; a response-body end completes the logical HTTP call.cancelaborts the logical call, cancels its streams and releases pending state. Cancellation of queued HTTP work prevents it from entering the application callback later.- Worker
stream-erroraborts an already started response without failing unrelated clients or falsely reporting a clean EOF.
Each credit grants one chunk; implementations must not create an unbounded credit balance or collect entire bodies. Body chunks, cancellation, membership removal, operation acknowledgements and heartbeat run outside the application callback mailbox. This permits a fetch callback to await its request body or a broker acknowledgement while other transport control continues. Application callbacks themselves are awaited in broker order; response-body streaming continues independently after the callback returns a Response.
Heartbeat and failure
Section titled “Heartbeat and failure”heartbeat receives heartbeat-ack in either direction and is never an application message. Broker heartbeat is initially ten seconds with a 30-second observed upstream-silence deadline. Lease renewal is separate and uses PostgreSQL time. Application events are checked against current authority before forwarding; new actor-bound disk operations also require a current generation/lease.
An upstream close, timeout, malformed protocol or lost authority ends that generation. wabe Cloud closes its downstream client WebSockets with 1012 where possible, fails dispatched HTTP calls with 502 or stream abort, and rejects pending undispatched calls/upgrades with 503 and Retry-After. It releases generation queues and does not automatically replay them. A later client reconnect receives a new ID and runs admission; new incoming use may activate a fresh handler and credential family on the same disk.
There are no retained-client snapshots, recovery buffer, onResume, durable mailbox or persistent transport acknowledgements. Broker death may prevent delivery of a close frame and loses in-memory state. Applications own reconnect/backoff, persistence, request acknowledgements and handling of unknown mutation outcomes. See credential lifetimes and limits.