feat(peer): prototype streamed installs
Add a streamed-install prototype that can receive archive-derived install bytes straight into local/ without first storing the peer-owned root archive payload. This is intended for low-disk clients that want to install a game but opt out of becoming a downloadable peer source for that game. The protocol gains a current-version-only StreamInstall request and framed StreamInstallFrame responses. The peer core owns the generic transport, transaction, path validation, size checks, CRC32 verification, and lifecycle state. The archive-specific work is hidden behind StreamInstallProvider so the prototype can use unrar while the final implementation can swap in a better provider without rewriting the peer command path. The receiver writes into .local.installing and only promotes to local/ after the full stream verifies. It deliberately does not write the root version.ini or archive files, so the settled local state is installed=true, downloaded=false, and availability=LocalOnly. That preserves the existing rule that local/ is not served to peers and makes streamed receivers non-sources by construction. The CLI is the only caller for now. It exposes stream-install and provides the prototype unrar implementation with unrar lt for entry metadata and unrar p for file bytes. This is simple and good enough to prove non-solid archive streaming, but it is not the production provider shape for solid archives because per-file unrar p would repeatedly decompress prefixes. The Tauri app explicitly passes stream_install_provider: None, so the GUI behavior stays unchanged until a real product path is designed. Document the production-readiness work in NEXT_STEPS.md. The main follow-up is to make the provider abstraction final-ish and replace the per-file CLI unrar provider with a one-pass archive provider, then wire a deliberate GUI low-disk mode, retry semantics, and broader failure scenarios. Test Plan: - just fmt - RUSTC_WRAPPER= CARGO_BUILD_RUSTC_WRAPPER= just test - python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py \ S39 S40 --build-image - RUSTC_WRAPPER= CARGO_BUILD_RUSTC_WRAPPER= just clippy - git diff --check - git diff --cached --check Follow-up: NEXT_STEPS.md
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
# 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. **Move from CLI-only to real app integration**
|
||||
|
||||
Add a GUI command/control path for “stream install / low disk mode”,
|
||||
probably behind an explicit option. The Tauri crate currently opts out with
|
||||
`stream_install_provider: None`, so the GUI cannot use it yet.
|
||||
|
||||
2. **Replace per-file `unrar p` with a final archive provider**
|
||||
|
||||
The prototype provider is intentionally simple: `unrar lt`, then `unrar p`
|
||||
per file. Good for non-solid archives, bad for solid archives. Final shape
|
||||
should be a one-pass provider with real entry boundaries, likely via libunrar
|
||||
or a purpose-built wrapper.
|
||||
|
||||
3. **Handle solid archives deliberately**
|
||||
|
||||
Add archive inspection that decides:
|
||||
|
||||
- non-solid: per-file streaming is fine
|
||||
- solid: one sequential archive pass only
|
||||
|
||||
This is the big architectural fork we discussed, and the prototype’s
|
||||
provider is the thing to swap.
|
||||
|
||||
4. **Decide the integrity model**
|
||||
|
||||
Current prototype verifies streamed bytes against RAR CRC32 from the
|
||||
sender’s archive headers. That catches corruption and provider bugs. It does
|
||||
not protect against a malicious peer lying. If you care about that, the next
|
||||
step is catalog-side trusted hashes for archive or extracted files.
|
||||
|
||||
5. **Upgrade retry/resume semantics**
|
||||
|
||||
Right now, failed stream means failed operation and rollback. Next useful
|
||||
step:
|
||||
|
||||
- retry whole stream from another trusted peer
|
||||
- later, maybe keep completed files and restart only the interrupted file
|
||||
- avoid byte-offset resume until there’s 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.
|
||||
Reference in New Issue
Block a user