feat(peer): validate manifests before download mutation
Why: - Remote and UI-echoed file descriptions could reach transaction and storage code one entry at a time, so a hostile late path could mutate earlier files. - Per-file consensus also accepted malformed peer lists and let duplicate rows inflate a source's vote. What: - Add a complete protocol-7 manifest adapter with catalog-root confinement, portable path and alias rules, reserved-path protection, shape and size caps, symlink/reparse inspection, and zero-mutation tests. - Keep download selection in the peer core, validate every peer manifest before consensus, and pass only the validated manifest into storage/orchestration. - Canonicalize locally advertised paths, cap exact chunk receives, and preserve the local-only install fast path. - Record the chosen safety limits and follow-up ownership/catalog decisions. Test Plan: - just clippy - just test - just frontend-test - just build - just fmt (Rust/TOML/Prettier completed; rumdl reports 39 pre-existing issues) - git diff --cached --check
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# Peer authentication refactor decisions
|
||||
|
||||
This file records implementation choices that are not fully determined by
|
||||
`PEER_AUTH_PLAN.md`, especially choices where every available option has a
|
||||
meaningful downside.
|
||||
|
||||
## 2026-08-09 — Bound protocol-7 download descriptions generously
|
||||
|
||||
**TL;DR:** Until catalog manifests replace remote descriptions, accept at most
|
||||
100,000 entries, 1 TiB per ordinary file, 64 KiB for the in-memory `version.ini`
|
||||
sentinel, 255 bytes per path component, 900 bytes per relative path, and 16 TiB
|
||||
in aggregate for one game. Resolved destinations are capped at 1,000 platform
|
||||
path units.
|
||||
|
||||
The plan requires count, per-file, and aggregate limits but intentionally does
|
||||
not prescribe values. These limits are far above the expected game and CLI
|
||||
fixture sizes while bounding allocation, planning work, and arithmetic.
|
||||
|
||||
Alternatives:
|
||||
|
||||
- Lower product-sized limits would reject abusive inputs earlier, but risk
|
||||
inventing constraints that real large games or mod packs exceed.
|
||||
- Limits near `usize`/`u64` maxima avoid practical compatibility concerns, but
|
||||
do not meaningfully bound memory or work.
|
||||
- Configurable limits add settings and test combinations without a current user
|
||||
need; protocol safety limits should be consistent between installations.
|
||||
|
||||
## 2026-08-09 — Track download ownership; never infer it from unknown files
|
||||
|
||||
**TL;DR:** Persist an atomic per-game set of successfully downloaded paths under
|
||||
application state. On replacement, remove only paths in the previous owned set
|
||||
that are absent from the new authoritative manifest. If old or corrupt state has
|
||||
no trustworthy set, preserve unknown root files.
|
||||
|
||||
The existing game directory has no provenance information. Deleting every
|
||||
non-reserved path would remove stale downloads, but could also delete a user's
|
||||
own files and contradict the plan's preservation rule. The first update after
|
||||
upgrading may therefore leave legacy stale files; subsequent successful
|
||||
downloads have exact ownership and can clean safely.
|
||||
|
||||
Alternatives:
|
||||
|
||||
- Bootstrap ownership from the current directory. This is seamless, but can
|
||||
permanently misclassify user files as Lanspread-owned and later delete them.
|
||||
- Delete every non-reserved file missing from the new manifest. This is simple
|
||||
and cleans legacy state completely, but is unsafe for user files.
|
||||
- Delete only familiar archive suffixes such as `.eti`. This avoids most user
|
||||
files, but bakes package naming guesses into the security boundary and cannot
|
||||
clean general catalog payloads.
|
||||
- Never clean stale files. This is safest for user data, but fails the explicit
|
||||
successful-replacement requirement and can keep serving obsolete payload.
|
||||
|
||||
## 2026-08-09 — Generate production manifests outside the source checkout
|
||||
|
||||
**TL;DR:** Provide a small deterministic generator binary that runs next to the
|
||||
canonical production packages and writes the JSON manifest artifacts that are
|
||||
then stored and packaged with Lanspread.
|
||||
|
||||
This checkout has catalog metadata for 186 games but canonical package bytes
|
||||
only for a small CLI fixture set. Hashes cannot be derived honestly without the
|
||||
bytes. Keeping the generator here makes the format reproducible without
|
||||
requiring production game packages in Git.
|
||||
|
||||
Alternatives:
|
||||
|
||||
- Store production packages in this repository. That would make generation
|
||||
self-contained, but is impractical for size and distribution reasons.
|
||||
- Generate hashes at application runtime. This would make local peer bytes the
|
||||
authority, defeating catalog-owned verification and adding startup work.
|
||||
- Accept hashes announced by peers. This is easy to deploy, but provides no
|
||||
protection against a peer that supplies both the bytes and their claimed hash.
|
||||
Reference in New Issue
Block a user