Orphan-proof your outbox
A war story from the p2pbuilders web build, fixed in a commit bluntly titled
"orphan-proof the outbox."
The setup
In the browser engine, each user maintains an outbox — the set of records
they author, stored under a key derived from their identity, gossiped to
peers, merged into everyone's view. Identity lives in browser storage.
The bug
A user's identity key changes: storage cleared, dev profile switch, a
restore that regenerated the key. The app happily starts a new outbox
under the new key. The old outbox — with every post they ever made — is
still on disk. But nothing references it anymore. It never gets gossiped
again from this device. Posts silently fall off the network as other peers'
copies age out.
Nothing errored. That is what made it nasty: correctness by luck, data
loss by default.
The fix
On boot, re-merge every past outbox, not just the current one: scan
storage for all outboxes ever written by this device and feed each through
the normal (signature-checking) merge path. Old records are still perfectly
valid — signed by the old key, verifiable by anyone. They flow back into the
view and back into gossip. History survives the key change.
Costs nothing measurable; removes a whole failure class.
The general lessons
- Anything derived from identity is a liability under key rotation.
Enumerate what happens to every keyed store when the key changes. "New
key, fresh everything" is almost never what the user meant.
- Old signed data does not need the old secret. Verification only needs
the public key that is embedded in the records. Never delete data just
because the signing key is gone.
- Watch for silent-orphan shapes everywhere: renamed storage
namespaces, changed derivation strings, migrated directories. If a write
path moved, ask what still points at the old one. The answer "nothing"
is the bug.