fix(peer): serve manifests from the validated cache only

Follow-up to the scanner finding #2 fix ("rejected bulk requests can
populate the persistent manifest cache"). The previous commit reordered
admission so the compact content index and local readiness are checked
before any manifest body is loaded. That relied on ordering alone: the
loader still called `CatalogBundle::manifest`, which reads and parses
the artifact from disk on a cache miss, and nothing in the test suite
proved that a rejected request leaves the cache untouched.

Switch the outbound admission loader to `cached_manifest`, which never
performs filesystem I/O. This is safe because every game that can pass
`can_serve_game` is catalog-eligible and its manifest was primed by
`prime_library_manifests` before the library revision that advertises it
became visible (server startup in `services/server.rs`, every library
scan publication in `handlers.rs`). A cache miss therefore means a
publication-ordering bug rather than a legitimate serve, and failing the
request closed with a logged error is the right outcome.

The admission tests now assert that a request with the wrong content
identity leaves `cached_manifest("game")` erroring, for both catalog
chunks and Stream Install, and prime the manifest explicitly before the
accepted-request assertions, mirroring what the server does. The
architecture document describes the cache-only serving path.

Behaviour visible to peers is unchanged for valid requests. Rejected
requests no longer cause a disk read under the admission lock.

This layers the `cached_manifest` switch and no-load assertions from the
parallel security branch (lanspread2 commit 4e0419a) onto the identity
gate introduced in 55fa494.

Test plan:
- `cargo test -p lanspread-peer --lib`: 488 passed.
- `just test`, `just clippy`, `cargo +nightly fmt --check` at the end of
  the series.

Claude-Session: https://claude.ai/code/session_01QRkCv4a4GqkajyamxmbSuA
This commit is contained in:
ddidderr committed 2026-09-12 11:12:14 +02:00
1 parent 098b8e9161
commit 63aa4bc77c
2 files changed
+31 -3

No files matched your search

+7 -2
View File
@@ -286,8 +286,13 @@ Most scans become O(number of game dirs), with full recursion only when needed.
replace that byte authority.
- Authenticated remote-availability joins use the compact index only. Unknown or
mismatched `(game_id, content_id)` pairs are rejected without filesystem I/O
or manifest parsing; actual serving, download planning, and Stream Install
still load and validate the full body.
or manifest parsing. Outbound serving (chunk and Stream Install admission)
also gates on the compact index and in-memory local readiness first and then
reads the manifest body from the validated cache only: every publishable
game's body was primed before its library revision became visible, so an
anonymous request can neither trigger a disk read nor populate the manifest
cache. Download planning on the requesting side still loads and validates the
full body on demand.
- The local catalog artifact is converted into a `ValidatedDownloadManifest`
before any destination mutation. It contains canonical game-root-relative
paths and rejects aliases, reserved state, shape conflicts, and bounded-size