Skip to content

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:

$ tovio lock release assets/checkpoints/model-v4.ckpt

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). A land that 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 a change rebase / split / absorb cascade, or a switch, 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, land tries 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 --ttl lease 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:

  • push is 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 through land.
$ 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 list shows 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). push is not lock-gated on the client.
  • Locks gate writes, never reads or clones.

Where to next

Last reviewed September 9, 2026

Suggest an improvement to this page Not for security reports — see disclosure