Skip to content
VERASPEC
Repository
VER 1.1 — draftdraft

§4 Model bundle manifests (ADR-0007)

4.1 The problem

1.0 §6.1 says “Weight pinning is mandatory” and the schema constrains weights_sha256 to 64 hex characters. Nothing says what is hashed. A modern checkpoint is not one file: it is a config, a tokenizer, and one or more shards, sometimes present in two formats at once. Measured today: one implementation folds file names into the digest preimage with no length framing, and hashes whatever the local cache happens to hold — so two conformant producers of the same space_id emit different weights_sha256. The mandatory pin is unverifiable.

4.2 Normative text (DRAFT)

ADR-0007 is accepted in principle, revised. The revision is the one that matters most: weights_sha256 is not redefined. Retrofitting a preimage onto a published field silently changes the meaning of values already in circulation, which is a major change wearing a minor's clothing. weights_sha256 keeps its 1.0 reading — E29's reading — forever: SHA-256, lowercase hex, over an artifact the Producer chooses and SHOULD document, comparable only within a producer. The 1.1 field is new, and it carries its own digest.

spaceDescriptor.model.bundle declares the file set the checkpoint consists of, and carries the digest of that declaration:

json
"bundle": {
"bundle_digest_alg": "ver-bundle-manifest/1",
"manifest_sha256": "8da0801c…",
"files": [ { "path": "config.json", "byte_length": 58, "sha256": "…" },
{ "path": "model.safetensors", "byte_length": 59, "sha256": "…" },
{ "path": "tokenizer.json", "byte_length": 55, "sha256": "…" } ]
}
  • bundle_digest_alg names the construction intrinsically. "ver-bundle-manifest/1" is the following definition, and a future construction is a new identifier rather than a new reading of this one:

manifest_sha256 := SHA-256( RFC 8785 JCS serialization of {"files": [ … ]} ), over exactly the entries carried in files, pre-sorted byte-wise ascending by the UTF-8 encoding of path.

There is no separate canonicalization member: a decorative member that every producer sets to the same constant is a member that tells a verifier nothing, and folding it into the algorithm identifier removes the possibility of a manifest whose two self-descriptions disagree.

  • manifest_sha256 is the cross-producer pin. weights_sha256 is not, and MUST NOT be compared across producers (E29). A Consumer comparing two descriptors for the same space_id compares manifest_sha256.
  • path grammar. path is relative to the checkpoint root. / is the only separator. Segments are non-empty and are never . or ..; there is no leading /; and every character is drawn from the printable portable set [A-Za-z0-9._-]. **Backslash, colon, NUL and control characters are rejected** — the schema encodes both an anchored pattern and a complement-class rejection, so a validator's reading of $ cannot admit a trailing newline (VER-1.1-draft.md §15, finding REV-05). A wider character repertoire — Unicode NFC, case-sensitive comparison — may be defined later by a new bundle_digest_alg; it is deliberately not defined now, because path canonicalization across platforms is where this class of digest goes wrong.
  • Paths MUST be unique within a manifest, and entries MUST be pre-sorted byte-wise ascending by the UTF-8 encoding of path as stored — a verifier checks the order it received rather than sorting first, so a manifest whose order was tampered with is detected rather than repaired. Duplicate or aliasing paths (two entries naming one file) are non-conformant.
  • **Verification MUST resolve every path beneath a verified root and MUST NOT follow symbolic links out of it.** The grammar makes a traversal payload unrepresentable; this rule makes a symlinked traversal unusable, which the grammar cannot reach.
  • byte_length is the file's exact length in octets and is present for framing: with lengths in the manifest, the concatenation ambiguity of SC-03 cannot arise. sha256 is the file's own digest, over its bytes and nothing else — in particular the filename is not folded in (SC-01).
  • **Membership is defined by the space publisher, and the digest proves the integrity of the LIST, not its completeness.** A manifest with correct per-file digests but a missing shard is a self-consistent manifest describing an incomplete bundle. No digest can attest to a file nobody listed. The bundle MUST list every file that affects the model's output and MUST NOT list files that do not (READMEs, licences, .gitattributes); where a checkpoint is published in two weight formats, the bundle lists one.
  • “Affects the model's output” means the forward pass this space declares, not everything the checkpoint can do. A kind: "visual" space over a joint image-text checkpoint lists the image tower's files and MUST NOT list the tokenizer, which cannot affect an image embedding; a joint_visual_text space over the same checkpoint lists both, because both towers are in its declared geometry. Two spaces over one checkpoint that declare different forward passes therefore have different bundles and different manifest_sha256 values. That is correct and not a collision: they are different spaces, and 1.0 §6.2 already makes them so.
  • Artifacts outside the checkpoint are pinned in model.artifacts[].

4.3 model.artifacts[] and model.revision

spaceDescriptor.model.artifacts is an optional array of typed non-checkpoint artifacts that affect the space's output:

json
"artifacts": [ { "role": "pca_basis",
"uri": "https://assets.example.org/bases/…f32le",
"sha256": "b7fb860e…", "byte_length": 56 } ]
  • role is one of tokenizer, pca_basis, codebook, processor, normalizer, other. The set is closed in 1.1 and is a registry candidate (§2.4).
  • At least one of path (same grammar as a bundle path) and uri is REQUIRED; sha256 and byte_length are always REQUIRED, so an artifact named by URI is pinned by construction (§15.3).
  • These artifacts are part of the space's identity under 1.0 §6.2. bundle describes a checkpoint; stretching it into a general artifact manifest would make the file list depend on how a Producer happens to package its pipeline.
  • Where an artifact is also named by an E13 preprocessing member — the text half's tokenizer_sha256 — the two values MUST be equal. preprocessing keeps the E13 member because E13 defines the full descriptor for 1.0.x and VER405 grades it; artifacts[] is the general typed form.

model.checkpoint_uri is a new optional member naming where the checkpoint is remotely resolvable. checkpoint_uri present ⇒ model.revision REQUIRED (schema-encoded). revision is an immutable revision — a commit identifier or content digest, never a mutable branch name.

A Producer whose checkpoint is remotely resolvable MUST state both checkpoint_uri and revision. The schema can only enforce the implication, because it cannot tell whether a bare checkpoint string names a remote repository; the unconditional half is profile material. An unpinned remote checkpoint means one space_id denotes different weights over time, which is a 1.0 §6.2 immutability violation (SC-05).

Interpretation for 1.0 Records. A 1.0 Record carries weights_sha256 with no stated construction. A Consumer MUST NOT compare weights_sha256 values across Producers; in 1.0 — and in 1.1 — it is a within-producer consistency signal only. This interpretation is recorded in the 1.0.1 errata annex as item E29, which also asks 1.0.x Producers to document which artifact they hashed.