lilydocs
HTTP APIWebSocketConsumer
Reference7 min read

Limits and common mistakes

Compare the source revision, defaults, support boundaries, and differences between existing documents and source code.

This guide uses the websocket feature of the lilyrs 0.1.0 facade published on crates.io and the lily_websocket 0.1.0 component. Its source baseline is commit 78ca08b822aa192623c7bcffd90edaf06e4f8e55. The Rust minimum and validated toolchain are 1.96.1, with edition 2024. Canonical installation uses the registry; a framework checkout is not required. Where documentation and implementation differ, this guide follows the implementation.

Defaults and fixed limits#

The following values are not capacity recommendations. Count, byte, and time limits manage different resources; configure them for your message sizes and concurrency.

Setting / rule Default / limit
Central websocket enabled / address false / 127.0.0.1:8081
Endpoint /ws, one exact canonical path; at most 256 bytes
Connection capacity 1000
Inbound reassembled message 1 MiB
Single inbound frame 256 KiB
Single outbound encoded application message 1 MiB; excluding WebSocket/backplane overhead
Inbound waiting queue 16 messages, 1 MiB total
Outbound queue 256 messages, 1 MiB total admission bytes
Outbound admission / write timeout 5000 ms each; accepted range 100..=300000 ms
Write-buffer threshold / ceiling 128 KiB / 2 MiB
Buffer validation Ceiling exceeds threshold and fits outbound message + server-frame header
Message / cleanup / connection middleware / lifecycle timeout 30 / 10 / 10 / 30 s; each 1..=300 s
Handshake timeout 10 s, positive
Ping / Pong / application idle 30 / 10 / 60 s, positive
Supported subprotocol / require ["lily.v2"] / false
Origin allow-list / any / missing Empty / false / false
Trusted proxy list / XFF hop cap Empty / 16; hop range 1..=64
Rooms per connection / name 128 / at most 128 ASCII route-token bytes
Explicit broadcast IDs / exclusions / room list At most 256 entries per list
Namespace / combined event route 128 / 256 ASCII bytes
message_id, ack_id Bounded identifiers of at most 128 bytes
Envelope metadata At most 32 fields; keys 128 bytes, values 1024 bytes
Handshake / connection / message local types At most 32 distinct types per map
Raw handshake header count / name 64 / 128 bytes
Raw header value / merged repeatable value 8 KiB / 16 KiB
Typed action arguments / payload consumers 16 / at most 1
Effective middleware plan 64 entries; duplicate concrete TypeId prohibited
Effective guard plan Duplicate TypeId prohibited; no separate 64-guard limit in this version
Principal ID 256 UTF-8 bytes; nonempty, no surrounding whitespace/control characters
Application error code / public message / Close reason 64 / 1024 / 123 bytes

Byte limits do not guarantee total process RSS bounds. TLS buffers, user callback memory, and retained objects consume additional resources. See configuration, backpressure, and transport for setup.

Common mistakes#

Symptom / assumption What to check
Default builder fails to build Central websocket enabled = true
Browser cannot connect Exact Origin, /ws, namespace, subprotocol
CLI cannot connect Explicit permission for missing Origin and an offer of lily.v2
Error instead of ACK Does the client event contain ack_id?
Payload does not parse Canonical envelope and correct content kind instead of a raw frame
Guard registered twice Duplicate type in the global/controller/action effective plan
Disconnected does not run Was connected actually called and did it return Ok(())?
Scoped service retained for the connection Do not carry Arc outside its scope; use owned locals or the correct lifetime
Pg cancellation signature mismatch WS and shared Pg cancellation are different types
Broadcast success mistaken for delivery The result is queue/publish acceptance
AsyncAPI endpoint cannot be found Current support is limited to metadata registration
Adding a direct client makes build require configuration websocket-client enables single registration; select websocket-client-di for the manual profile
Factory compilation reports a feature conflict Select *-factory instead of the base feature; avoid single/factory unification in the dependency graph
Worker cancellation type mismatch Use BackgroundCancellation; message cancellation is a different type

Documentation–source differences#

C01 — Stage-based timeout description#

The crate controller guide describes separate normal durations for forward middleware, guards, actions, and reverse exits. Current source runs the entire normal pipeline under one absolute message deadline. This site uses action → controller → app precedence and one budget; handshake/cleanup caps remain separate.

C02 — Cooperative ownership is implemented#

Contrary to the future-phase description in the crate rustdoc introduction, the cooperative window and retained execution owner are implemented. Scope/task ownership of interrupted normal work is preserved. The guide describes this as an existing capability; blocking-poll and budget limitations still apply.

C03 — Cancellation and send cutoff#

The shutdown migration text suggests execution cancellation directly removes send authority. The current outbound path waits for the cooperative execution cutoff. Shutdown explains the signal, bounded continuation, and end of authority separately.

C04 — Guard callback parameter#

Resolved. The cancellation parameter missing in the 14 September review is now present in the published release's README guard summary. The valid signature is can_activate(&self, &mut WsMessageExchange, ExecutionCancellation). The guard example uses the same signature; this record is no longer an open discrepancy.

C05 — BaseGateway facade exception#

The derive crate's rustdoc wording speaks as though no umbrella facade exists yet. lilyrs is now published; however, BaseGateway is still not re-exported by the facade and its macro implementation emits direct crate paths. The explicit dependency exception in the gateway guide is therefore retained. General facade-discovery guidance does not apply to this derive.

Separate scope boundaries#

The Pg cancellation type distinction and absence of an AsyncAPI generator are integration boundaries, not documentation discrepancies. The name is_fully_delivered() does not create a peer-delivery guarantee. Disconnected requiring a successful connected is also current behavior.

Do not assume Socket.IO/SignalR wire compatibility, automatic CRUD/join server actions, switching namespaces within a connection, persistent offline inbox/replay, or exactly-once delivery support. HTTP file uploads/SSE and a built-in JWT/OIDC provider are not built-in APIs of this server. add_background_service is available for application-lifetime work; the worker guide explains its separate cancellation and scope contract.

Wire qualification boundary#

Source tests cover frame/message limits, fragmentation, and control frames. However, Tungstenite may not complete UTF-8 validation until a Text frame's declared payload is complete. Invalid text is not dispatched to an action, and 1007 is selected in the appropriate terminal state; there is no guarantee of fail-fast rejection at an earlier TCP read.

This is tracked as LWS-TD-001 in TECHNICAL_DEBT.md. This guide does not claim that every Autobahn case passed during its preparation. Testing lists compilation, socket, and environment-dependent validation separately.

Sources for this page v0.1.0

This guide is based on the reviewed framework commit.

QUICK START
Documentation
Source code