Skip to main content
The local brain is an OCI Image Layout, so publishing is a copy rather than a conversion, and selective installation falls out of the layout.
One layer per installed module, the snapshot as the config blob. Any OCI tool can copy the result without this SDK.

Pack: no network at all

pack materializes the current snapshot as an OCI artifact inside the local layout, with no registry involved. It is also what persists a travelling index — see the warning at the end of Ingestion.

Two identities per layer, and the pair is the point

Two clients that packed the same blocks with different gzip settings have different layer digests and the same Merkle root. The digest drives transfer; the root drives meaning.

Push

Only the blobs the registry lacks are uploaded, then the manifest. The digest it returns is the same name the registry filed it under — which is what makes org/brain@sha256:… resolvable, the only way to point at a version nobody can move a tag away from. Publishing selected modules writes a Projection config under application/vnd.gaussia.boltzmann.projection.v1+json. It is a view, not a new snapshot: the document binds the source snapshot digest and copies the retained ModuleRef values verbatim. A consumer resolves that source from the history layer, verifies its signatures, and rejects any reference that is not an exact subset of it. The source-snapshot manifest annotation remains only a pre-download hint; authority comes from the digest inside the projection document. A consumer that installed the projection cannot publish it back over that tag until reconciliation restores references for the source modules it did not receive; it may still publish the partial view under a different repository or tag.
A push refuses to overwrite a remote whose snapshot is absent from the local history. The two brains advanced from a common ancestor, and publishing would drop whichever side lost.
It raises DivergenceError, a DistributionError you can catch on its own, because it is the one distribution failure with a defined remedy: see Reconciliation. Pass force=True only when you mean to replace the remote.

Tags move; digests do not

A tag is a pointer, like a git branch. Pushing to v1 repeatedly moves it. Your local history keeps every version (ten by default, RetentionPolicy.retained_roots), each still verifying. The remote does not — when v1 moves, the previous manifest is untagged, and registries collect untagged manifests. So the discipline is the one you already use for container images:

Plan a pull

One manifest request, no layers:

Pull, selectively

A selective install is a first-class outcome, not a partial failure. Taking the semantic module without the canonical one is a real way to consume a brain, and the manifest records what was left out. Asking for a module you did not install is an error, never an empty module.
What you install carries the digest that was published, and the layers you already hold are reused by digest rather than transferred again.

Rollback protection

A moving tag can be rewritten to an older snapshot. When the served head is a strict ancestor of the head already held, pull raises RollbackError with a ROLLBACK report and leaves the current head unchanged. Returning to that version must be explicit:
The override always logs a ROLLBACK warning. If local pruning removed the ancestry needed to decide the relationship, the pull continues with a ROLLBACK_UNCHECKED warning; uncertainty is reported but is not treated as proof that the registry moved backwards.

Ignore incompatible vector indices

A vector index can only be loaded by a consumer using the same representation space. When an artifact was published with an unavailable or incompatible embedding model, install the verified module blocks without its vector layers:
This option does not weaken module verification and does not silently translate vectors: every requested module is still checked against the published Merkle root. The SDK is model-agnostic, so the caller must then build a compatible local vector index before relying on semantic retrieval. The default remains strict and rejects a published index from a different model space.

Fetch: retrieve a history without adopting it

fetch writes blocks and the remote history into the local layout and leaves the current snapshot exactly where it was. That is the difference from pull: judging an incoming history should not require adopting it first. It is the step at which nothing has changed yet, and it is what Reconciliation starts from. No index is touched. A travelling vector index is bound to the root it was built over, so loading one for a history that is not installed would leave this brain holding an index bound to a root its snapshot does not name.

The history travels as its own layer

A snapshot names its parents. An artifact that published only its head would hand over a lineage whose links resolve to nothing — the chain an audit walks would stop at one, and nobody but the publisher could find the ancestor two histories share. So the snapshot documents ship alongside the modules. Its own layer rather than loose blobs, because a blob no manifest references is unreferenced, and a registry is entitled to reclaim it. A snapshot document is a few hundred bytes and compresses well against its near-identical siblings.

Publishing without a registry

LocalLayoutRegistry speaks the same RegistryClient protocol against directories on disk — useful for tests, air-gapped transfers, and understanding what a push does:

Supplying your own transport

OrasRegistryClient requires the oci extra. The rest of distribution — packing, layer construction, local layouts — needs nothing beyond the standard library.

Inspecting an artifact

A registry UI can only draw the types it was built to know, so a brain often shows up as an unrecognized artifact. Nothing is broken; read it through the manifest instead:
docker pull on a brain will fail, correctly. It is an OCI artifact, not a container image.