Skip to content

Audit

Contamination certificates

One signed file about one run: the harness receipt, the control-arm deltas, the positive-control result and the reasoning-audit findings. Whoever holds it recomputes every hash in it on their own machine, with the Go toolchain and nothing else.

BUILDING The verifier and the format are done and committed. pit_harness.certify assembles and signs, cmd/certverify verifies, and the two share a golden certificate byte for byte. Priced flat per run, with the audit included, once the harness ships.

  • CLI
  • ed25519
  • canonical JSON
  • Go stdlib only

The artifact

The verifier, run against the committed golden certificate

This is the whole report, from cmd/certverify against the 23,347-byte certificate committed in this repository, on 2026-08-27. The golden is unsigned, because signing it would mean committing the key that signed it, so the signature check reports skipped and the verdict is verified_partial.

terminal

$ go build -o certverify ./cmd/certverify
$ ./certverify verify -cert certificate.json \
    -trajectory trajectory.jsonl -manifest MANIFEST.json

certverify · stdoutexit 0

{
  "tool": "certverify",
  "certificate": {
    "path": "cmd/certverify/testdata/certificate.json",
    "certificate_version": 1,
    "certificate_id": "3b32f95fecb55b4a",
    "run_id": "53adff537d13f755",
    "issued_at": "2026-08-27T00:00:00Z",
    "issuer": "AQX LLC"
  },
  "verdict": "verified_partial",
  "exit_code": 0,
  "failed": [],
  "skipped": [
    "signature"
  ],
  "checks": [
    {
      "check": "body_digest",
      "status": "pass",
      "expected": "sha256:0bd013bea7dd553a04aa124e48fe839fbd66f150955c263177046f28e10f75b8",
      "actual": "sha256:0bd013bea7dd553a04aa124e48fe839fbd66f150955c263177046f28e10f75b8",
      "form": "as-stored",
      "proves": "the body has not changed since body_sha256 was written over it"
    },
    {
      "check": "receipt_body",
      "status": "pass",
      "expected": "sha256:bf25b8aaa3aa5ef577ea9fb794f4fe519c9c1cceee8dc9dff79278caf7b87dd9",
      "actual": "sha256:bf25b8aaa3aa5ef577ea9fb794f4fe519c9c1cceee8dc9dff79278caf7b87dd9",
      "form": "as-stored",
      "proves": "the embedded run receipt is the receipt the harness hashed, unedited"
    },
    {
      "check": "signature",
      "status": "skipped",
      "reason": "the certificate is unsigned",
      "proves": "the certificate was signed by the key it names, and has not changed since"
    },
    {
      "check": "trajectory",
      "status": "pass",
      "expected": "sha256:d6d6c3e2449cc88e516484b504b582e3d3ab5227ce730ce04ca2c49271369940",
      "actual": "sha256:d6d6c3e2449cc88e516484b504b582e3d3ab5227ce730ce04ca2c49271369940",
      "file": "cmd/certverify/testdata/trajectory.jsonl",
      "proves": "the trajectory you hold is the one the run recorded, tool call for tool call"
    },
    {
      "check": "bundle_manifest",
      "status": "pass",
      "expected": "sha256:68993e15bfc367571579ffb3b664a4faf4f86d10c39c27bbdfe382b9fbef5a24",
      "actual": "sha256:68993e15bfc367571579ffb3b664a4faf4f86d10c39c27bbdfe382b9fbef5a24",
      "file": "cmd/certverify/testdata/MANIFEST.json",
      "proves": "the run read the corpus release this manifest describes, byte for byte"
    }
  ],
  "run": {
    "valid": true,
    "invalidated_by": [],
    "protocol_complete": true,
    "protocol_gaps": []
  },
  "reasoning_audit": {
    "status": "reserved",
    "verdict": "not_run"
  },
  "cannot_prove": [
    "that the agent reasoned from the rows rather than from memory: the control arms and the reasoning audit bound that, not the signature",
    "that the corpus release is a fair test of the task the agent was given",
    "that the key you verified against belongs to who you think it does; that is the key file's provenance, not this program's"
  ]
}

Build it rather than go run it: go run reports its own exit 1 for any nonzero child and throws away the class codes below. Every check carries a proves line, and the report ends with cannot_prove.

The failure it prevents

A benchmark result nobody outside the team can check

A number in a table is a claim about a run that already finished on a machine you cannot see. Publishing the code lets a reader start their own run, which is a different run from the one the number came out of. A certificate binds the number to the run that produced it, and hands over the hashes to check that binding.

CheckPasses whenWhich means
body_digestsha256 over the stored body equals body_sha256the body has not changed since the digest was written over it
receipt_bodysha256 over the embedded receipt equals binding.receipt_body_sha256the receipt inside is the one the harness hashed, unedited
signatureed25519 verifies over the context line and the bodythe file came from the key it names
trajectoryyour trajectory's sha256 equals binding.trajectory_sha256the trajectory you hold is the one the run recorded, tool call for tool call
bundle_manifestthe manifest's content_digest equals binding.bundle_content_digestthe run read the corpus release that manifest describes

-cert and -pubkey are the two files a reader needs. -trajectory and -manifest check the certificate against artifacts you already hold; leave one out and its check reports skipped. -require-all turns a skip into exit 8.

How it works

Canonical bytes, one signature, a stated boundary

The numbers are restated from the receipt

The certificate embeds the run receipt whole and promotes the parts a reader acts on to the top level: arm_deltas carries every arm-by-clock return and the largest measured delta, positive_control carries the date-recoverability probe per arm, and reasoning_audit carries the claim-level check. None of it is computed at certificate time. A test checks the restatement agrees with the receipt cell for cell, so a certificate cannot report a number the receipt does not carry.

Two certificates from one receipt are the same bytes

The file is compact canonical JSON on one line. Keys sort at every level, floats fix at six decimals, -0.0 is written 0.0, and everything outside ASCII is escaped. The bytes stored for body are exactly the bytes body_sha256 covers, so a verifier hashes what it read instead of reproducing our serialiser. issued_at is the only wall-clock field and it sits outside the hash.

The signature is domain-separated

The signed bytes are pit-certificate-v1\n followed by the canonical body, so a signature over a certificate body cannot be presented as a signature over anything else PIT signs. The certificate carries a copy of the public key for convenience, and certverify checks against the key file you hand it and never against that copy. When the two disagree it fails the signature check, which usually means you hold the wrong key file.

Exit codes say which class of thing went wrong

CodeClass
0every check that ran passed
1usage: bad flags, or a file that would not open
2the certificate will not parse, or is not version 1
3a hash over bytes inside the certificate does not match
4the signature does not verify, or the key is unusable
5the trajectory file is not the one the run recorded
6the release manifest is not the one the run read
7every check passed and the run itself is marked invalid
8-require-all was set and a check had no file to run against

Code 7 means the certificate is genuine and the run it certifies was thrown out by the harness, usually because something attempted network egress while the guard was armed. run.invalidated_by says which.

Integration

Build one, sign it, hand it to somebody

terminalsigning needs the cryptography package; verifying needs nothing

$ python -m pit_harness.certify keygen \
    --private ~/.pit/signing.pem --public pit-certificates.pub --key-id pit-cert-2026a
$ python -m pit_harness.certify build --receipt receipt.json --audit findings.json \
    --out certificate.json
$ PIT_SIGNING_KEY=~/.pit/signing.pem python -m pit_harness.certify sign \
    --cert certificate.json --pubkey pit-certificates.pub

The private key is read from the path in PIT_SIGNING_KEY, and there is no default location to fall back to. Signing refuses a key file that group or other can read, and refuses an encrypted PEM rather than prompting. Verifying in Go needs nothing but the toolchain; in Python it uses the same library when installed and reports the signature check as unchecked when it is not, so a report never says a signature passed when nothing checked it.

What a signature cannot establish

A signature shows the file came from the key it names and that its body has not changed, and the hashes show you hold the same trajectory and the same corpus release the run was scored on. Those are claims about bytes, and whether the run was a fair test is a different question. The report repeats these three limits on every run:

  • The agent may have reasoned from memory. That is what arm_deltas, positive_control and reasoning_audit measure. If the run leaked, you read it there rather than in the signature check.
  • Whether the corpus release is a good test of the task is not scored anywhere in the certificate.
  • Whether the key you verified against belongs to PIT depends on where you got the key file. certverify checks the signature; it cannot check where a file came from.

Related

What sits either side of it

Upstream

Evaluation Harness

The run receipt a certificate carries whole.

Upstream

Reasoning Audits

The claim-level findings that fill the reasoning-audit slot.

Downstream

Certified Leaderboard

Where a certificate goes when you want it counted beside other people's.