Systems · 2026

fpx

LAN peer-to-peer file transfer in Rust with crash-resume and integrity checks

Complete · source available

Overview

  • What: two machines on the same network find each other over UDP broadcast and move large files directly over TCP, with no server, cloud or account.
  • Hardest problem: resuming a transfer after a crash without ever trusting a corrupt partial file.
  • How it is validated: 14 integration tests plus a 400 MiB transfer where the receiver is SIGKILLed mid-transfer and the resumed file matches by hash.

Written in Rust with one dependency (sha2); the discovery protocol, wire format, chunked transfer, crash resumption, flow control and integrity checks are all original.

Architecture

 sender                                         receiver
   |-- UDP broadcast: discovery ----------------->|
   |-- HELLO / OFFER ---------------------------->|
   |<-- RESUME (bytes held + SHA-256 of them) ----|
   |-- START ------------------------------------>|
   |-- CHUNK* (per-chunk digest, bounded window)->|  ACK only after chunk hits disk
   |-- DONE (whole-file SHA-256) ---------------->|
fig. 01 — fpx architecture

Highlights

  • Protocol: HELLO -> OFFER -> RESUME (bytes held + SHA-256 of exactly those bytes) -> START -> CHUNK* with per-chunk digests and a bounded unacked window -> DONE (whole-file SHA-256); ACKs are sent only after a chunk reaches disk.
  • Crash-resume proven with a 400 MiB file: receiver SIGKILLed mid-transfer, restarted, and the send resumed at byte 148,897,792 (35% of the file not re-sent) with matching source and received hashes.
  • Backpressure holds under a slow disk: peak 3 in-flight against a window of 4, bounded memory of 1,024 KiB.
  • Throughput 153.2 MiB/s with 1 MiB chunks on loopback, 77% of a raw-TCP baseline on a single vCPU; the gap is SHA-256 work, analysed in docs/DESIGN.md.
  • 14/14 integration tests: corrupt partial files force restart not corruption, bad-hash chunks rejected, filename traversal rejected, oversized/truncated frames rejected, stale peers expire.

What I learned

Verifying the held prefix by hash on resume, rather than trusting its length, is what makes a corrupt partial file restart cleanly instead of corrupting the result. The 77% throughput figure comes with a measured explanation: the gap is SHA-256 work.

Limits

No GUI, no encryption (SHA-256 detects corruption, not an attacker), single files only, IPv4 broadcast only.