The gateway now ships in the
praisonai-bot package. praisonai serve gateway still works exactly as documented here; for a standalone install see praisonai-bot Migration.Quick Start
1
Minimal hello
2
Opt into capabilities
3
Resume a session
hello_ok, replayed events arrive as {"type": "replay", "event": …} before normal traffic resumes.How It Works
Negotiated protocol version:min(client_max, GATEWAY_PROTOCOL_VERSION) where server version is 1 and minimum accepted client version is 1.
Legacy join → joined still works for existing clients. New clients should prefer hello.
HelloParams (client → server)
Legacy nested
protocol: {min, max} is also accepted; missing values fall back to 1.
HelloResult (server → client, hello_ok)
Example success frame:
methods: message, leave only (abort is not implemented). Base events: message, error.
HelloError (server → client, hello_error)
Wire frame also includes a legacy
next key for backward compatibility — it mirrors next_action (or falls back to next_step.value). The next_step, retry_after_seconds, and next keys are omitted entirely when no recovery hint is set — they are never null.ConnectErrorCode
The
rate_limited code is driven by an injectable RateLimitPolicyProtocol — see Gateway Rate Limit Policy for SlidingWindowRateLimitPolicy, YAML gateway.rate_limit, and custom policies.ConnectRecoveryStep
Machine-readable recovery hint. Clients branch on(code, next_step) instead of parsing message.
Capability Matrix
Events are advertised only if the client requested the matching capability.
Server-side state after handshake
Afterhello_ok is sent, the gateway records the negotiated protocol version and the client’s advertised capabilities on the session itself. Both are read-only and survive resume.
Both properties are populated on the
hello path and the legacy join path, so server code can branch on session.capabilities without checking which handshake the client used.
Client-side wiring (praisonai-bot)
The bundled praisonai-bot[gateway] client speaks the modern handshake for you — construct it with the capabilities you handle, connect(), then read the negotiated manifest off the instance.
hello first — capabilities= controls what it advertises, not whether the modern handshake is used.
Client construction
Negotiated accessors
connect() populates these from the server’s hello_ok. Against a legacy gateway they stay at permissive defaults so existing code keeps working.
supports_event(event)
Gate optional behaviour on the negotiated event set instead of try: … except: probing.
- Accepts a
str(e.g."token_stream") or anEventTypemember; the enum’s.valueis used for lookup. - Returns
Truefor legacy gateways (emptyfeatures["events"]) so code that assumed everything worked keeps working — this is the intentional permissive default. - Otherwise returns
Trueiffevent in client.features["events"].
Handshake flow — client branch on the server’s first reply
The wire is documented server-side above; this is what the client does with each reply.PayloadTooLarge
client.send(...), before the WebSocket write, when len(payload.encode("utf-8")) > client.policy["max_payload"].
- The frame never leaves the client — nothing reaches the server and no round-trip is spent.
- Size is measured in UTF-8 bytes, not
len(str), matching how the server counts. Size payloads by encoded length, not string length. - Catch it and shrink/split the payload or accept the loss — retrying the same frame fails identically.
- A
max_payloadof0is honoured as “reject everything”;None/missing means unbounded. On a legacy gateway (policy == {}) this exception is unreachable and the previous server-side reject still applies.
Policy Limits
Clients should self-configure from the
policy object in hello_ok. See Gateway Flow Control for tuning max_buffered_bytes and max_queued_frames.
Persisted Session State
Afterhello_ok, the gateway records both the negotiated protocol version and the client-advertised capabilities on the GatewaySession. These values survive disconnect and resume.
Both properties are written into
session.to_dict() and restored by from_dict(), so they survive server restarts and session persistence backends.
Server-side hooks and custom routing code can read these off any live session:
If
to_dict() / from_dict() encounters a non-list value for capabilities (e.g. from an older snapshot), it is safely restored as [] to avoid errors.Best Practices
Always advertise protocol_min and protocol_max
Always advertise protocol_min and protocol_max
Even when you only support version 1 today, send an explicit range so future servers can negotiate.
Only request capabilities you handle
Only request capabilities you handle
Requesting
streaming without handling token_stream events wastes bandwidth and confuses clients.Use session_id + since on reconnect
Use session_id + since on reconnect
Resume cleanly after disconnect and process
replay envelopes before sending new messages.Advertise only the capabilities you handle
Advertise only the capabilities you handle
Requesting
streaming you don’t consume wastes bandwidth and the server advertises token_stream back at you — you’ll get events your loop drops silently.Prefer client.supports_event(...) over try/except probing
Prefer client.supports_event(...) over try/except probing
The manifest is the source of truth once negotiated; probing races the server’s advertisement. Use
client.supports_event("token_stream") to gate optional behaviour.Catch PayloadTooLarge around client.send(...) for user-generated content
Catch PayloadTooLarge around client.send(...) for user-generated content
The local guard is the fast-fail seam; without a catch, a single oversized user message crashes the send loop.
Do not depend on client.features being populated on a legacy gateway
Do not depend on client.features being populated on a legacy gateway
Fall through to
supports_event(...)’s permissive True default so a mixed-version fleet keeps working.Branch on (code, next_step) from hello_error
Branch on (code, next_step) from hello_error
Use the structured
(code, next_step) pair instead of parsing message. The legacy next key is still emitted for older clients but new code should branch on next_step.Related
Gateway & Control Plane
Unified gateway architecture
Gateway Overview
WebSocket gateway features
Session Protocol
Session message format
Error Handling
Structured connection errors

