Lock a file that can't be merged¶
TOVIO's default is optimistic and non-blocking: conflicts are data, and work is never serialized. That works because text can be merged. Binaries can't. Two divergent edits of a 200 MB checkpoint, a CAD file, or a design export cannot be reconciled — the later writer's work is simply lost. For those paths, you reach for a lock.
Built and tested; release evidence remains open
File locking is built: tovio lock (acquire, list, release) and the Forge-side arbitration of
exclusive grants (LockStore, tovio forge lock list / break) are done and pass the hermetic
--features tls test suite. Production binaries passed the distinct-Docker-host exercise under a real
operator CA; public-release, broader HA/load, hosted-parity, and external evidence remain Phase 5
gates. Enforcement is narrower than "nobody else can write this file" — read
what a lock enforces today before you rely on it.
Locking is the exception, never the default
Locking re-introduces exactly the serialization TOVIO is built to avoid. Use it only for paths that genuinely can't be merged. Don't lock text — text belongs in the conflict model, where edits never block each other. A path is either usefully lockable or conflict-merged, not both.
Two kinds of lock¶
- Exclusive — the path is single-writer: the holder edits it until they release the lock or it expires. A land that finds the path edited divergently on both sides is refused rather than turned into a conflict object, and — with a Forge origin — a land that would write a path someone else holds is refused too. This is the right tool for "two people must not edit this at once."
- Advisory — a visibility signal. Nothing is refused on its account; a diverging land goes through the ordinary conflict model. Good for soft coordination; agents default to advisory.
Your repository policy decides which paths are lockable and whether their locks are exclusive or
advisory; --exclusive / --advisory on tovio lock override the declared kind for one lock.
Lockable paths are declared in policy¶
You can only lock a path the policy manifest declares lockable. That keeps locking deliberate — nobody locks an arbitrary file on a whim. Lockability is declared by pattern (a glob), while a lock itself is always on one exact path. Each declaration carries the two fields below, and the most-specific matching pattern wins — the same ranking the manifest's read policies use:
pattern = "assets/checkpoints/**" # which paths may be locked
kind = "exclusive" # exclusive or advisory
Try to lock a path that isn't declared lockable and TOVIO tells you so (TVO-LOCK-003, "is not
lockable"), pointing you at the manifest — it doesn't just fail.
No CLI verb declares a lockable path yet
The manifest carries lockable declarations and the client reads them, but no tovio policy
subcommand writes one — the field is reachable only through the library that builds the manifest.
Until that surface lands, a repository's lockable patterns have to be authored by whatever tooling
produced its manifest, and tovio lock on a fresh repository will answer TVO-LOCK-003.
Acquire, list, release¶
Every lock carries a required TTL. That's deliberate: an expired lock releases itself, so a teammate who locks a file and then goes on leave never strands the asset.
When the repository has a saved origin (the Forge you cloned from), tovio lock asks that Forge for the
grant first, so the lock is authoritative — arbitrated by the one server every teammate talks to:
$ tovio lock assets/checkpoints/model-v4.ckpt --ttl 2d --reason "retraining run"
🔒 Locked `assets/checkpoints/model-v4.ckpt` (exclusive) on forge.example.dev:7743 — authoritative (Forge-arbitrated), expires at 2026-09-10 14:02:11
reason: retraining run
Release it: tovio unlock assets/checkpoints/model-v4.ckpt
If the Forge can't be reached, TOVIO still records the lock locally — but says so, marks it non-authoritative, and lets the Forge arbitrate on the next reachable operation. A repository with no origin at all keeps a purely local lock table.
See what's locked. tovio lock list is this replica's last-known view (expired locks are dropped on
read); tovio forge lock list --remote <forge> is the open read of every grant the Forge holds:
$ tovio lock list
2 active lock(s):
🔒 exclusive assets/checkpoints/model-v4.ckpt (held by did:key:z6Mk…, expires at 2026-09-10 14:02:11)
reason: retraining run
🔒 advisory design/hero-banner.psd (held by did:key:z6Mp…, expires at 2026-09-08 18:30:00)
Release when you're done:
Releasing is idempotent — releasing a path you don't hold succeeds quietly. tovio unlock <path> and
tovio locks still work for one release as deprecated aliases of lock release and lock list; they
print a one-line note on stderr and run the same code.
What happens when someone else holds the lock¶
Acquiring a path that's already held fails clearly, naming the holder and the expiry — so you know exactly who to ping and how long it'll be:
$ tovio lock assets/checkpoints/model-v4.ckpt --ttl 1h
✗ `assets/checkpoints/model-v4.ckpt` is already locked
An active lock on `assets/checkpoints/model-v4.ckpt` is held by `did:key:z6Mk…` (expires at 1789000000).
An exclusive lock is single-writer until it is released or its TTL expires.
See who holds it and when it expires:
tovio locks
Wait for the holder to `tovio unlock` it, or for the TTL to expire
[TVO-LOCK-001]
What a lock enforces today¶
Be precise about where the gate sits, because it is land-shaped, not "nobody else can write this
file". Two independent checks run, and both run at land:
- The no-conflict-on-exclusive invariant (
TVO-LOCK-002). Alandthat finds an exclusive-lockable path edited divergently on both sides is refused instead of producing a conflict object — a binary can't merge, and the later write would be silently lost. The same refusal fires when achange rebase/split/absorbcascade, or aswitch, would leave a conflict on such a path. This check is entirely local and keys on the path's declared kind, not on who holds a lock — so it works in a solo repository with no Forge at all. - The land-time write-guard (
TVO-LOCK-001). When the repository has a Forge origin,landtries to take the authoritative Forge lock for every exclusive-lockable path the land writes, before any ref advances. Another principal already holding one refuses the land, naming the holder. This is the holder-based, cross-machine half: two machines can't race conflicting edits into the same unmergeable binary. Paths you already hold an exclusive lock on are skipped, so your own long--ttllease is never truncated to the guard's brief window. With no Forge configured the guard is a no-op, and an unreachable Forge warns and proceeds on your local lock rather than blocking a write on an outage.
And one place it deliberately does not run:
pushis not lock-gated on the client. A push advances every local lane at once and the client doesn't know the relay's per-ref tips, so it can't compute the true written delta — a head-only check would fall open and a whole-tree check would over-block unrelated pushes. Authoritative push-time arbitration belongs in the Forge's write gate and is a follow-on. Land is the integration point that is guarded, so route shared-binary work throughland.
$ tovio land my-lane --into main
✗ Cannot land: `assets/checkpoints/model-v4.ckpt` is exclusive-lockable and diverged
`assets/checkpoints/model-v4.ckpt` is declared `lockable = exclusive`, so concurrent divergent edits are
NOT reconciled into a content conflict — a binary cannot merge and the later write would be
lost. Two versions diverged: ours `blake3:…`, theirs `blake3:…`. Serialize edits through an
exclusive lock instead of merging.
Acquire the lock before editing:
tovio lock assets/checkpoints/model-v4.ckpt --exclusive --ttl 1h
See current locks:
tovio locks
Keep one side's version and re-land; the other holder re-applies their edit under the lock
[TVO-LOCK-002]
Offline clients surface the last-known lock state
Exclusive grants are arbitrated at the Forge when it is reachable. An offline client can't consult a remote lock, but it shows you the last-known lock state so you're not editing blind, and it records a non-authoritative local lock rather than pretending it holds the grant. An advisory lock only ever signals — you may proceed.
Breaking a stuck lock (admin)¶
If a lock outlives its usefulness before the TTL, a forge-admin can force-release it on the Forge. The
request is signed and gated server-side (a non-admin gets a clean 403), and the break is written to the
Forge's audit log, attributed to the admin — no silent seizure.
$ tovio forge lock break design/hero-banner.psd --remote forge.example.dev:7743
✓ Broke the lock on design/hero-banner.psd (admin force-release; audit-logged to the Forge).
Locks don't touch reads¶
A lock restricts modification, never reading or cloning. It's entirely orthogonal to the cryptographic read-permission model — locking a file changes who can write it, not who can see it.
Recap¶
- Lock only unmergeable binaries; leave text in the conflict model.
- A path must be policy-declared lockable; locks are exclusive (a diverging land is refused) or advisory (signal only).
tovio lock <path> --ttl <d>acquires — on the Forge when you have an origin;tovio lock listshows this replica's view;tovio lock release <path>releases.- Every lock has a required TTL and self-releases; an admin can
tovio forge lock break <path> --remote <forge>(audited). - Two gates run at land: the local divergence refusal (
TVO-LOCK-002) and, with a Forge origin, the holder-based write-guard (TVO-LOCK-001).pushis not lock-gated on the client. - Locks gate writes, never reads or clones.
Where to next¶
- Big assets that don't need a lock — just commit them: see large binaries.
- The landing gate that keeps a shared lane buildable: lane protection.
- Read permissions, which locks don't touch: the permissions guides.
Last reviewed September 9, 2026
Suggest an improvement to this page Not for security reports — see disclosure