Metrology Contract
The Mandrel Project ships the Metrology Lab: a service that holds an agent registry, runs the promotion workflow, and provides fleet visibility and an audit trail.
Not every deployment wants to run it, and none should have to in order to use Mandrel. So the
role that service fills is defined separately, as a contract:
mandrel-metrology-spec,
Apache 2.0.
Anything satisfying the contract is a metrology lab. The managed service is one conforming implementation. A CI pipeline with a signing key is another.
Artifacts before APIs
The contract splits in two, and the split is the point.
Artifacts are documents produced and consumed — the Realized Spec, the verification key set, exception grants. Enforcement depends only on these, and verification is offline by construction: the Collet checks a signature against a key it already holds and never calls the authority that produced the document.
APIs are how a lab that is a service exposes lifecycle operations. Useful, but nothing in the enforcement path requires them.
graph LR
SS[Source Spec] --> AUTH
PP[Platform Policy] --> AUTH
subgraph AUTH["Any conforming authority"]
M[Managed Lab]
H[Self-hosted lab]
CI[bake + sign in CI]
end
subgraph ART["Artifacts"]
RS[Realized Spec + signature]
VK[Verification key set]
EG[Exception grant]
end
AUTH --> ART
ART --> COL[Collet<br/>verifies offline]The consequence worth internalizing: producing realized specs needs no HTTP server, no database and no uptime. A pipeline job that bakes, signs and writes a file satisfies the mandatory profile completely.
Conformance profiles
| Profile | Required | Contains |
|---|---|---|
| Core | yes | Produce a valid signed Realized Spec; publish a verification key |
| Registry | no | Agent lifecycle and promotion API |
| RefData | no | Platform policy and chip rate table serving |
| Approval | no | HITL step-up token issuance — only where the operator’s IdP cannot do RFC 9470 |
| Delegation | no | RFC 8693 assertion minting — only where the IdP cannot do token exchange |
A client with no lab implements Core and nothing else. A client replacing the managed service implements Core, Registry and RefData. Approval and Delegation exist because some identity providers cannot do the job; they should be the exception, not the assumption.
What Core actually requires
Three documents in the contract repository are normative. They are not reproduced here — duplicating a normative document onto a website is how the two drift.
| Document | Pins down |
|---|---|
docs/canonicalization.md | RFC 8785 (JCS) plus the YAML 1.2 core-schema profile it needs, and the two hashes |
docs/signing.md | Signature placement and coverage, the claim set, algorithm agility, verification order |
docs/baking.md | Ceilings and injection, workload reference resolution, extension injection |
The canonicalization document is longer than it looks like it should be, for a reason worth
repeating: the divergence between implementations is not in the JSON step. It is in the YAML
step. A library that resolves an unquoted 2026-01-01 to a timestamp, or yes to a boolean,
produces a different hash from a document that neither implementation rejected — and the
signature then fails to verify with nothing pointing at the cause.
Conformance is falsifiable
vectors/ in the contract repository holds the test cases. They are the enforcement mechanism:
a MUST with no test vector is a suggestion.
Rejection cases carry as much weight as acceptance cases. An implementation that accepts a document it should reject produces a hash where a conformant one produces an error, and the resulting signature is valid to one party and absent to the other.
A conformant vector runner must fail when it executes zero cases, must assert the specific
rejection reason rather than merely that something failed, and must fail when no rejection
cases ran. The reference runner in mandrel-go/pkg/realized implements all three, plus a
negative control that runs the vectors against the implementation this contract replaces and
fails if they stop diverging.
Running a lab in CI
The minimum viable metrology lab is a policy file, a pipeline and a key.
graph LR
PP[platform-policy.yaml<br/>governance repo] --> BAKE[bake]
AS[mandrel.yaml<br/>agent repo] --> BAKE
BAKE --> RS[Realized Spec]
RS --> SIGN[sign]
SIGN --> OUT[Artifact, OCI,<br/>or Git tag]Git becomes the audit trail, protected branches become the promotion gate.
Be honest about what this gives up. A CI-signed spec is only as trustworthy as the pipeline: anyone who can modify the CI definition or read the signing variable can bake their own ceilings. Mitigate with protected branches and tags, protected and masked variables, signed commits, and preferably keyless signing so provenance lands in a transparency log rather than resting on a shared secret.
There is also one capability this shape cannot provide:
Agent-name uniqueness. The contract requires the signature’s sub to be unique, but
uniqueness is a property of a namespace, not of a document. Two repositories can promote the
same agent name with no shared registry to notice. An authority holding a registry gets it for
free; a CI-only lab does not, and the operator is responsible for it out of band — a naming
convention, a namespace per team, or a shared registry.
That is the trade being made, and it should be made deliberately rather than discovered.