Skip to content

Inside a .mtrl archive

Think of tamper-evident packaging with a contents checklist glued to the lid. The seal tells you whether the contents still match the list, and the checklist tells you exactly which compartment failed — it does not flash one red light and leave you guessing. A .mtrl file works the same way. It is one self-contained package of everything you saved, sealed by a manifest of digests, and its checks report each compartment separately.

When you saved polymer-film-01 in lesson 2, the .mtrl file you wrote is a bounded archive: a ZIP container holding every record, run, metadata value, source, and revision of your work. The ftir-replicate-01 run, the “spectrum” checkpoint and each of its immutable record versions, the lineage between them, and the full revision history all travel inside this one file.

The layout is fixed and explicit. A small set of JSON documents describes the archive: archive.json for identity and sample metadata, runs.json, checkpoints.json, and records.json for your data and its immutable versions, active.json for the current selections, and revisions.json for the ordered history. Payload bytes (your spectra) live under payloads/, retained source bytes under sources/. On top of all of this sits integrity.json, the manifest: a complete inventory of every entry with its digest. That manifest is the checklist on the lid.

Every JSON document is written as canonical bytes under RFC 8785. Canonical form means the same content always serialises to the same bytes, with no ambiguity in key order or number spelling. That is what makes the digests meaningful: a digest only proves something if everyone agrees on exactly which bytes were hashed. Because the document bytes are reproducible, the digests of unchanged content are stable across saves, and a single altered byte inside a canonical document breaks the match. The container’s own metadata, such as entry timestamps, sits outside that guarantee: integrity checks cover entry contents, not every container byte.

Opening a file you did not write is a small act of trust, so MTRL bounds that trust. Every archive is read through a bounded reader that enforces hard limits before it trusts anything. It caps the archive file size, the number and sizes of ZIP entries, the compression ratio, the size and nesting depth of each JSON document, the record count, and more — fourteen limits in all, with defaults such as a 1 GiB file, 4,096 entries, and a 100:1 compression ratio.

The limits are checked in order, before memory is allocated or payloads are decoded. A declared size is rejected before a buffer exists to hold it. A hostile or corrupt file — a decompression bomb, a deeply nested JSON document, an archive claiming a million records — meets a hard limit and is refused, rather than being trusted with unlimited memory and time. The defaults exist so that opening an archive is never an act of faith.

An archive that asks for something your installation does not understand is refused outright. An unknown archive format or schema version fails the load; MTRL does not warn and continue with a partial read. The same rule governs format features: only registered format features may gate a load, so an archive can require a capability (such as scientific assertions) only if that capability is a recognised, registered feature. Anything else in the file is descriptive metadata and cannot hold your data hostage.

Verification reports each layer separately

Section titled “Verification reports each layer separately”

Verification is the check of the seal against the checklist. It reads the manifest and re-checks the archive against it — and then, deliberately, it does not give you one answer. A VerifyResult carries independent statuses for each layer: structural (is the archive well-formed), hash (do the bytes match their digests), anchor (does the history root match), coverage (is every entry accounted for), and source closure (can each result be traced back to retained source evidence). Alongside the statuses it carries individual issues, each naming what failed and where.

There is no single aggregate “verified” flag, and that is a design choice, not an omission. One yes/no answer would hide which layer failed. A corrupt payload and a missing source are very different problems with very different remedies; the separate statuses let you tell them apart at a glance. You saw this report when you verified your first archive in Save, load and verify.

Verification tells you the archive is intact today; Trust over time explains how revisions and receipts carry that trust forward as the archive grows. To run the checks yourself, revisit Save, load and verify.