Overview
tether is version control for datasets that live in several systems at once: an Icechunk repository, a directory of raw files, a Postgres database, the code that produced them. It keeps one small manifest per object in a git or jj repository. A commit of those manifests is a reproducible position for every object. Where the system allows it, tether pins that position with a native ref so it stays readable, and forks a writable branch off it when you want to change things safely.
The loop
tether init # inside a git or jj repository
tether add zarr/imaging --kind icechunk s3://bucket/imaging.icechunk
tether add raw/plate1 --kind file s3://bucket/raw/plate1/
tether commit -m "Baseline" # record every object; pin what can be pinned
tether new -b relabel # a branch in every system, forked on first write
python relabel.py # write through repo.open("zarr/imaging")
tether commit -m "Relabel plate1" # pin the branch heads; move the bookmark
tether promote # land it on main, in every system at onceReading back is one flag: tether open zarr/imaging --rev <commit> (or TETHER_REV=<commit> for a whole job) hands you the system’s native handle at exactly the state that commit recorded.
Where to go
| You want to | Read |
|---|---|
| Run it in ten minutes, on local data | Getting Started |
| Understand the model and how it maps onto jj | Concepts |
| Record state and reproduce reads later | Pinning |
| Change data on a branch, then land or discard it | Branching and Writing |
| Keep the storage bill down; preview store writes as plans | Reclaiming Storage |
| See the whole loop run, with real output | Worked Examples |
| Look up a command’s behaviour, exit codes, JSON | CLI Guide |
| Configure a dataset; keep credentials out of it | Configuration |
| Check what a backend can and cannot do | Backends, Caveats and Performance |
| Publish history as SQL, or import objects from a registry | Registries and SQL |
| Add a system tether does not know yet | Writing a Backend |
| Hand a dataset to a teammate, or run it in CI | Sharing and CI |
| Read an error and find the way out; remove tether from a repository | Troubleshooting |
The generated Reference (top navigation) has every CLI flag and every Python symbol.
Vocabulary
Eight words carry the whole guide.
| Term | Meaning |
|---|---|
| object | One thing tether tracks, under a key such as zarr/imaging: an Icechunk repository, a directory or object-store prefix, a Neon database, a git repository, … |
| dataset | The git/jj repository holding one manifest per object. A dataset commit is a VCS commit of those manifests: a position for every object at once. |
| state | An object’s fingerprint right now – a snapshot id, an LSN, a content digest. Comparing states is how drift is found. |
| pin | A durable native ref (tether.<dataset>.<hash>: an Icechunk tag, a Neon branch, a git tag) that tether creates at commit time so a recorded state cannot be expired or garbage-collected. |
| bookmark | A jj bookmark or git branch of the dataset. It stands for one branch per system. |
| trunk | The main bookmark. It stands for each object’s upstream branch – the branch the object had before tether – so working on the trunk writes there. |
| working ref | The store branch this checkout writes through: the upstream branch on the trunk, tether.ws.<dataset>.<bookmark> on any other bookmark. |
| fork | A writable branch created off a pin, the first time a bookmark writes to an object. |
Backends fall into four tiers: Observed (fingerprint only), Addressable (re-read later by id), Pinnable (a native ref holds it), and Forkable (a branch can be made from it). Backends lists every system’s tier.