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.
- framework/lily_websocket/src/lib.rs
- framework/lily_websocket/src/app/mod.rs
- framework/lily_websocket/src/server.rs
- framework/lily_websocket/src/connection.rs
- framework/lily_websocket/src/connection/broadcast.rs
- framework/lily_websocket/src/request/body.rs
- framework/lily_websocket/src/extractor/message_cancellation.rs
- lilyrs/Cargo.toml
- foundation/lily_websocket_derive/src/base_gateway.rs