blobmap manifest schema¶
A blobmap manifest records which objects in a zarr store move to and from tape together. One JSON object per scope, stored alongside the data it describes but never inside it.
This page is the permanent home of the format. The schemas below are stable URLs: once published, a version's constraints never change.
Versions¶
| version | schema | status |
|---|---|---|
| 3 | manifest-v3.schema.json |
current |
| 2 | manifest-v2.schema.json |
superseded |
Version 2 predates the pinned field and is otherwise identical. It is
published so that manifests written during development remain identifiable,
not because it is supported: no v2 manifests should exist in production, and a
consumer written today should accept version 3 only.
Version 1 was never used outside a single development branch and is not published.
Moving from 2 to 3¶
There is no migration tool, because there is nothing worth migrating. Blob definitions are recomputed from the store, so the answer is to delete the old manifest and partition again:
blobmap --data ... --manifests ... partition <scope>
The only content a repartition cannot reconstruct is pins, and version 2 had none. Do this before anything is archived: once tape copies exist, blob identifiers must be preserved and deleting a manifest orphans them.
Using it¶
Validate a manifest against the schema in any language with a JSON Schema implementation. Draft 2020-12.
curl -sO https://waterpark.dkrz.de/blobmap/manifest-v3.schema.json
check-jsonschema --schemafile manifest-v3.schema.json manifest.json
import json, urllib.request, jsonschema
url = "https://waterpark.dkrz.de/tech/blobmap/manifest-v3.schema.json"
schema = json.load(urllib.request.urlopen(url))
jsonschema.validate(manifest, schema)
The blobmap package carries its own copy and validates on read, so Python
consumers need neither the download nor a JSON Schema library.
What a manifest looks like¶
{
"schema_version": 3,
"scope": "healpix/era5land/PT1H/level_9.zarr",
"epoch": 4,
"generated_at": "2026-08-19T09:12:00+00:00",
"generated_by": "blobmap 0.5.0",
"policy": {"t_max_bytes": 107374182400, "t_min_bytes": 0,
"t_hot_bytes": 16777216, "width_clamp": 8, "pow2_floor": 64},
"hot_always": ["**/zarr.json", "**/.zarray", "**/.zattrs",
"time/**", "cell/**"],
"pinned": [],
"blobs": [
{"id": "b_t2m", "prefixes": ["t2m"],
"bucket": {"index": 0, "width": 2048, "key_encoding": "v2_slash"}}
],
"provenance": {}
}
The central idea is that a manifest is a rule, not a table. The entry
above resolves any chunk of t2m arithmetically:
t2m/5000/0 -> b_t2m_2 because 5000 // 2048 == 2
So chunk five million resolves without anyone having enumerated it, and appending to a store requires no change to the manifest.
Keys matching hot_always or covered by a pinned prefix are never
archivable. A key that matches nothing is unmanaged, which also means it stays
on disk: that is the safe default, not an error.
Stability policy¶
Constraints are frozen. Once a version is published, no change may make a previously valid manifest invalid, or a previously invalid one valid. Anything that would is a new version at a new URL, and the old schema stays served indefinitely.
Prose may be corrected. A title or description may be fixed in place.
Validators read only constraints; the prose is for people. Bumping the version
for a typo would invalidate every manifest in existence, since consumers check
schema_version exactly.
Superseded versions stay online. A manifest written years ago must remain checkable against the schema it was written for, even when nothing is expected to produce that version any more.
The two schemas reject each other, deliberately. A version 2 document fails
against version 3 because schema_version must be exactly 3, and a version 3
document fails against version 2 because pinned is not a known field. There
is no version in which a document is silently readable as the wrong one.
Fields¶
| field | required | meaning |
|---|---|---|
schema_version |
yes | Format version. Reject a version you do not know. |
scope |
yes | Prefix these definitions cover, relative to the bucket. |
epoch |
yes | Bumped when definitions change, so a consumer can detect staleness without diffing. |
blobs |
yes | The definitions. A list of rules, so its length tracks cut decisions, not object count. |
hot_always |
yes | Glob patterns never archivable. Derived from the store's structure and recomputed on every partition. |
pinned |
no | Prefixes deliberately held on disk. Preserved across repartitions; carries who, why and an optional review date. |
policy |
no | Thresholds used to produce this cut. |
generated_at, generated_by |
no | Provenance of the producing run. |
provenance |
no | Measured sizes, for debugging. These go stale while the manifest is untouched, so a consumer must not read them. |
A blob is always {id, prefixes, bucket}. bucket: null means one bucket, so
the resolved identifier is always {id}_{n} and a consumer needs one code
path rather than two.
Blob identifiers are join keys against tier state and, through it, against tape addresses. They are derived from the full prefix and are independent of the order arrays were listed.
Implementation¶
The reference implementation can be retrieved by the schama subcommand
which validates manifests on read and carries its own copy of the schema,
so Python consumers need neither the download nor a JSON Schema library.
The published files here are generated from it:
blobmap schema > manifest-v3.schema.json
Questions: waterpark@support.dkrz.de.