Full catalog generation previously prepared each game sequentially, so a
version mismatch late in the catalog could surface only after earlier packages
had already been extracted and hashed. Validate every selected version.ini
against game.db before starting manifest preparation, while retaining the
per-package validation during preparation to detect later input changes.
Document the ordering and cover it with a regression where an earlier archive
would fail if archive processing began before a later version mismatch.
Test Plan:
- `just fmt` -- passed
- `just test` -- passed
- `just clippy` -- passed
- `git diff --cached --check` -- passed
Replace address-only trust and pushed peer state with installation identities,
SPKI-pinned QUIC, candidate-only discovery, and bounded responder-owned
protocol-8 pulls. The runtime now owns each network generation and all admitted
work through shutdown.
Add exact bundled content identities, reproducible manifest publishing,
capability-confined downloads, streaming BLAKE3 verification, quarantine and
retry, and crash-recoverable download and install transactions. Ship generated
fixture catalogs and fail closed when production manifests are absent.
The Tauri backend exposes durable sharing policy, redacted identity state, and
attempt-keyed transfer snapshots. Frontend consumption follows in the next
commit. Repository-wide test certificates and protocol-7 paths are removed.
BREAKING CHANGE: peers must use protocol 8 and exact catalog content artifacts;
protocol-7 frames and shared-certificate identities are no longer accepted.
Test Plan:
- `just test` -- passed on the completed stack (708 workspace tests)
- `just clippy` -- passed on the completed stack
- `just build` -- passed with fixture catalogs on the completed stack
- `just catalog-check-production` -- failed closed because the external
production manifest corpus is absent
- `git diff --cached --check` -- passed
Updating or removing a local game rewrites its on-disk files. Peers that
were mid-download of that game would keep streaming bytes from files that
are being deleted or replaced, handing them a corrupt or stale copy.
There was also no authoritative notion of which game version a peer
should serve or accept, so a peer could serve whatever happened to be on
disk and downloaders could aggregate files from peers running mismatched
versions.
This introduces a reader-writer coordination scheme between outbound file
transfers (readers) and local mutation operations (writers), and gates
both serving and downloading on an authoritative game catalog version.
Reader-writer coordination:
- Track active outbound transfers per game in a shared `OutboundTransfers`
map of (id, CancellationToken), threaded through `Ctx`/`PeerCtx` and
registered by a `TransferGuard` in the stream service. The guard is
registered *before* the serve-eligibility check to close a TOCTOU window
where a writer could miss an in-flight reader.
- `stream_file_bytes` now honors a cancellation token at every await point
(file read, network send, stream close) via `tokio::select!`, so a
transfer aborts promptly instead of hanging on a stalled receiver.
- `begin_operation` marks a game active first, then cancels its outbound
transfers and waits for the count to reach zero before any
Updating/RemovingDownload work touches the filesystem.
- Active games are now hidden from library snapshots entirely while an
operation is in flight, instead of freezing their last announced state,
so peers stop discovering a game that is being mutated.
Authoritative version catalog:
- Replace the `HashSet<String>` catalog with `GameCatalog`, mapping each
game id to its expected version (from the bundled game.db / ETI data).
- Serving requires the local `version.ini` to match the catalog version
(`local_download_matches_catalog`); peer selection, file aggregation,
and majority size validation all filter on the expected version
(`peers_with_expected_version`, `aggregated_game_files`, and friends).
User-visible changes:
- The GUI shows confirmation dialogs before Update and Remove, and
surfaces a sharing-status indicator on game cards and the detail modal.
- A new `OutboundTransferCountChanged` event lets the UI reflect live
outbound transfer activity.
Test Plan:
- just test
- just frontend-test
- just clippy
Game::availability used string labels that were carried through persisted
library data, protocol summaries, and the Tauri-facing game payload. That
allowed invalid states to exist and required legacy summary conversion code to
defensively map strings back into protocol availability values.
Move Availability to lanspread-db and re-export it from lanspread-proto so the
persisted Game type and wire GameSummary type share one serde enum. The JSON
spelling stays Ready, Downloading, or LocalOnly, so the serialized shape does
not change for current library indexes or peer payloads.
Add typed helpers for sentinel-derived download state. Game::set_downloaded
keeps downloaded and Ready/LocalOnly in lockstep and intentionally collapses
non-ready local state, including Downloading, back to LocalOnly. That matches
the current local-summary contract where active operations are suppressed
instead of advertised as Downloading. Game::normalized_availability keeps the
legacy Game-to-summary path from publishing an inconsistent Ready value when
downloaded is false.
Update the follow-up status note so typed availability is no longer listed as
open work.
Test Plan:
- just fmt
- just test
- just clippy
- just build
Refs: none
The follow-up review found a few stale lifecycle edges around local game
transactions. Recovery could sweep active roots, post-operation refreshes
still re-ran full startup recovery, and the UI kept inferring local-only state
from downloaded and installed flags instead of the backend availability.
This updates the peer lifecycle so startup recovery skips active operations,
install/update/uninstall refresh only the affected game after the operation
guard is dropped, and path-changing game-directory updates are rejected while
operations are active. It also removes the dead UpdateGame command, drops the
unused manifest_hash write field while preserving old JSON reads, renames the
internal install-finished event, and carries availability through the DB,
peer summaries, Tauri refreshes, and the React model.
The included follow-up documents record the review source, implementation
decisions, and the remaining FOLLOW_UP_2.md work so later commits can stay
small instead of reopening the completed plan items.
Test Plan:
- git diff --check
- just fmt
- just clippy
- just test
Follow-up-Plan: FOLLOW_UP_PLAN.md