@livedesk/rendezvous
v0.1.14
Published
Bounded UDP rendezvous and authenticated TCP fallback relay for VuvoDesk
Downloads
2,433
Maintainers
Readme
@livedesk/rendezvous
Bounded VuvoDesk connectivity service with:
- raw UDP rendezvous for
livedesk.udp.p2p.v1endpoint discovery; and - an authenticated TCP fallback relay for
livedesk.relay.v1.
npx -y @livedesk/rendezvous@latest --port 5199The default bind address is 0.0.0.0. Override it when the service must only
be reachable locally:
npx -y @livedesk/rendezvous@latest --host 127.0.0.1 --port 5199UDP and TCP listen on the same numeric port. TCP and UDP are separate
transport namespaces, so both can bind port 5199 at once. When --port 0 is
used for tests, the CLI first obtains an ephemeral UDP port and binds TCP to
that exact number.
The UDP service only exchanges public endpoints observed for a matched VuvoDesk Hub and Client room. The TCP service relays only authenticated, newline-delimited opaque envelopes. It does not start a Hub or Client and it does not decode, interpret, or persist a relay payload.
TCP relay protocol
Each TCP connection must register before sending any other line:
{"type":"relay.register","protocol":"livedesk.relay.v1","roomId":"room","role":"client","peerId":"random-peer","token":"registration-token"}
{"type":"relay.register","protocol":"livedesk.relay.v1","roomId":"room","role":"hub","peerId":"","ack":true,"token":"registration-token"}A room has one persistent Hub connection and multiple Client connections,
keyed by peerId. A replacement connection with the same role/peer receives a
deterministic handoff; the replaced socket receives a relay.close notice.
A token mismatch cannot replace an existing peer.
When a Hub requests ack:true, a successful registration receives one direct
relay.registered liveness acknowledgement. It is not forwarded and does not
replace the Hub/Client end-to-end proof.
After registration, only these envelopes are accepted:
{"type":"relay.handshake","protocol":"livedesk.relay.v1","roomId":"room","peerId":"device-a","direction":"client-to-hub","stage":"client-hello","clientPublicKey":"base64url","proof":"base64url"}
{"type":"relay.data","protocol":"livedesk.relay.v1","roomId":"room","peerId":"device-a","direction":"client-to-hub","sequence":1,"payload":"opaque string"}
{"type":"relay.close","protocol":"livedesk.relay.v1","roomId":"room","peerId":"device-a","reason":"reason-code"}For Client-originated traffic, the server ignores the supplied peerId and
uses the authenticated registration peerId. For Hub-originated traffic,
peerId selects the target Client in the same authenticated room. Only
documented fields are forwarded. Handshake stage, direction, public-key and
proof strings are validated only for bounded shape and relayed without
cryptographic interpretation. relay.data preserves direction and
sequence: direction must match the registered sender role, and each peer
direction starts at sequence 1 and advances by exactly one. Close reasons are
bounded code strings. Tokens are never forwarded or logged. Encrypted payload
content remains opaque.
Public-DMZ safety defaults
The standalone command is bounded in process memory and work accepted from the network:
| Limit | Default | Behavior at the limit | | --- | ---: | --- | | Room TTL | 60 seconds | Expired rooms are removed by a 10-second sweep. | | Rooms | 4,096 | The least-recently-used unpaired room is evicted. Active paired rooms are never capacity-evicted; a new room is rejected if all rooms are paired. | | Sources | 8,192 | The least-recently-used source without an owned room is evicted. A new source is rejected only when every tracked source owns a room. | | Rooms per source IP | 128 | Further rooms from that IP are rejected. | | Per-source rate | 20 packets/s, burst 40 | Excess datagrams are dropped without a response. | | Global rate | 1,000 packets/s, burst 2,000 | Excess datagrams are dropped before JSON parsing. | | Source TTL | 120 seconds | Idle sources without owned rooms are removed. |
TCP relay state and queues are independently bounded:
| Limit | Default | Behavior at the limit | | --- | ---: | --- | | Idle room TTL | 300 seconds | All sockets in the idle room receive a close notice and are closed. | | Registration deadline | 5 seconds | A socket that does not send a valid first line is closed. | | Rooms | 1,024 | A new room registration is rejected. | | Clients | 2,048 | A new non-replacement Client is rejected. | | Clients per room | 64 | A new non-replacement Client in that room is rejected. | | TCP connections | 3,072 | Further accepted sockets receive a capacity close notice. | | JSON line | 1 MiB | An oversized line closes only its sender. | | Opaque payload | 768 KiB | Supports a base64url-wrapped 512 KiB encrypted plaintext. | | Aggregate partial input | 32 MiB | The connection exceeding the global input budget is closed. | | Queued output | 4 MiB/socket, 64 MiB total | A slow consumer is disconnected instead of growing memory. | | Per-connection rate | 120 lines/s, burst 240 | The offending connection is closed. | | Global rate | 10,000 lines/s, burst 20,000 | The offending connection is closed before JSON parsing. |
Every state table has a hard upper bound. A token mismatch cannot replace a live room. Rejections intentionally produce no UDP response, avoiding an additional reflection path.
These limits protect the Node.js process from ordinary scanning and bounded abuse; they cannot absorb a volumetric network attack. Keep host/router or provider flood protection enabled where available, expose both UDP and TCP 5199, and run the command as an unprivileged account.
Options and environment variables
CLI options override environment variables:
| CLI option | Environment variable |
| --- | --- |
| --host | LIVEDESK_UDP_RENDEZVOUS_HOST |
| --port | LIVEDESK_UDP_RENDEZVOUS_PORT |
| --room-ttl-ms | LIVEDESK_UDP_RENDEZVOUS_ROOM_TTL_MS |
| --source-ttl-ms | LIVEDESK_UDP_RENDEZVOUS_SOURCE_TTL_MS |
| --sweep-interval-ms | LIVEDESK_UDP_RENDEZVOUS_SWEEP_INTERVAL_MS |
| --status-interval-ms | LIVEDESK_UDP_RENDEZVOUS_STATUS_INTERVAL_MS |
| --max-rooms | LIVEDESK_UDP_RENDEZVOUS_MAX_ROOMS |
| --max-sources | LIVEDESK_UDP_RENDEZVOUS_MAX_SOURCES |
| --max-rooms-per-source | LIVEDESK_UDP_RENDEZVOUS_MAX_ROOMS_PER_SOURCE |
| --per-source-rate | LIVEDESK_UDP_RENDEZVOUS_PER_SOURCE_RATE |
| --per-source-burst | LIVEDESK_UDP_RENDEZVOUS_PER_SOURCE_BURST |
| --global-rate | LIVEDESK_UDP_RENDEZVOUS_GLOBAL_RATE |
| --global-burst | LIVEDESK_UDP_RENDEZVOUS_GLOBAL_BURST |
| --relay-room-ttl-ms | LIVEDESK_RELAY_ROOM_TTL_MS |
| --relay-registration-timeout-ms | LIVEDESK_RELAY_REGISTRATION_TIMEOUT_MS |
| --relay-sweep-interval-ms | LIVEDESK_RELAY_SWEEP_INTERVAL_MS |
| --relay-max-rooms | LIVEDESK_RELAY_MAX_ROOMS |
| --relay-max-clients | LIVEDESK_RELAY_MAX_CLIENTS |
| --relay-max-clients-per-room | LIVEDESK_RELAY_MAX_CLIENTS_PER_ROOM |
| --relay-max-connections | LIVEDESK_RELAY_MAX_CONNECTIONS |
| --relay-max-line-bytes | LIVEDESK_RELAY_MAX_LINE_BYTES |
| --relay-max-payload-bytes | LIVEDESK_RELAY_MAX_PAYLOAD_BYTES |
| --relay-max-pending-bytes | LIVEDESK_RELAY_MAX_PENDING_BYTES |
| --relay-max-buffered-bytes | LIVEDESK_RELAY_MAX_BUFFERED_BYTES |
| --relay-max-total-buffered-bytes | LIVEDESK_RELAY_MAX_TOTAL_BUFFERED_BYTES |
| --relay-per-connection-rate | LIVEDESK_RELAY_PER_CONNECTION_RATE |
| --relay-per-connection-burst | LIVEDESK_RELAY_PER_CONNECTION_BURST |
| --relay-global-rate | LIVEDESK_RELAY_GLOBAL_RATE |
| --relay-global-burst | LIVEDESK_RELAY_GLOBAL_BURST |
Run npx -y @livedesk/rendezvous@latest --help for accepted ranges. A status
interval of 0 disables periodic status output.
Health and monitoring
Startup prints the package version, the shared UDP/TCP endpoint, and effective limits. Every 60 seconds the process emits separate UDP and TCP JSON status lines containing:
healthy,started,version,protocol, endpoint, and uptime;- active, paired, and unpaired room counts;
- tracked source count and effective limits;
- accepted traffic, sent peer notifications, rate/capacity/token drops, expiry/eviction, and socket/send error counters.
- relay room, Hub, Client, connection, pending-input, and queued-output totals;
- relay forwarding, invalid/oversize, authentication, capacity, rate, backpressure, replacement, expiry, and socket error counters.
On macOS and Linux, SIGUSR1 emits the same status immediately:
kill -USR1 <pid>No network health endpoint is intentionally exposed. A service manager should
verify that the process is running and inspect the startup/periodic JSON log;
the exported createUdpRendezvousServer().getStatus() and
createTcpRelayServer().getStatus() APIs provide the same snapshots to an
embedding process.
The service keeps no durable room data. Restarting it safely clears all rooms, and Hub/Client sessions may register again.
