88bfaeb04a
NEXT_STEPS item 5 needs streamed installs to have an explicit retry policy. The handler already retries whole-stream attempts across the majority-validated peer set, so add S42 to prove that behavior with the Docker harness instead of leaving it implicit. S42 starts two catalog-version-matching `cnctw` sources. The first source sorts first in retry order but has `--unrar /missing-unrar`, so its stream attempt fails before sending chunks. The second source then completes a fresh whole-stream attempt. The scenario asserts local-only installed state, no root archive or sentinel, no `.local.installing` staging leftover, chunk events only from the good source, matching streamed byte count, and SHA-256 payload equality against the good source's `unrar p`. This pins the current policy: retry the entire stream from another validated peer, do not preserve partial files across attempts, and do not promise byte-offset resume. Test Plan: - python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py - python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S42 - git diff --check - git diff --cached --check Refs: NEXT_STEPS.md item 5
67 lines
3.0 KiB
Markdown
67 lines
3.0 KiB
Markdown
# Streamed Install Next Steps
|
||
|
||
I’d treat the prototype as proof of the hard part: “can we stream
|
||
archive-derived install bytes into `local/` without making the receiver a
|
||
source?” Yes. Next I’d harden the pieces that decide whether this is
|
||
product-ready.
|
||
|
||
1. **Done — Move from CLI-only to real app integration**
|
||
|
||
The GUI now has an explicit “Low disk install” action in the game detail
|
||
modal for remote-only games. The Tauri backend queues that path through
|
||
`stream_install_game`, injects the shared external `unrar` stream provider,
|
||
and hands fetched file details to `StreamInstallGame` instead of the normal
|
||
download command.
|
||
|
||
2. **Done — Replace per-file `unrar p` with a final archive provider**
|
||
|
||
The shared external `unrar` stream provider now runs `unrar lt` once for the
|
||
archive metadata and one sequential `unrar p` pass per archive for payload
|
||
bytes. It frames directories, file starts, file chunks, and file ends from
|
||
the technical listing, so CLI and GUI callers use one purpose-built provider
|
||
instead of a per-file extraction loop.
|
||
|
||
3. **Done — Handle solid archives deliberately**
|
||
|
||
The provider exposes the RAR `solid` flag in `ArchiveBegin` and always uses
|
||
one sequential payload pass per archive, which is the safe path for solid
|
||
archives. S41 now verifies a real solid RAR fixture through the Docker
|
||
peer-cli flow, including local-only final state, absent root archive/sentinel,
|
||
byte count, and extracted payload SHA-256 hashes.
|
||
|
||
4. **Done — Decide the integrity model**
|
||
|
||
Streamed installs intentionally verify against sender archive metadata for
|
||
now: each file must match the RAR-advertised size and CRC32. That catches
|
||
transport corruption, truncation, and provider bugs, but does not claim
|
||
malicious-peer protection. Trusted content remains a separate catalog schema
|
||
step: add catalog-owned archive or extracted-file SHA-256 hashes, then verify
|
||
those at the receiver before commit.
|
||
|
||
5. **Done — Upgrade retry/resume semantics**
|
||
|
||
Streamed install attempts now use the same majority-validated peer set as
|
||
normal downloads, and each failed attempt rolls back its staging transaction
|
||
before trying the next peer. S42 pins the policy: retry the whole stream from
|
||
another validated peer, keep no partial files across attempts, and do not add
|
||
byte-offset resume until there is a strong reason.
|
||
|
||
6. **Expand scenario coverage**
|
||
|
||
I’d add cases for:
|
||
|
||
- sender disconnect mid-stream
|
||
- receiver cancel mid-stream
|
||
- corrupted/truncated stream fails and leaves no `local/`
|
||
- already-installed game rejects streamed install
|
||
- multi-archive `.eti` roots stream in sorted order
|
||
|
||
7. **Clean product semantics**
|
||
|
||
Decide how the UI labels this state. It is installed but not downloaded, so
|
||
“Local only” is technically correct, but users may need a clear affordance
|
||
like “Installed, not shareable”.
|
||
|
||
My recommended next slice: make the provider abstraction final-ish, then
|
||
implement a real one-pass provider. Everything else builds cleanly on that.
|