Imagine the postmortem. A training run had four parents. The code that launched it lives in git, and git handles that fine. The dataset revision lives in an object-store path with a date baked into it. The checkpoint it resumed from lives in a bigger object-store path that someone eventually pruned to save money. And the knowledge that shaped it (which eval set was canonical, which preprocessing spec was current) lives in a wiki updated three weeks after the run finished.
Six months later a research lead asks whether run 4471 can be reproduced. The code commit is easy. The dataset path still exists, though nobody can prove the bytes under it are the bytes from March. The checkpoint is gone. The wiki page has been edited nine times since.
The lab and the run are composites, but the pattern gets a nod from everyone I describe it to. Labs version four things with four tools of wildly different quality: code well, datasets by naming convention, checkpoints by bigger naming convention, and the binding knowledge by tribal memory.
I am building Grixel, a version control and data platform, at
Unabated Games. It grew out of game development, where the daily
workload is the same shape as a lab's: a little code attached to an
enormous amount of binary data, plus standards documents everyone is
supposed to follow and mostly cannot find. The proposition for a lab is
one content-addressed depot where a training run's code, dataset
revision, config, and the standards it followed are a single
reproducible, access-governed graph. The demo tenant below borrows the
hypothetical's name: a research org at /companies/parallax,
with a run at runs/run-4471.
#One depot, one catalogue
Before any command lines, the picture. Imagine the whole lab's artifacts in one library. Every version of every book is kept, and shelf space is cheap because two editions that differ by one chapter share the unchanged pages. The catalogue at the front desk is the only authority on what exists. You never photocopy the library to work; you request the three shelves you need. And if you are not cleared for a restricted collection, the catalogue does not show a locked door: those entries are simply absent from your copy of the catalogue.
Technically: Grixel is a server-authoritative depot. One global
namespace, with paths like
/companies/parallax/datasets/pretrain-v3/. There is no
clone; there is checkout of a declared view. The server
daemon, grixeld, owns everything (metadata, access rules,
chunk storage, atomic changelist commits); the CLI is a thin client.
Files are split into content-defined chunks, each hashed with BLAKE3,
and only new chunks are stored or transferred.
This is Perforce-shaped rather than Git-shaped, and it matters for a lab even though most labs never ran Perforce. In a Git-shaped system every machine holds a full copy and the server is a hosting convenience; policy lives in a forge layered on top. That model cannot give you path-level invisibility, because the data is already on the laptop. Cross-repo pins it can genuinely approximate: a submodule is a committed SHA pin into another repository. What it lacks is a server that validates the pin, a queryable notion of drift, a mode that advances only past reviewed revisions, and an experience anyone defends. A server-authoritative single namespace turns governed sharing and reproducible pins from per-team glue into structure. Perforce proved the shape at studios pushing terabytes of binary assets; Grixel keeps the shape, replaces the substrate, and adds the layer labs need: governed, pinned context for humans and AI agents.
· · ·
#Three decisions, and their costs
Three design decisions do most of the work in this article, and each has a real cost. I would rather you hear the costs from me.
Local-filesystem chunk store fronted by a caching read proxy, instead of object-storage indirection. Chunks are immutable and named by their hash, so a transparent read proxy can sit between a compute cluster and the depot and cache bytes on the LAN, the way Perforce's proxy has fanned out game builds for decades. The cost: you give up the operational story of S3, its durability, lifecycle policies, and billing. lakeFS rides your existing buckets and inherits all of that on day one; Grixel asks you to run a daemon and own its disks. For hot-path training data the proxy keeps repeated multi-gigabyte reads inside the rack, so I think the trade is right. It is still a trade.
Committed, server-validated lock files, instead of
per-machine cache state. A project declares its external
dependencies (a dataset elsewhere in the namespace, an eval spec, the
lab's data standards) in a committed scope.json; the
resolved revisions are written to a committed scope.lock.
The server rejects any lock that is not a correct resolution of its
scope, so a committed pair is always internally consistent, and
reproducibility becomes a property of the depot rather than of any
machine's cache. The cost is ceremony: advancing a pin is an explicit,
committed operation, never something a sync does silently.
Single-writer metadata. Changelist commits serialize through a single write path, WAL-backed and crash-recoverable. Chunk upload happens outside that critical section; bytes stream in concurrently, and only the metadata commit at the end serializes. That keeps atomicity easy to reason about and the serialized section short, and it still puts a ceiling on metadata write throughput. For changelists the ceiling is irrelevant; humans and CI do not commit thousands of times per second. But it makes the depot the wrong place for high-frequency telemetry. Grixel is the system of record for artifacts and decisions. It does not want your training metrics.
A note on the transcripts: they were captured in local mode, where
the CLI asserts a principal with -as <user>; in a
team deployment you grixel login once, drop the flag, and
identity comes from the server-verified token.
· · ·
#Where chunking wins, and where it finds nothing
Content-defined chunking backs the storage claims, so here are both faces of it, including the one that does not flatter the product.
The win case first: an append-heavy dataset shard. The first submit stores all 96 MiB new; then a week of examples is appended and the shard resubmitted whole:
$ grixel submit -as rin -ref companies/parallax/datasets/pretrain-v3 -m "shard 000: append week-26 examples" pretrain-shard-000.tar
Committed: 85c88f1cb4ccaf0e011d42f6262a49919ab6d53ad7a64bc4bba6074bc4121877
ref : companies/parallax/datasets/pretrain-v3
changes : 1 files, 100.0 MiB
stored : 6.5 MiB new, 93.5 MiB deduped
parent : 2b14d133e011eff0e8207c6accc3edc79801eff86157187bc51e12228d0e92d9
author : rin
message : shard 000: append week-26 examples
100 MiB submitted, 6.5 MiB stored, and every earlier revision stays checkable-out.
Now the case where you might expect the same result and must not. Adjacent checkpoints of a dense run feel like they should be "mostly the same bytes." They are not: a dense optimizer step perturbs essentially every parameter, the serialized tensors differ everywhere, and chunking finds nothing to share. Two adjacent checkpoints from the demo run:
$ grixel submit -as rin -ref companies/parallax/runs/run-4471 -at checkpoints -m "checkpoint step 8000" step-08000.safetensors
Committed: 71cd8849e6ae6dfa2b165ad9330d968e3b6d08453bb08a93401ebfa6774b10b0
ref : companies/parallax/runs/run-4471
changes : 1 files, 96.0 MiB
stored : 96.0 MiB new, 0 B deduped
parent : (initial commit)
author : rin
message : checkpoint step 8000
$ grixel submit -as rin -ref companies/parallax/runs/run-4471 -at checkpoints -m "checkpoint step 9000" step-09000.safetensors
Committed: 319e11b6873bfceb2dadc87c03ac717fd722f2b201d358f29d343dd8201ea684
ref : companies/parallax/runs/run-4471
changes : 1 files, 96.0 MiB
stored : 96.0 MiB new, 0 B deduped
parent : 71cd8849e6ae6dfa2b165ad9330d968e3b6d08453bb08a93401ebfa6774b10b0
author : rin
message : checkpoint step 9000
Zero bytes deduplicated, and the tool says so at submit time. Dense adjacent checkpoints do not dedup, in Grixel or in anything else built on content-defined chunking; if a storage vendor tells you otherwise, ask for this transcript. What versioning buys on those files is retention, addressing, and provenance, at full storage cost per step you keep.
Between those poles sits the warm-start fine-tune, where most of the model is frozen and a slice is retrained. The unchanged tensors dedup against the parent checkpoint:
$ grixel submit -as rin -ref companies/parallax/runs/run-4471 -at finetune -m "warm-start: 11 of 96 MiB retrained" ft-warmstart.safetensors
Committed: e0fa0de37f5fb23b1140280b0e1389714af418c4b37689fca1effa0ab0ee859c
ref : companies/parallax/runs/run-4471
changes : 1 files, 96.0 MiB
stored : 29.1 MiB new, 66.9 MiB deduped
parent : 319e11b6873bfceb2dadc87c03ac717fd722f2b201d358f29d343dd8201ea684
author : rin
message : warm-start: 11 of 96 MiB retrained
29.1 MiB stored where 11 MiB of tensors changed; chunk boundaries do not line up with tensor boundaries, so the overhead is real and printed. The remaining wins are boring: a re-uploaded identical artifact stores zero new bytes anywhere in the namespace, which makes retries and copies idempotent, and configs and tokenizers are noise in the budget.
On scale: these demo files are around 100 MiB where yours are 6 GB or 600 GB; the mechanism is identical chunk by chunk and unmeasured at your sizes, and the closing section bounds that claim. The same manifests drive delta-pull: syncing a small change to a large file fetches only the new chunks.
#A node pulls only what it needs
A checkout names the subtrees it wants, so an eval node that needs only the config does not pull the checkpoints:
$ grixel checkout -as rin -path 'configs/**' companies/parallax/runs/run-4471 eval-node-ws
Checked out companies/parallax/runs/run-4471
dir : ~/demo/eval-node-ws
base : 0a897ad88e0b3ba42c3817bea0dae13caffdc2e397abdb17fedf9bd12814ed69
files : 1
view : configs/** (partial checkout)
Edit files, then: grixel changes / grixel diff / grixel submit -m ...
$ du -sh eval-node-ws
16K ~/demo/eval-node-ws
The ref holds hundreds of MiB of checkpoints; the workspace on disk
is 16K, and grixel sync widens or narrows the view later.
It is the difference between "the repo is 2 TB so nobody versions the
weights" and versioning the weights.
· · ·
#Reproducibility as a committed file, not a cache
This is the part I care most about for labs, because naming conventions have no equivalent. The run project declares its dependency, pinned to an exact dataset revision (the hash is the appended-shard commit from earlier):
$ cat .grixel/scope.json
{
"name": "run-4471",
"owner": "team:pretraining",
"description": "Run 4471: seasonal-mix pretrain",
"mappings": [
{
"source": "/companies/parallax/datasets/pretrain-v3",
"mountAt": ".data/pretrain-v3",
"pin": { "mode": "rev", "rev": "85c88f1cb4ccaf0e011d42f6262a49919ab6d53ad7a64bc4bba6074bc4121877" }
}
]
}
$ grixel scope update -as rin
companies/parallax/runs/run-4471 — resolved 1/1 mapping(s):
.data/pretrain-v3 <- /companies/parallax/datasets/pretrain-v3 @ 85c88f1c
Committed scope.lock: a022a03146c27869b7687378c61129f95c37a317753a5a3e5829747c86792ed5
ref : companies/parallax/runs/run-4471
author : rin
The resolved lock is a committed, server-validated file in history. Two consequences follow.
First, checking out the project at any historical commit replays its
mappings at the revisions pinned then. Run 4471 pinned the dataset at
85c88f1c; checking out the commit that launched it
materializes the dataset at 85c88f1c, today, next year, on
a machine that has never seen the project. There is no "whichever
version happened to be in the cache." Reproducibility stops being a
runbook and becomes a property of the checkout.
Second, freshness is a queryable, shared state. A rev
pin reports behind N when the dataset advances N
changelists past it; a head-mode pin reports
drift-on-head when the source moves, while the workspace
keeps the locked snapshot. Advancing a pin is a committed, reviewable
act, so "when did we move to the new eval set" has an answer with a hash
and an author.
There is a third pin mode: approved. Grixel's review and
sign-off system lives in the server, and an approved-mode mapping
resolves to the latest revision of the source that has fully cleared its
review gate. In the demo depot's other tenant, a mapped standards ref
gets a new, unreviewed commit at its head;
grixel scope update re-resolves to the same signed-off
revision, and moves the pin only after a reviewer signs the new head
off. Read "data-cleaning standard" for "brand standards." The MCP
integration assembles AI context bundles from these same pinned
mappings, so an agent's context advances only past reviewed content,
never an unreviewed draft.
That covers three of the four parents from the opening. The code
parent closes concretely: grixel vendor pins an external
git repository read-only inside a project at a fixed SHA, recording
source URL, branch, and commit as import provenance; or, if the training
code stays canonical in git, the launcher records the git SHA in the
changelist's provenance. Either way the code revision joins the same
graph as the dataset pin and the config.
· · ·
#What an unreleased model does not look like
Every lab holds things that must not leak internally: an unreleased model, a restrictively licensed dataset, a partner's data. Grixel's access rule is invisibility. A denied path is not "permission denied"; it is gone, from listings, search, error messages, and AI context. These transcripts come from the demo depot's studio tenant; a hidden unannounced game and a hidden unreleased model are the same mechanism.
Bob, who can read the whole namespace, lists
/companies/acme/games and sees two projects,
seasonal/ and secret/. Alice, whose
permissions cover only the seasonal project, sees one. If she guesses
the hidden path anyway, the answer is byte-identical to a path that has
never existed:
$ grixel ls -as alice /companies/acme/games/secret
grixel: not found: /companies/acme/games/secret
$ grixel ls -as alice /companies/acme/games/doesnotexist
grixel: not found: /companies/acme/games/doesnotexist
This is enforced structurally: every metadata-returning handler routes through one access-resolution chokepoint, and a model-based leak test asserts a less-privileged view is byte-identical whether a hidden path exists or not. The property extends to everything path-anchored: tickets under a hidden project are hidden, and provenance on a commit you cannot read is absent. An AI assistant authenticated as Alice gets a context bundle in which the secret project has never existed, a far stronger statement than "the assistant was told not to mention it."
#Who, or what, made this change
In a lab, a growing fraction of commits are AI-authored or
AI-assisted, and "who changed this" is no longer answerable from a
username. Any changelist can carry structured provenance
(--tool, --model, --role, a note)
alongside the server-verified principal:
$ grixel provenance -as maya 9c80ad050744013231cdcf05e1d5856a866ac1843e05b81c6ee0cdd4107ab498
9c80ad05 (declared by alice, 2026-07-05T23:32:41Z)
claude-code (claude-opus-4-8) — assisted: drafted from quest brief
Provenance is a side-record keyed by the commit hash, never a field inside the commit, so content hashes stay untouched and an agent can auto-tag its own commits through an environment variable. A change can also capture the AI's working context, the session transcript the assistant worked from, content-addressed, bound to the commit, and optionally encrypted client-side so the server cannot read it. When a compliance review asks "which tool wrote this" and then "what did it know when it did," the depot has both answers.
The work itself is trackable in the same namespace: a jobs system with an admin-defined schema of experiment and bug types, path-anchored so hidden projects hide their tickets, event-sourced so agents and CI edit without clobbering each other, and linked bidirectionally to the changelists that resolved them. The backlog lives with the artifacts it describes, under the same permissions.
#Pipelines, retention, and moving bytes
None of this is worth much to a lab if it is CLI-only. Every command
takes --json and emits a frozen, versioned envelope (a
schemaVersion field, a stable error-code vocabulary), and
--json status carries the mapping freshness state as
structured data, so a scheduler can refuse to launch a run whose pins
have drifted. Beneath the CLI sits a public, stable gRPC API with
first-party Go and Python SDKs, so a training launcher can resolve pins
and record the launching commit without shelling out.
The opening featured a pruned checkpoint, and labs prune
deliberately, so here is the deletion story. grixel ref rm
retires a run's ref, and grixeld gc mark-and-sweeps
chunk-store objects unreachable from any live ref. The interlock with
reproducibility matters most: commits pinned by a committed
scope.lock are protected as GC roots, so retiring an old
run cannot break a downstream eval project that still pins its dataset.
An offline grixeld verify walks every ref, confirms the
objects exist in the chunk store, and scans the op-log for read errors;
run it after any unclean shutdown. Backup is a point-in-time archive
taken under the writer lock, consistent by construction, and that is its
limitation too: stop-the-writer or a filesystem snapshot; continuous
replication does not exist. The growth curve at lab scale, chunk and
metadata volume over a year of real runs, is unmeasured.
For distribution, the design is the caching read proxy: chunks are immutable and hash-named, so a proxy in the rack serves repeated reads locally and a cluster cold-starting the same dataset revision hits the origin roughly once. The design has a ceiling of its own: three hundred nodes each pulling a 200 GB revision is 60 TB through one proxy's network interface, so the real questions are proxies-per-cluster, cache sizing, and eviction under mixed workloads.
· · ·
#Where lakeFS, DVC, and Git LFS stand
Each of these tools is good at what it was built for.
lakeFS gives you Git-like branch semantics over object storage, per repository. For dataset branching and pipeline isolation inside one repository it is strong, and it inherits S3's scale and durability by riding your existing buckets. What it does not give you is one namespace across repositories: a training project consuming a dataset repo, an eval repo, and a standards repo at coordinated pinned revisions is application-layer stitching you build and maintain. In Grixel that mapping is a first-class, server-governed object with a committed lock and drift detection.
DVC bolts data versioning onto git with pointer
files, and the pointers deserve more credit than pitches like this
usually give them: .dvc files and dvc.lock are
committed hash locks, and dvc pull fetches by hash
regardless of any machine's cache state. The real gaps are elsewhere.
Nothing enforces pointer/data consistency server-side: a pointer commits
fine while the object it names was never pushed, and you find out at
pull time. The remote is a bucket whose contents can be lifecycled,
garbage-collected, or mutated out from under the committed pointers.
Permissions live outside the VCS's own model; S3 IAM prefix conditions
are path-shaped, but they are a second permission system,
hand-maintained, invisible to the version history, and free to drift
from it. And dedup is whole-file: the appended shard above stores 100
MiB in a DVC remote instead of 6.5. Against all that, DVC costs nothing
to adopt and composes with infrastructure a lab already runs. That is a
real argument.
Git LFS was built for "some big files in a code repo," and it works at that scale. A lab's situation is inverted: the dataset is the repo, and the code is the small thing attached to it. Git LFS at dataset scale means smudge-filter pain, no chunk-level dedup between versions, and a server never meant to be the system of record for petabytes.
The honest scoreboard: lakeFS and DVC are production-proven at labs today, and Grixel is not yet. What Grixel offers that neither can retrofit is the single global namespace with governed cross-project pins, invisibility-grade access control, and the AI context plane, all enforced at one server chokepoint. And the review, jobs, and provenance surfaces are the beginning of a forge inside the VCS. A Git forge's protected branches do block direct pushes; the seam is that branch protection is hosting-layer configuration, in force only where that forge is, while Grixel's gate is a committed policy evaluated by the server that owns the bytes, so it travels with the depot.
#What a lab should demand first
ML infra readers are the most skeptical audience I write for, correctly. The state of the evidence:
The caching read proxy has no published multi-node, multi-gigabyte throughput numbers. The design follows Perforce's proxy, with its decades of production history for this exact fan-out problem, and the single-node path is implemented and tested. But "Perforce-shaped" is an argument, not a measurement. The benchmark a lab should demand is the one sketched above: many nodes, a multi-GB revision, rack-local proxies, with proxy count, cache sizing, and eviction reported. Until I publish it, treat cluster fan-out as design intent.
Petabyte scale is the same category: the system is designed with it in mind and measured far below it. The transcripts above are real and verbatim at around 100 MiB per artifact; your 600 GB checkpoint exercises the same code path and is an extrapolation until measured. Two adjacent gaps matter at lab scale. There is no FUSE mount, so pipelines materialize files through checkout and sync; streaming data loaders and datasets larger than a node's local disk are unaddressed today. And the checkout cost of many-small-files layouts, millions of tiny example files rather than packed shards, is unmeasured; pack your shards.
There is no multi-region replication: one authoritative daemon plus read proxies is the deployment shape, and a lab spanning continents will feel that. The single-writer ceiling means what I said earlier: changelists yes, telemetry no. And the retention machinery has its own unmeasured curve, storage growth under a year of dense checkpoints that, as the transcript shows, do not dedup.
What would settle all of this is one lab running one real project
next to its existing stack: dataset and checkpoints in the depot, pins
in scope.json, the launcher recording commits through the
SDK, gc on a schedule. Hold every claim here to the
checkpoint transcript's standard: run it, print it, publish the number,
whichever way it comes out.
If you run infrastructure at a lab and any of this contradicts something you learned the hard way, tell me. The checkpoint section owes its current form to one such correction.
Grixel is pre-release. Read the docs, or request early access.