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.Looking for the event-loop watchdog? This page covers connection-level heartbeats that reap silent clients. To detect a wedged asyncio loop (the process is up but the loop isn’t running), see Event-Loop Watchdog.
Quick Start
1
On by default
2
Tune the cadence
3
Opt out with interval_ms=0
How It Works
The gateway emits aPING on each interval; any inbound frame (a PONG, a peer PING, or a normal message) refreshes activity. A silent peer misses beats until the reaper closes it.
A connection is reaped once
now > last_activity + interval_seconds × missed_beats_before_reap. Half-open peers with no bound session yet (a stalled pre-hello/join handshake) are evaluated against a per-connection _client_last_seen clock, so silent handshake sockets age out instead of leaking forever.
The runtime is on by default: the config’s enabled=False no longer disables it. Only interval_ms=0 yields a disabled policy, after which the loop stays inert and nothing is reaped.
Health Output
WebSocketGateway.health() surfaces the reaper’s state when the task is running:
LIVENESS_TIMEOUT close code
The reaped peer sees a WebSocket close with:- Code:
4002(application-defined, private-use 4000–4999 range) - Reason:
"liveness_timeout"(equal toGatewayCloseCode.LIVENESS_TIMEOUT.value)
2 × heartbeat_ms of silence it closes its own socket so the backoff/resume path re-establishes the connection before the server reaps it.
Configuration Options
LivenessConfig is the user-facing config; to_policy() bridges it to the pure LivenessPolicy the reaper consumes.
Full field, type, and default reference
Common Patterns
Liveness runs at the defaults; pick a cadence from the peer’s network profile, or opt out entirely withinterval_ms=0.
Out-of-box defaults
Aggressive reaping for high-turnover realtime
Fully disabled
Best Practices
Only opt out with interval_ms=0
Only opt out with interval_ms=0
enabled=False alone does not disable liveness — the runtime overrides it and synthesises an enabled policy from the config’s window. To fully turn liveness off, set interval_ms=0, which yields a disabled policy and keeps the reaper loop inert.Alert on liveness.reaped_connections
Alert on liveness.reaped_connections
health() exposes a cumulative liveness.reaped_connections counter. A sudden burst of reaps signals an upstream network problem (flaky peers, NAT timeouts, or a proxy dropping idle sockets) — watch the rate, not just the total.Honour the advertised interval on custom clients
Honour the advertised interval on custom clients
The server advertises
heartbeat_ms in hello_ok; when liveness is enabled (the default) this equals the policy’s interval_ms. The reference client already runs _heartbeat_loop: it pings every interval and force-closes the socket after 2 × heartbeat_ms of silence so backoff/resume reconnects. Custom clients should derive the same 2× silence window from the advertised value — miss it and the server closes you with 4002.Treat LIVENESS_TIMEOUT as expected
Treat LIVENESS_TIMEOUT as expected
A reaped peer sees
GatewayCloseCode.LIVENESS_TIMEOUT (value "liveness_timeout", close code 4002) in the WebSocket close reason. Match on it to reconnect and resume — don’t surface it as a fatal error to the user.Related
Doctor Plugins
Surfaces liveness state in gateway diagnostics
Reliability Preset
Related resilience knobs
Session Continuity
What survives a reap
Event-Loop Watchdog
Detect a wedged asyncio loop (not just a silent connection)

