What makes an implementation Provetrail-conformant?
5 min readAn implementation is Provetrail-conformant at a level if it accepts every valid record in the published vectors for that level and rejects every malformed or tampered one. The vectors are the whole test: who wrote the code, and whether we like it, does not enter into it. Any runtime, in any language, can be conformant, and anyone can check it by running the vectors against their own verifier.
The suite is versioned with the specification, which is a draft (0.1.0-draft). Vectors are still being added, and the wire format is not frozen until v0.1.0 is tagged.
Conformance is defined by the vectors
A standard is only useful if two independent implementations agree on the same inputs. Provetrail publishes conformance vectors for exactly this: a set of valid records a conforming implementation must accept, and a set of malformed or tampered ones it must reject.
That makes conformance something you measure. If a verifier passes a level’s vectors, it is conformant at that level. If it fails, the vector it failed on tells you where. No committee grants it, and anyone can rerun the check.
What the suite checks
The must-reject cases pin down the failure behaviour that a vague spec would leave to interpretation. A conforming implementation must reject at least:
- A broken root: an event altered after the fact, so the recomputed Merkle root no longer matches the signed root.
- A bad signature: a record signed by the wrong key, or not validly signed at all.
- A non-monotonic sequence: events that claim an ordering the sequence numbers contradict.
- A fold mismatch: a final state that is not the deterministic result of folding the event stream, that is, of replaying the events in order from the initial state.
- A byte/logical disagreement: carried bytes that do not match the logical content they claim to encode.
The must-accept cases confirm the inverse: valid records verify cleanly. An implementation that rejects a valid record is as non-conformant as one that accepts a tampered one.
The levels an implementation can reach
Not every implementation does the same job. A verifier declares the highest of four levels it meets (CONFORMANCE.md is authoritative):
- L1 Structural. Canonical encoding, a well-formed envelope, monotonic sequence numbers, a consistent fold. No cryptography.
- L2 Cryptographic. A valid COSE signature from the bound key, no algorithm substitution, and every event’s leaf committed under the signed root.
- L3 Transparency. RFC 9162 inclusion and consistency proofs against signed roots, and receipt validity.
- L4 Governance-complete. Every side-effecting action has a matching admission, and outcome claims are bound to a check.
On this site, “integrity” means L1 to L3, and “governance and ground truth” means L4.
A verifier can be conformant at the integrity levels without touching L4. The governance and ground-truth events are ones a governed runtime enforced and emitted; a verifier reads them, but only a runtime can produce them. That distinction is covered in signed versus proven.
The reference implementation today
A standard needs running code beside the document. The reference implementation of Provetrail is Flynn, an open agent runtime. It emits records as it executes and verifies them per tier with flynn spine verify.
Flynn’s reference verifier passes the published suite at L1-L4 as scoped at 0.1.0-draft. L4 at this version means exactly two things: admission completeness (gov.*) and outcome binding (shallow.no_ground_truth); gate-result consistency and containment-downgrade detection are explicitly deferred and are not part of conformance at this version. The three reference clients (Rust, Python, npm) implement the integrity tiers (L1-L3).
A signing logger can reach the integrity levels, and a signed, tamper-evident record is useful on its own. Producing L4 records requires a runtime that enforces those invariants at the point it dispatches an action, because a logger outside the dispatcher cannot see an admission decision. Run the vectors against any implementation to see which levels it clears.
The full Flynn write-up is on Flynn’s own site.
Implement your own
We want third-party verifiers: every one that agrees with the vectors is one fewer reason to trust us. The verification path is deliberately thin: rehash the carried bytes into a Merkle root, and check the signature on that root. A verifier does not re-serialize anything, so it cannot drift from the producer. The mechanics are walked through in how third-party verification works, and you can see a verifier run client-side on the home page.
If you build one, run it against the published vectors. If it agrees with the reference implementation on every case, it is conformant, and your users no longer have to trust either of us.
For the definition of the category, see what is verifiable execution provenance.