Fossil's formats and sync protocol, reimplemented in Zig from their specifications.
zit is a library first — a Zig analog of libfossil: the delta encoding, the
artifact formats, derived repository state, and the HTTP sync protocol as a
clean, embeddable API. The zit binary is a thin CLI over that library.
The implementation is a direct port of five companion Internet-Drafts (frozen
reference copies in docs/rfcs/; errata the code implements
ahead of the published text sit beside them in
docs/rfcs/erratum/):
| Module | RFC | What it specifies |
|---|---|---|
delta |
draft-fossil-delta-format-00 |
byte-level delta encoding |
artifact |
draft-fossil-artifact-format-00 |
manifest / control-artifact card format, naming, checksums |
repostate |
draft-fossil-repo-state-00 |
derived branch / tag / leaf / current-trunk views |
submanifest |
draft-fossil-submanifests-00 |
S-card tree manifests: bounded per-commit manifest size for wide repositories |
sync |
draft-fossil-sync-protocol-00 |
HTTP sync / clone protocol |
Implementation follows the RFC dependency layering:
delta -> artifact -> repostate -> sync
\-> submanifest -/
Design
The drafts are the authority, not Fossil's source. Test expectations come from the spec text and its worked examples — every normative clause a module implements gets a test citing its section. Fossil itself appears only in an on-demand interop smoke check, which may confirm a spec-derived expectation but never author one; a disagreement there means either zit misread the spec or the spec is wrong, and the spec gets an erratum rather than the code a workaround.
No module touches ambient time, randomness, filesystem, or network; effects arrive through injected interfaces. The sync client in particular is a pure state machine — bytes in, bytes out — with transport supplied by the caller. This keeps the library embeddable and makes the whole stack testable under seeded deterministic simulation, where any failure replays exactly from its seed.
Scope
zit targets the version-control substrate: the check-in (manifest), cluster, and tag/control artifact types. The collaboration types (wiki, ticket, forum, attachment, technote) are carried opaquely — stored and transported by hash, but not interpreted.
Beyond the substrate, the CLI carries Git interop in both directions
(import-git, export-git), sub-manifest adoption tooling with
replay-measured k selection (submanifest analyze/enable), a local rebuild
checkpoint that collapses cache-cold opens from minutes to seconds
(checkpoint), store reclamation (shun, pack), and an fsck (verify).
What Fossil has that zit doesn't
Deliberate scope, not defects — zit is a substrate, not a Fossil replacement.
- The collaboration layer. Wiki, tickets, forum, technotes, and
attachments (and their
Wcards) are carried opaquely — stored and synced by hash, never interpreted. The web UI (fossil ui) goes with them, as dogrepandsettings. (stashexists: save/list/show/apply/pop/drop over a local.zit-stashsidecar, with a fail-closed apply instead of merging.) - Delta manifest authoring on fresh repositories. A repository whose history carries delta manifests keeps its dialect — commits there emit deltas against the proper baseline. A purely flat repository stays flat; zit never introduces the delta dialect on its own.
- Manifest signing. Signature wrappers are recognized and stripped on read (§4.1.1); zit never signs what it commits.
- SHA-1 repository creation. SHA-1 history clones and reads fine;
zit initwrites the sha3-256 naming policy only. - Unversioned-file management. No
uvverb and no unversioned store, by decision rather than omission: the uvfile sync vocabulary is fully parsed and content-verified on the wire, and a peer's unversioned files are declined visibly — the session completes and reports "N unversioned file(s) not transferred (unsupported)" instead of aborting or dropping them silently.
What zit has that Fossil doesn't — and where compatibility breaks
- Sub-manifests (
draft-fossil-submanifests-00): S-card tree manifests bound per-commit manifest size on wide repositories, with adoption tooling that replays the repository's own history to measure whether enabling pays (submanifest analyze/enable,import-git's k selection).
This is the one compatibility break. A repository that emits S-bearing
artifacts is permanently unreadable by stock Fossil (§8.1: disabling stops
new trees but cannot retire existing ones), and sync's peer-version gate
refuses to send trees to a pre-S peer rather than hand it artifacts it
would misread. Emission is off by default; enabling it — and import-git,
whose default imports as trees — states the spend.
- The rebuild checkpoint (
zit checkpoint): a local, disposable sidecar witness that collapses cache-cold opens from minutes to seconds on large histories. Never stored, never synced; no wire or format impact. - A library-first, sans-IO architecture: every module is embeddable and runs under seeded deterministic simulation; the sync client is a pure state machine.
Everything else round-trips with real Fossil in both directions — clone,
push, pull, and serve are exercised against a stock fossil binary in the
smoke suite, R checksums included. Where the code deliberately follows
Fossil's implemented behavior against the published draft text, the
divergence is documented in docs/rfcs/erratum/.
Build
Requires Zig 0.16+.
zig build # build the `zit` binary into zig-out/bin/
zig build run -- version
zig build test # run unit tests
Further named steps: zig build dst (deterministic simulation, --seed N to
replay), zig build fuzz -- <target> --seed N (seeded fuzz targets),
zig build bench (benchmarks), zig build cov (kcov coverage report over the
test suite), and zig build smoke (interop check against a fossil binary on
PATH; run manually at milestones, never part of test).
Source control
This project is itself under Fossil. The repository lives in .zit.fossil
(ignored); open a checkout with fossil open --keep .zit.fossil.