# zit 0.14.0
zit 0.14.0 lets a repository's owner erase a version of a file on every
peer that syncs, and makes `annotate -n` and the history of one file
cheaper on a forge.
This release is from trunk. Stores, caches and the daemon protocol version
are unchanged from 0.13.0; nothing is rebuilt on upgrade.
## Signed erasures
Until now `zit shun --force` erased a version on one store only; other
devices, the server and old clones still held the bytes and could sync
them back. An erasure the owner signs now reaches every peer that syncs,
directly or through a server, and every peer refuses the bytes afterwards.
- **Where it lives.** An erasure is a row in Fossil's existing shun list
(`/shun`), with Fossil's payload byte for byte. Its comment carries an
Ed25519 signature over the project code, the hash and the time, a
not-before time and a kind (erase or cancel).
- **Who can erase.** One owner key per repository. `zit shun key generate`
makes it and pins the public half; `--print` or `-F json` prints the pair
without writing a key file, for a key kept in a platform keychain and
passed per call in `ZIT_ERASURE_KEY`. A clone pins the key of the store
it clones from; a pull, sync or `config pull` never changes it, and
`zit shun key pin` replaces it by hand. A peer applies only rows that
verify against its pinned key, so a lost device cannot erase history.
- **Erasing.** `zit shun --force --sign HASH...` signs one or more hashes
in one step. An erasure applies 24 hours after it is signed, or at once
with `--now`; `zit shun cancel HASH` withdraws it inside the 24 hours.
`zit shun --list` (and `-F json`) shows pending and applied erasures.
- **Carriage.** Every `pull` and `sync` asks for the shun list, records the
rows that verify, and removes the erased bytes at the end of the session.
A push sends the signed rows the server does not already have. `zit
serve` accepts a verified row from a user with Write access, refuses
unsigned rows while it has a key pinned, and removes the bytes between
connections; the server must have the owner's key pinned.
- **After it applies.** `cat`, `diff` and `annotate` of an erased version
exit 8 saying it "was erased by the repository owner", and `log -F json`
reports `"content": "erased"`. The bytes also leave `purge`'s graveyard,
and `purge undo` will not restore them. An applied erasure stays applied
if the pinned key later changes. A running `zit daemon` notices an
erasure reaching its time without any write.
- **`sync` and `pull -F json`** report `"erasures_deferred": N`, the rows
this session carried that are not yet fully applied, because another
process held the store. The exit status does not change, and the next
session applies them.
- **Stock Fossil.** `fossil clone` from a zit server removes erased bytes,
and `fossil configuration pull shun` makes Fossil refuse them. Stock
Fossil applies a pending erasure at once and keeps it after a cancel.
Pushing to a stock Fossil server needs Admin; without it zit says so and
finishes the sync without the rows. A Fossil client cloning a zit server
that has a key pinned prints "Skipping non-exportable setting:
erasure-key", which is harmless.
The sync protocol draft describes the row format and the rules in a new
section 9.3.1.
## annotate -n
`annotate -n N` now answers from the blame cache when that file's cache
holds an answer from an earlier unlimited `annotate`, so a file annotated
once in full costs about the same at any limit. On a 32,500-line file,
`annotate -n 50` went from 0.68 s to 0.09 s. The answer is the same as
walking the versions, byte for byte. The cache cannot stand in for the walk
where a version inside the limit has its primary parent outside it and a
merge parent inside it; that walk runs as before. A limited `annotate` does
not fill the cache on a long history.
## log for a path
`zit log PATH` walks the ancestry with bit sets instead of hash tables. A
200-row page of a 11,000-version history costs about 0.09 s instead of
0.19 s.
## Daemon
A stopping `zit daemon` lets go of its store's lock as soon as it stops
listening, before it writes its caches. A new daemon whose socket nothing
answers on waits up to 2 seconds for the lock, so a restart right after a
stop is no longer refused. A daemon that is serving is still refused at
once.
## Compatibility
Stores, check-ins, the sync protocol and the daemon protocol version are
unchanged, and no cache changes format.
A signed erasure row reads, to an older zit, as an ordinary shun with an
unusual reason: it refuses the bytes at once, with no delay and no cancel.
Until every peer runs 0.14.0, give `--now` or expect older peers to apply an
erasure early.
## Qualification
The release manifest records native qualification against the exact tagged
source archive on macOS ARM64, Linux x86-64 and Linux ARM64, using the pinned
Zig 0.17.0 toolchain. Publication is gated on unit and end-to-end tests, Git
differentials, deterministic simulation, seeded fuzzing, and Fossil
interoperability checks on every distributed target.
For the basis of the conformance claim, see
[the 0.1.0 release notes](RELEASE-NOTES-0.1.0.md).