diff --git a/crates/lanspread-peer/ARCHITECTURE.md b/crates/lanspread-peer/ARCHITECTURE.md index a32e61a..18045dc 100644 --- a/crates/lanspread-peer/ARCHITECTURE.md +++ b/crates/lanspread-peer/ARCHITECTURE.md @@ -57,8 +57,12 @@ When a peer is discovered: 1. Parse `peer_id`, address, and `proto_ver` into a candidate `PeerEndpoint`. Discovery is not authentication and does not add the peer to `PeerGameDB` or - emit UI membership events. The mDNS ingress queue and active candidate - negotiations are each capped at 64. + emit UI membership events. Advertised addresses must be unicast with a + nonzero port; multicast, broadcast, and unspecified targets are dropped + before any handshake packet is sent. The mDNS ingress queue and active + candidate negotiations are each capped at 64, and one source IP may hold at + most 8 active-or-cooling candidates so a single host cannot fill every slot + by rotating claimed peer IDs and ports. 2. Reserve a candidate negotiation lease before queueing or awaiting work, then establish a TLS-pinned connection to that exact endpoint. Missing or non-v8 records are rejected, and neither an ephemeral QUIC source port nor a payload @@ -85,15 +89,20 @@ When a peer is discovered: and both domain revisions. A session or revision mismatch schedules a full pinned pull, so idle liveness heals dropped change hints. - Local library or Call-to-Play changes send cheap revision hints. Hints are - untrusted and lossy: unknown claims are ignored, and a known claim can only - coalesce a pull from the claimed peer's already authenticated endpoint. + untrusted and lossy: unknown claims are ignored, a hint is honoured only when + it arrives from the IP address at which the claimed peer was authenticated, + and a known claim can only coalesce a pull from the claimed peer's already + authenticated endpoint. Requesters are anonymous, so this source binding is + what stops a third host from choosing which peers this node pulls. - The state-sync scheduler caps its ingress and tracked peers at 64, coalesces work for five seconds, and runs at most eight pinned pulls and eight hint sends concurrently. Child work is drained lexically on shutdown. - The server admits at most 64 unauthenticated handshakes, 64 established connection scopes, and 32 control streams per connection. Each control stream - accepts one frame followed by request EOF and sends at most one response; the - frame cap is 8 MiB and control I/O has ten-second deadlines. + accepts one frame followed by request EOF and sends at most one response. + Inbound request frames are capped at 64 KiB (the QUIC receive window per + server stream matches it), response frames at 8 MiB, and control I/O has + ten-second deadlines. ### Call to Play replication @@ -315,6 +324,16 @@ Most scans become O(number of game dirs), with full recursion only when needed. `local/`, and install transaction metadata are preserved, so a cancelled update of an installed game settles as local-only. +### Ordinary install extraction + +- `install`/`update` extract every direct regular root `.eti` archive with the + external `unrar` unpacker into the staging directory. The archives were + BLAKE3-verified against the catalog when downloaded. +- `unrar` runs with `-ol-`, so symbolic-link entries are skipped, and the + staging tree is walked without following links before promotion. Any symlink + (or reparse point on Windows) aborts the transaction and rolls staging back, + so a link can never redirect later launch-time rewrites or uninstall. + ### Streamed install integrity - Low-disk streamed installs request archive-derived file bytes from one peer