The Store

Underneath the shop is a content-addressed object store: every artifact is named by the hash of its bytes, and the name and the bytes verify each other. Re-hash an object and you know whether it is what it claims to be.

objects/blake2/<hash>   the bytes
catalog/<key>           a name pointing at a hash

Objects are immutable and shared. The catalog maps names — locators, derivation keys — onto the hashes they currently resolve to, so names can move while the objects they point at stay fixed. Two packages that compile the same source produce the same object and store it once.

One interface, many backends

A store is anything that can answer two questions:

get_manifest()            -> catalog rows, object rows, roots, metadata
get_content(hash, range?) -> canonical bytes, verified against that hash

That is the whole requirement. The optional range is what lets a caller take part of a large object without materializing all of it — a ranged answer carries the whole-object hash, the offset and length returned, and the range proof its manifest or sealed container supplies, so a slice verifies as strictly as a whole object.

Anything meeting that interface can be a source, which is why the shop can draw from such different places:

BackendWhat it is
git repositorysource blobs, trees, commits, and refs — local or on a server
local object storethis shop’s own built artifacts
pit bundlea shop slice frozen into a signed file
carta target-placed read-only store with physical range rows
another shopmounted directly, or served by an actor in front of it
remote storea signed manifest, then objects or verified ranges over HTTP
plain foldera development directory, adapted through a generated manifest

A shop can therefore target itself, another shop, another actor, a bundle, a folder you are editing, a git checkout on disk, or a repository on a website — and the code asking for an object does not change.

Layers

Mounted stores form an ordered stack. A lookup asks local layers first, then peer or remote layers as shop policy allows, and every object that comes back is checked against its content hash before it is used. A corrupt or substituted object fails at its own address.

Because objects are immutable and named by content, adding a layer can only add availability. Nothing a new layer says can change what an existing hash means.

Fetching only what is missing

Distribution follows from the interface. Get the manifest, compare its object hashes against what the local layers already hold, ask only for the ones missing, verify each on arrival, then check the whole root closure before making the result live.

Fetching is driven by the closure, never by the container. What travels is the set of hashes the closure names and the local layers lack — not a repository, not a package tree, not a whole bundle. A git host negotiates the blobs and trees behind those hashes the same way a peer actor or a remote store hands over built artifacts: the manifest says what is wanted, and only that arrives.

A shop is a store

A shop is not only a consumer of stores — it is one. Everything in your shop is available to be served, so another shop can simply mount yours and retrieve files from it. There is no export step and no special artifact: the shop’s own content store answers get_manifest and get_content like any other backend.

That is what makes distribution work without a central server. A machine with no route to the internet can mount a shop on the machine beside it, take the objects it is missing, and verify every one against its hash. Trust does not come from the transport, so an untrusted peer is a usable source.

Pit bundles

A pit bundle is the same slice of a shop, frozen into a file instead of served live: a selection of the objects a shop holds, a catalog naming them, and the lockfile, signed and packaged so it can be moved, mounted, and verified as one unit. That makes it convenient for shipping and for offline installation.

It is one way to satisfy get_manifest/get_content, not the way distribution works. The same program can be fetched from a git repository, pulled object-by-object from a peer, mounted from another shop, read out of a cart, or taken from a folder on disk. What travels is always manifest-first and hash-verified; a bundle is just a particularly portable container for it.

Mounting is the trust decision

A shop looks for whatever it needs — source, mcode, an image, a whole runnable program — among its mounts. There is no separate authorization step afterward: what you mounted is what you trust. Adding a mount is therefore the security-relevant act.

Mounting is not an endowment, though. The clerk mounts, and what it can mount is which mount modules it carries: a directory, its own .pit store, a git repository, an archive. A $mount capability would be a second gate over gates that already exist — the real ones sit underneath, in the endowments those modules claim. A directory mount needs the target’s file endowment; a git mount reaches the network through an actor that claims a socket. A shipped game reaches only what was placed in front of it because it carries no module that could reach further.

Two kinds of lookup behave differently, and the difference is exactly where a hostile mount could lie:

Content lookups verify themselves. Ask for blake2:7f7f… and the bytes prove what they are. A bad mount can fail to answer; it cannot substitute. So taking objects by hash from an untrusted peer is safe on its own terms.

Catalog lookups are trust. A row saying this locator resolves to that hash, or this derivation key produced that output, is an assertion nothing can check. A mount that answers derivation keys is telling you what compiling something produces, and you are taking its word.

That distinction is where per-mount policy belongs when it is wanted: which mounts may answer catalog rows at all, and for what — source only, source and mcode, or built pools too. A shop that states nothing there lets every mount answer everything, which is why the mount list is the boundary that matters.

See Capabilities for the authority model and Security and Threat Model for what that does and does not defend.