Never report success for work that did not happen.
That sentence is the rule PIGEON is built around, and most of the product is the cost of keeping it. PIGEON moves files across air gaps, constrained links and legacy networks — then re-reads what actually landed at the destination and hashes it there before it calls the job done.
- ✓survey/2026-q3/tiles/level_09/0421_0118.bin9f2c…41ab
- ✓survey/2026-q3/tiles/level_09/0421_0119.bin07de…c3e9
- ✓survey/2026-q3/elevation/grid_north.tifb41a…0f7d
- ✓survey/2026-q3/reports/coverage.pdf5e80…9a12
- ✓survey/2026-q3/README.txtc1f3…77b0
The rule, and what it costs
Verification is easy to claim and easy to get subtly wrong. The cheap version hashes the source as it streams past and records that as the destination’s hash. It proves the read was correct. It says nothing at all about what the write did. PIGEON re-reads the bytes at the destination and hashes those, and verification is on by default in every profile that ships.
Four consequences follow, and each one is a place the cheap version quietly breaks:
- Every write and every close is checked. A close is where buffered data is finally handed to the filesystem, so a close is where a full disk surfaces.
- A file that fails verification is not checkpointed, so a resume re-copies it rather than skipping it as already done.
- The manifest is the delivery receipt, so a manifest that could not be written fails the run on its own. Files with no receipt are not a completed transfer.
- A later step succeeding is not proof an earlier one did. Archive extraction does not stop at the first failed member — it records the error and reads the next header — so “the next file’s progress callback fired” is not evidence the previous file landed intact. A destination filling up mid-entry produces exactly that sequence. Archive mode’s incremental checkpointing re-verifies size, and hash where the profile asks for it, against the source file before recording anything.
We know the difference because we got it wrong. Three failures in this product’s own history compiled, linked, passed review and were caught only by running the thing: a signing option that silently signed nothing and exited zero, because the guard the signing code sat behind was never true and unreachable code cannot report an error; a plain copy that reported success over a truncated file; and archive mode on Windows reporting completed having delivered zero files. Six more, in detail.
Every failure says what state your data is in
When a transfer fails at two in the morning, the question that matters is not what went wrong. It is whether the destination can be used, and whether re-running will help or waste an hour. PIGEON answers that explicitly rather than leaving it to be inferred: every failure carries a stable code, a category, what happened, what it means, what to do next, and one of six answers about the destination.
- Untouched
- Nothing was written. The destination is as it was.
- Partially delivered, verified
- What landed is hash-verified, and a checkpoint means re-running resumes rather than restarts.
- Partially delivered, unverified
- A partial result with no checkpoint and no per-file verification behind it. Treat as suspect.
- Discarded
- Archive mode is all or nothing. Nothing was delivered.
- Delivered
- Everything landed and verified; the failure is in a step after delivery. Re-running the transfer is not the fix and would re-copy the whole set for nothing.
- Unknown
- The engine genuinely cannot say — so it says that, rather than guessing.
There are seventy codes today, across five categories: input, environment, policy, data and defect. They are append-only — never renumbered, never reused. Inside an enclave where the tool cannot be updated, a code gets read down a phone and written on a form, so the words may change and the number must not. A test enforces uniqueness, the category ranges, and that the mapping from category to process exit code stays one-to-one.
What is actually built
The full surface, with the qualifications each part travels with, is on the product page. In outline:
Moving data
Multi-threaded local copy with per-file resume. Differential sync that decides by content hash rather than size or timestamp, with mirror-delete available and off by default. Archive-and-go for huge counts of small files over a slow link. An offline batch workflow for hand-carrying changes across an air gap.
Across
Mounted paths; SFTP, HTTPS with mutual TLS, and FTPS — each driven through the host’s own client rather than a bundled protocol stack, so your hardened configuration is the boundary. TFTP exists for equipment that speaks nothing else: compiled out by default, and refused at runtime even in a build that has it unless a separately signed authorisation is present.
Making a batch small
File-level differencing against a recorded far-end state; block-level deltas against a basis the far end is known to hold; and content-addressed chunking, so five machine images sharing one base layer carry that layer once — across files and across runs, not just within one file’s own history.
One-way
For a far end with no return channel at all: complete signed snapshots every time, sequence-chained so a replayed or rolled-back delivery is refused, with anything the snapshot does not list set aside in dated quarantine rather than deleted. Optional binding to the identity of the removable volume the batch was written to.
Receiving
An unattended watcher that applies each batch landing in a drop directory and sets aside what it refuses. An embedded HTTPS server with mutual TLS. The host’s own SSH server, public-key only.
Unattended
A real Windows service or systemd unit with a persistent job queue, so a transfer survives a logoff or a power loss. Priority ordering, retry with backoff, and crash recovery that never reads an interrupted job as either finished or failed.
Records
A hash-chained audit log of every run, with one chain across processes. Hybrid signatures over PIGEON’s own artifacts: a classical one and a post-quantum one, both required. An optional projection of the audit log to a SIEM that carries the chain link, so a collector can detect a gap the same way the local verifier does.
Nothing phones home
No update check, no crash reporting, no analytics. The only traffic PIGEON starts is the transfer you asked for, to the host your own profile names, and it only listens when you start one of the server modes yourself. Credentials are never written to a profile: a profile holds a host, a path, and at most the path to an identity file.
Verified against things somebody else wrote
Every protocol is checked against an implementation from outside this project, in both directions where both exist: a real OpenSSH server; real independent TFTP client and server implementations; a real FTPS server with real TLS; and PIGEON’s own HTTPS client driven against PIGEON’s own embedded server rather than either side against a stand-in — a pairing that found two real bugs immediately, both of which look correct when either side is tested alone.
Where a platform feature is the thing under test, the platform is real. NTFS alternate data streams are checked on a real NTFS volume, because creating a file with a colon in its name on an ordinary Linux filesystem produces a file with a colon in its name and a test built on it would pass for entirely the wrong reason. Same-drive detection is checked between two filesystems that genuinely are different. The suite builds and runs on real Windows hardware, not in emulation.
Several Windows-only features carried an explicit “compiles, not proven” status in our own engineering log, in writing, for as long as that was true — and were marked proven only when a real Windows machine settled them. In one case the compatibility layer did not merely fail to prove a feature; it reported the opposite of the truth.
Some of it is what the tool will not do
Move mode and mirror-delete cannot be combined on one profile, and there is no override flag — run that combination twice and the second run deletes the only remaining copy of your data, deterministically, not as an edge case. Mirror-delete only ever removes a path this profile recorded delivering, so a file something else put at the destination is left alone with a notice. On the receive side of a one-way link, where nobody can be asked whether an unrecognised file was meant to go, it is moved into dated quarantine instead of removed. A local state file that will not parse fails closed, because the benign reading is always the one that reopens the hole the record was keeping shut.
If you are evaluating this
The most useful thing you can send is a specific question about a specific environment: what the data is, what it has to cross, and what runs at the far end. We would rather answer that than send a brochure — including when the answer is that PIGEON is not the right fit.