6.0 KiB
6.0 KiB
lanspread-peer proposed protocol and architecture
This document proposes a tighter, more fault-tolerant protocol while keeping the current idea: mDNS discovery, QUIC transport, on-demand metadata, and chunked file transfers.
Goals (unchanged)
- Local LAN discovery via mDNS.
- QUIC + JSON messages for control, raw streams for file data.
- UI drives operations through
PeerCommand, peers remain headless. - Peers can appear/disappear at any time without data loss.
Peer lifecycle and message flow
1) Startup and advertise
- Start QUIC server.
- Advertise via mDNS with TXT records:
peer_id(stable ID, not tied to IP)proto_verlibrary_rev(monotonic local library revision)- optional
hostname
2) Discovery and handshake
When a peer is discovered:
- Connect and send
Hello { peer_id, proto_ver, library_rev, library_digest, features }. - Receive
HelloAck { peer_id, proto_ver, library_rev, library_digest, features }. - If the remote
peer_idis already known but the address changed, update it. - If protocol versions are incompatible, drop the peer (and keep mDNS watching).
- If library digests match, do nothing else.
- If digests differ:
- If we have a known
library_revfor that peer, requestLibraryDelta. - Otherwise request
LibrarySnapshot.
- If we have a known
3) Steady state
- Any message updates
last_seen. - Pings run only when idle (or on a longer interval), not every 5 seconds.
- Library updates are pushed as deltas, debounced and coalesced.
4) Shutdown
- Optional
Goodbye { peer_id }lets others remove the peer quickly. - If a peer vanishes without goodbye, stale timeout + ping removal handle it.
- Goodbye is a hint, never required for correctness.
Library sync protocol
Summary and snapshot
LibrarySummary { library_rev, library_digest, game_count }LibrarySnapshot { library_rev, games: Vec<GameSummary> }
Delta updates
LibraryDelta { from_rev, to_rev, added, updated, removed }removedis a list of game IDs.- Deltas are idempotent; ignore if
to_rev<= known rev.
GameSummary (concept)
id,name,eti_version,size,downloaded,installedmanifest_hash(hash of file list + sizes)availability(e.g.,ready,downloading,local_only)
When peers broadcast their game list
- Only on changes, not on a timer.
- Use a short debounce (1-2 seconds) to coalesce bursts of filesystem events.
- Send
LibraryDeltato known peers; sendLibrarySummaryon new connections.
Local game scanning: fast and low cost
Strategy
- Maintain a persistent on-disk index (per game):
manifest_hash, total size, file list (optional), and a fingerprint (version.ini mtime, .eti mtime/size, local install dir presence).
- Use filesystem watchers to update only changed games.
- Keep a fallback periodic scan with a long interval (minutes) to recover from missed events.
Fast-path scanning
- On startup, list only top-level game directories.
- For each game, read a cheap fingerprint:
.etisize + mtimeversion.inimtime (if installed)- presence of
local/content
- If fingerprint unchanged, reuse cached size and manifest hash.
- Only run a recursive scan for new or changed games.
Result
Most scans become O(number of game dirs), with full recursion only when needed.
File manifests and downloads
- Keep
GetGame/manifest requests, but keyed bymanifest_hashso repeated calls can be skipped when unchanged. - Downloads remain chunked QUIC streams with the existing integrity checks.
Fault tolerance rules
- Every peer is keyed by
peer_id, not by IP address. library_revis monotonic and guards against out-of-order updates.- Any mismatch or missing delta falls back to
LibrarySnapshot. - Loss of goodbye is harmless; stale timeout is authoritative.
Roadmap from current design to this one
- Protocol updates in
lanspread-proto:- Define
Hello,HelloAck,LibrarySummary,LibrarySnapshot,LibraryDelta, and optionalGoodbyemessages. - Thread
peer_id,library_rev, andmanifest_hashthrough all library and manifest-bearing types. - Make
HelloAckcarry the remotelibrary_revandmanifest_hashso the client can immediately selectLibraryDeltavsLibrarySnapshot.
- Define
- Peer identity:
- Persist a stable
peer_id(UUID) in the peer config and inject it intoPeerInfoandPeerGameDBat startup. - Track
peer_id -> SocketAddrin the discovery table and update the address on any incoming handshake or mDNS refresh.
- Persist a stable
- Discovery handshake:
- Publish
peer_idandlibrary_revin mDNS TXT records to avoid immediate TCP/QUIC roundtrips when nothing changed. - Add a lightweight handshake in
run_peer_discoverythat exchangesHello/HelloAckbefore any library sync. - Keep a fallback path that uses
ListGameswhenHellois unsupported.
- Publish
- Library revisioning:
- Store a monotonic
library_revlocally and increment only after a successful index refresh completes. - Apply
LibraryDeltawhenlibrary_revmatches; reject stale or future revisions and requestLibrarySnapshotinstead. - Cache the last accepted
manifest_hashper peer to short-circuit manifest requests when unchanged.
- Store a monotonic
- Local index + scan optimizations:
- Introduce a cached index file (e.g.,
.lanspread/index.json) that stores per-root fingerprints and computed manifests. - Use filesystem watchers with a debounce window to collect changes and incrementally update the cache.
- Schedule a low-frequency full scan to reconcile missed watcher events.
- Introduce a cached index file (e.g.,
- Announce updates:
- Replace
AnnounceGameswithLibraryDeltabroadcasts keyed bylibrary_rev. - Send
LibrarySummaryon new connections to seed the delta flow.
- Replace
- File manifest caching:
- Store per-game
manifest_hashand only fetch details when changed.
- Store per-game
- Liveness:
- Reduce ping frequency; update
last_seenon any message. - Add optional
Goodbyeon shutdown paths.
- Reduce ping frequency; update
- Tests:
- Delta apply/merge, rev ordering, manifest hashing, and scan cache behavior.