Skip to content

Store records

The records a store keeps beside its objects, such as aliases, bundles, revisions, policies and releases, with their fields and IDs.

A store holds objects, which are identified by their content and accepted by kernel replay. It also holds records above object identity: names, selections, history, decisions and releases. No object cites a record, so a record never changes an object’s ID, and records are outside the identity rules that crates/lema-core/tests/identity.rs pins. None of them is a kernel check. The rules below are store rules, tested without a runtime; the code is in crates/lema-store/src/.

Overview

RecordSchemaIdentified byStored inWritten by
Aliasnoneits namealiases.jsonalias, submit/import, publish, replace
Bundlelema.bundle/v1content IDbundles/<ab>/<id>.jsonbundle.create
Binding maplema.binding-map/v1content IDrecords/binding-map/<ab>/<id>.jsonbinding-map.create, merge.create
Authoring bundlelema.authoring/v1content IDrecords/authoring/…authoring.create, publish
Snapshotlema.snapshot/v1content IDrecords/snapshot/…snapshot.create, merge.create
Revisionlema.revision/v1content IDrecords/revision/…revision.create
Policylema.policy/v1content IDrecords/policy/…policy.create
Guarantee recordlema.guarantee/v1content IDrecords/guarantee/…executable
Merge recordlema.merge/v1content IDrecords/merge/…merge.create
Reviewlema.review/v1content IDrecords/review/…review.create
Project referencenoneits namerefs.jsonref.create, ref.publish, ref.adopt
Change reportlema.change-report/v1content IDnot storedrevision.report, snapshot.diff, ref.report
Releaselema.release/v1content IDreleases/<ab>/<id>.jsonrelease.create
Actornoneits actor IDactors.json (lema.actors/v1)lema actor add

<ab> is the first two hex digits of the ID.

Content IDs

A record’s content ID is the SHA-256 of a hash domain of its own followed by the record’s canonical JSON:

id = sha256("LEMA-<KIND>-v1\0" ‖ canonical(record))

The domains are LEMA-BUNDLE-v1, LEMA-BINDING-MAP-v1, LEMA-AUTHORING-v1, LEMA-SNAPSHOT-v1, LEMA-REVISION-v1, LEMA-POLICY-v1, LEMA-GUARANTEE-v1, LEMA-MERGE-v1, LEMA-REVIEW-v1, LEMA-CHANGE-REPORT-v1 and LEMA-RELEASE-v1, each followed by a zero byte. They differ from each other and from the objects’ LEMA-TYPED-CORE-v3, so a record can never have an object’s ID.

These rules hold for the stored records with a content ID: bundles, project records and releases. A change report shares the canonical form and the bounds.

  • Fields are exact, and unknown fields are refused. Lists are sorted with no repeats, except the parents of a revision or a merge record, whose order is meaningful. Empty optional fields are absent, except a policy’s require.axioms, where [] means no axiom and is kept. A record in any other form is refused, never repaired.
  • Every list, every text and the whole record have a maximum size.
  • A record is written once. Its creation is a history.jsonl line: "event": "record" with kind and id for project records, "bundle" for bundles, "release" for releases. Every read re-checks the bytes, the ID and the rules.
  • A creation request’s lists are sorted before hashing, and a repeated item is refused, never merged. The same content in any order gives the same record.
  • Every object a project record names must be accepted; an obligation’s statement, clients and dependencies need only be stored. Every record it names must already be stored, so the revision graph has no cycles.
  • Every record but a policy carries namespace, the namespace object’s ID. A policy carries one only when it has protect gates.

Two kinds of text recur. A path is relative: /-separated components of ASCII letters, digits and ._-+@, none empty, . or .., at most 256 bytes. An actor ID is 1 to 128 bytes of ASCII letters, digits and ._@-, starting with a letter or digit.

Aliases

aliases.json maps a name to {id, role?, generation}.

FieldMeaning
idAn accepted object.
roleOptional: one role of that object, such as a structure’s constructor.
generation1 when the alias is created, one more at each move. Re-asserting the current target moves nothing.

A name is 1 to 512 bytes with no control characters. Aliases are never removed, so a generation is never reused, and an expectation with a generation detects a move from A to B and back to A. Every move names what it expects the alias to hold, or overwrite: true (server methods). Each request’s changes are one history line, {"event", "changes": [{name, target, generation, previous, overwrite?}], …}, written before aliases.json is replaced. Moving an alias safely is the move an alias guide.

Bundles

A bundle names a set of accepted objects once. elaborate’s receive binds its exports as Received.* names, which builds exactly the objects that the same bindings written by hand build.

FieldRequiredMeaning
schemayeslema.bundle/v1
namespaceyesThe namespace object’s ID.
exportsyes1 to 4,096 {name, id, role?}, sorted by name. A name is a dotted Lean identifier of at most 256 bytes; the role is one its object has.
helpersnoExport names hidden from default listings.
dependsno1 to 64 bundle IDs it re-exports from.
docnoText, at most 4,096 bytes.

A bundle record is at most 4 MiB. bundle.create resolves from_aliases once: the record holds the IDs the aliases named then, and later alias moves do not change it. Using bundles is the name a set of objects with a bundle guide.

Binding maps

A binding map selects a project’s roots and exported names.

FieldRequiredMeaning
schemayeslema.binding-map/v1
namespaceyesThe namespace object’s ID.
rootsnoUp to 16,384 accepted object IDs.
exportsnoUp to 16,384 {name, id, role?}, sorted by name.

At most 8 MiB. Renaming an export changes the binding map, not the objects. binding-map.create also takes from_aliases and alias_prefix, read once.

Authoring bundles

An authoring bundle keeps original source, source-to-object mappings and notes.

FieldRequiredMeaning
schemayeslema.authoring/v1
namespaceyesThe namespace object’s ID.
sourcesyes1 to 256 {path, text}, each text at most 4 MiB without NUL.
mappingsnoUp to 16,384 {source, name, id, role?}, sorted by source and then name.
notesnoUp to 16,384 {id, text}, one per object, each text at most 16,384 bytes.
validationyes{status: "unchecked"}, or {status: "elaborated", call, tooling_sha256, inputs_sha256}.

At most 16 MiB. authoring.create always writes unchecked: the stored source is not claimed to reconstruct anything. Only publish with authoring writes elaborated, which records that this server elaborated exactly these inputs into the mapped objects in that call. inputs_sha256 is sha256("LEMA-ELABORATION-INPUTS-v1\0" ‖ canonical JSON) of the request’s source, roots, prefix, bindings, receive, seeds and seed_salt. It is a record of what happened, not a re-check.

Snapshots

A snapshot is a binding map plus separately referenced authoring material and assets.

FieldRequiredMeaning
schemayeslema.snapshot/v1
namespaceyesThe namespace object’s ID.
bindingsyesA binding map’s ID.
authoringnoUp to 64 authoring bundle IDs.
assetsnoUp to 4,096 {path, kind, sha256, size}, kind being doc, config or asset.

At most 4 MiB. The store holds only an asset’s digest and size, never its bytes. Editing documentation changes the snapshot and not the binding map, and two histories that reach one state share its snapshot ID.

Revisions

A revision goes from its first parent’s after-snapshot, or from the empty state, to after.

FieldRequiredMeaning
schemayeslema.revision/v1
namespaceyesThe namespace object’s ID.
parentsno1 to 16 revision IDs, in order. Absent for an initial revision.
basefor mergesThe common base: present exactly when there are two or more parents.
beforewith parentsThe first parent’s after, filled in by revision.create. Absent for an initial revision.
afteryesA snapshot ID.
intentyes1 to 4,096 bytes.
evidencenoUp to 1,024 {kind, id}: accepted (an accepted object) or guarantee (a guarantee record).
obligationsnoUp to 256 obligations: all those open after the revision.
dischargedwith parentsUp to 256 {obligation, by} (an accepted theorem’s ID) or {obligation, waiver} (text).

At most 1 MiB. An obligation is {claim, statement?, clients?, assumptions?, depends?}: a claim of 1 to 4,096 bytes, the statement object it names (if any), and up to 256 client IDs, assumption texts (each at most 1,024 bytes) and dependency IDs.

Obligations are inherited. Each obligation open in any parent is either listed again, exactly, or discharged. by must be an accepted theorem whose statement is the obligation’s statement, matched by identity; an obligation without a statement can only be waived. An obligation cannot be both open and discharged, and only one some parent has open can be discharged. Bringing a waiver into a project reference needs an actor in the active policy’s waive list.

Policies

A policy decides who may move a project reference and what a revision must meet.

FieldRequiredMeaning
schemayeslema.policy/v1
versionyesA positive integer.
previousfrom version 2The policy it follows, of the version before; present exactly when version is 2 or more.
publishyesActor IDs that may publish a revision (0 to 256).
adoptyesActor IDs that may adopt the next policy (0 to 256).
resetnoActor IDs that may move the reference back to an ancestor.
waivenoActor IDs that may bring waived obligations into the reference.
requireno{axioms?, profiles?, no_obligations?}, below.
docnoText.
namespacewith protectThe namespace object’s ID.
protectno1 to 16 gates, sorted by export.
reviewno{reviewers, adopt?, distinct?, display?}, below.

At most 256 KiB. An absent or empty reset or waive means nobody.

require fieldMeaning
axiomsUp to 64 axiom names that every kernel receipt in the revision’s closure, and in the closure of the objects it cites, must stay within. [] allows none. A name below the fixed floor is refused. This is on top of the store’s own axiom policy.
profiles1 to 16 {profile, exports}: each named export must be covered by a cited guarantee record of that profile whose verdict is holds and whose checker is the current one.
no_obligationstrue: the revision has no open obligation, inherited ones included.

A gate protects an implementation export by identity.

Gate fieldRequiredMeaning
claimyesimplements, refines or replaces.
exportyesThe implementation export it protects.
stateyesThe export binding its start state.
contractyesThe contract declaration’s ID.
startyesThe start state’s ID.
contract_exportrefines onlyThe export binding the reference’s contract.
parametersnoreplaces only: 1 to 64, the contract binders the claim quantifies over.
representationsnoAllowed relation IDs, up to 64. Not for implements.
clientsnoAccepted client theorem IDs, up to 64. Not with parameters.
reviewnodelegated or steward: the review a move needs.

review lists reviewers (1 to 256 {actor, authority}, authority delegated or steward, sorted by actor), and optionally adopt (the authority an adoption needs), distinct: true (a reviewer’s approval does not count for its own move) and display: true (only approvals bound to the approval view they read count). A steward’s review meets a delegated requirement.

Project references

refs.json maps a reference name to {policy, revision, generation}.

FieldMeaning
policyThe reference’s active policy.
revisionThe revision the project’s line is at, or null at the explicit empty initial state.
generationCounts every change of either part, never reused.

A name follows the alias rules. A reference moves only through ref.create, ref.publish and ref.adopt, through the same engine as aliases: one history line per request, with the actor and the decision. It never takes an overwrite, and every move names the revision, generation and policy version it expects. Setting one up is the set up a project reference guide.

Actors

actors.json (lema.actors/v1) maps an actor ID to {operator, token_sha256}. lema actor add writes it with the server stopped and shows the token once. An operator may create project references. A token authenticates the bearer of a token the operator issued, and nothing more: every client of a local store runs as the store’s OS user, who can also read and write the store’s files. Tokens are not rotated or revoked in this version.

Reviews

A review records one reviewer’s decision on one move of a project reference.

FieldRequiredMeaning
schemayeslema.review/v1
namespaceyesThe namespace object’s ID.
revieweryesAn actor the active policy lists in review.reviewers.
authorityyesdelegated or steward, as that policy grants.
policyyes{id, version} of the active policy.
refyesThe reference name.
basenoThe reference’s revision when the review was made.
subjectyes{revision} for a publication or reset, or {policy} for an adoption.
decisionyesapprove or reject.
declarations, closurenoWhat the subject binds: the revision’s gated exports, or the policy’s pins.
notenoText.
displayno{renderer, sha256}: the approval view the reviewer read.

At most 256 KiB. Each review.create also appends the decision to the history, even when the record exists already, so a reviewer’s latest decision on a transition is defined. An approval never stands in for missing evidence.

Guarantee records

A guarantee record stores one Std.Guarantee.IR receipt that executable computed with profiles, for a revision to cite as evidence. No method takes one from a client.

FieldMeaning
schemalema.guarantee/v1
namespaceThe namespace object’s ID.
profileNoCopySliceCall, NoBigIntPrimitives or NoBoxOrCtorInCycles.
verdictholds, unsupported or violated.
identities1 to 64 root names mapped to accepted object IDs.
checkerThe guarantee checker’s digest. A record from another checker no longer meets a policy’s profiles.
inventoryThe SHA-256 of the captured IR inventory.
callThe native call that captured it.

At most 64 KiB. Its receipt’s own claim is “tested, not proved”: a static check of the captured Lean.IR closure of the roots, which reads what the elaborating process reported.

Merge records

A merge record stores the three-way comparison of two or more lines against their common base.

FieldRequiredMeaning
schemayeslema.merge/v1
namespaceyesThe namespace object’s ID.
baseyesThe base revision: an ancestor of every parent, and a nearest common one when merge.create writes it.
parentsyes2 to 16 revision IDs, in order.
conflictsnoEach conflict, with its resolution when one was given.
bindings, afternoThe merged binding map and snapshot, present together exactly when every conflict is resolved.
reusenoResolved merges only: each evidence item cited on a merged line, its premises, and whether the merged target still selects them.

At most 16 MiB. A conflict is on an export, asset, source or note, and is a collision, delete-update, update-update, rename-rename or bundle conflict. Immutable objects are never merged; a conflict is between bindings. The merge revision itself is a revision.create with the merged snapshot, the same parents in the same order, and the base. Merging is the report on a change and merge two lines guide.

Change reports

A change report says what moving from one snapshot to another changes, computed by identity from the two snapshots’ binding maps, assets and authoring bundles, never from source text. It is a computed view and is not stored; the same store state gives the same report and the same ID. It is at most 16 MiB.

FieldRequiredMeaning
schemayeslema.change-report/v1
namespaceyesThe namespace object’s ID.
summaryyesThe count of changes per category, and obligations and uncarried.
conservativeyestrue when every change adds an export or root, or is material that needs only its digest, and a merge revision follows its three-way merge.
revision, before, afternoWhat was compared.
refno{name, generation, event, reset?}, for a reference’s move.
changesnoThe changes, sorted by category and then by name, path or ID.
mergenoFor a merge revision: the comparison with its base.
decisionnoFor a reference’s move: its recorded decision.
uncarriednoThe obligations the changes need that the revision does not carry.
CategoryA change ofRequires
renamean object and role exported under another nameidentity
exportsan export or root added or removedreceipt when added, deprecation when removed
rebinda name, to a theorem of the same statement or a definition of the same templatereceipt
substitutea name, to another implementationreplacement, a replace witness
evolvea name, to a refined or corrected contractrefinement, a refine witness
policya reference’s active policydecision
materialsources, notes, docs, configuration, assets, authoring bundlesdigest, or mapping for sources and authoring bundles

Missing evidence becomes an open obligation in the revision format. The store has no deprecation record, so a removal always stays open until an authorized waiver closes it.

Releases

A release is an immutable record of what a deployment runs: one artifact pinned to the store’s accepted closure and to a deployment configuration’s digest. release.create checks the artifact against the tool’s rules and pins before it writes the record. A release adds no claim of its own: its proof receipts are the store’s kernel receipts, and the artifact and its IR receipts are tested.

FieldRequiredMeaning
schemayeslema.release/v1
namespaceyesThe namespace object’s ID.
programyes{kind, artifact, key, binary, provenance, exec?, runner?}, below.
closureexec, exec-app{roots, objects, sha256}: the roots (name to ID), the closure’s size and its closure record’s digest.
receiptsyes{proof?, ir?, capability?}: the Std.Guarantee.Proof, Std.Guarantee.IR and Std.Guarantee.Capability receipts.
bundlesnoUp to 64 bundle IDs.
deploymentyes{sha256}: the deployment configuration’s digest.
docnoText, at most 4,096 bytes.
program.kindArtifactWhat the release holds
execan execution artifact (lema.exec-artifact/v1)program.exec, the closure, the proof and IR receipts
cap-appa capability application (lema.cap-app/v1)the capability receipt only; its C is not bound to accepted objects
exec-appan app artifact (lema.exec-app-artifact/v1)all of the above and program.runner, the reviewed runner

program.artifact is the artifact’s ID, the SHA-256 of its canonical key. program.binary is {backend, file, sha256}. A record is at most 8 MiB.

The deployment digest is sha256("LEMA-DEPLOYMENT-v1\0" ‖ canonical(input)), over an input with schema lema.release-deployment/v1: the capability configuration as given and the digest of each trust anchor it names. Credential files are never opened, so no secret value enters the input or the release.

A release is never changed. A withdrawal is a record releases/<ab>/<id>.withdrawn.json (lema.release-withdrawal/v1) and a history line, and either one withdraws it; a withdrawn release cannot be selected. A supersession is a history line, and a superseded release can still be selected, as a rollback. Selection takes a release ID, never a name, and needs the release’s creation line in this store’s history. Withdrawal and supersession stay local to one store. Releasing is the release an artifact guide.

Written against lema-lang at commit 5a94951, 17 Oct 2026.