Decisions
Non-obvious, reversible-but-significant decisions are recorded as ADRs in
docs/adr/.
Add a new numbered record when you change the wire format, the fountain PRNG or degree table, the isolate/threading model, disk persistence semantics, or a language or runtime choice.
| # | Decision | Status |
|---|---|---|
| 0001 | Flutter receiver worker isolate | Accepted |
| 0002 | Fountain (LT-code) vs. sequential chunk encoding | Accepted |
| 0003 | Disk hydration design | Accepted |
| 0004 | Sender language — TypeScript → Rust | Accepted (QR-display sender only) |
| 0005 | mobile_scanner stays pinned at the vendored fork (7.2.0) | Accepted (known gap) |
| 0006 | porter join ported to Rust; nodejs/ and all JS deleted | Accepted (resolves 0004's defer) |
In brief
0001 — Worker isolate
Decoding ran on the UI isolate and froze the app during a 109 MB transfer.
A per-call compute() was rejected because the decode state must persist across
thousands of calls; one long-lived worker owns it instead.
0002 — Fountain coding
A QR slideshow has no back-channel. Sequential encoding stalls on a
consistently-missed frame; fountain removes the positional dependency at the
cost of per-symbol decode overhead. Both are implemented and sender-selectable —
the leading F token disambiguates.
0003 — Disk hydration
Chunk filenames are the ground truth for what has been received, because
debounced metadata.json writes can lag the actual files. Hydration records
what exists without reading it; an earlier eager implementation exhausted memory
on real transfers.
0004 — Rust sender
"Install Node to send a file" is friction an air-gapped tool should not impose.
Rust gives a single static binary and ratatui/crossterm for the interactive
controls. Go was considered and lost on the TUI ecosystem.
0005 — Pinned scanner fork
mobile_scanner is vendored at 7.2.0 with a local patch adding macOS
external-camera selection. Upstream is at 7.4.0; re-diffing risks the feature
the fork exists for, for mostly-irrelevant fixes. Recorded as a gap.
0006 — Porting join
Joining is the last step of an air-gapped transfer, so it runs on exactly the
class of machine that motivated ADR-0004. join is pure local file
concatenation plus a SHA-256 check — no networking, no rendering, no wire-format
surface — so porting it allowed nodejs/ to be deleted entirely.
One deliberate behavioural difference: stray files sharing a .part prefix are
skipped rather than concatenated in.