# Evidence packs

> Download a signed record of how one change was made (task, models, approvals, findings, tests, delivery) and verify it in the browser or offline.

Source: https://aicodeops.dev/docs/evidence-packs

An evidence pack is a signed record of how one change was made in AI CodeOps. It is for auditors, compliance reviews and customers who need to show how AI-written code reached production.

## What a pack contains

For one change (**Develop → Changes**, then a change):

- **The task:** the prompt given to the AI, the model, provider and tier, and the context files.
- **The runs:** the session and every follow-up run, with status, timings, tokens and cost, plus each agent step's provider and model.
- **The result:** the summary, the changed files and a SHA-256 hash of the diff. The code itself is not included; the hash lets you match it against the pull request.
- **Approvals:** each approval request, who could approve, and every vote with its time and note.
- **Policy decisions:** each allow, deny or ask the workspace's policies made during the change.
- **Findings and tests:** review findings with their triage, and test results (passed, failed, skipped, coverage).
- **Delivery:** the pull request, the deployment, and any incident linked to the change.
- **The audit trail:** the audit log entries about any of the above.
- **People:** the email address behind every account ID in the pack.

## Download a pack

Open **Changes**, choose a change, and select **Download evidence pack**. You need audit access in the workspace: owners, admins and security admins have it. Every download is recorded in the workspace audit log.

## Verify a pack in the browser

Open [/evidence/verify](https://aicodeops.dev/evidence/verify) and choose the file. No account is needed. The page tells you whether the signature matches and which key signed it.

## Verify a pack offline

You don't have to trust our verifier. The pack's `signature` is Ed25519 over the canonical JSON of its `payload`: object keys sorted at every level, no whitespace. The platform's public keys are at `/api/evidence?action=keys`. With Node.js 20 or later:

```js
// verify-evidence.mjs <pack.json> <keys.json>
import { readFileSync } from "node:fs"
import { createPublicKey, verify } from "node:crypto"

const canonical = (v) =>
  v === null || typeof v !== "object"
    ? JSON.stringify(v)
    : Array.isArray(v)
      ? `[${v.map(canonical).join(",")}]`
      : `{${Object.keys(v)
          .sort()
          .map((k) => `${JSON.stringify(k)}:${canonical(v[k])}`)
          .join(",")}}`

const pack = JSON.parse(readFileSync(process.argv[2], "utf8"))
const { keys } = JSON.parse(readFileSync(process.argv[3], "utf8"))
const key = keys.find((k) => k.kid === pack.signature.kid)
if (!key) throw new Error(`unknown key ${pack.signature.kid}`)
const ok = verify(
  null,
  Buffer.from(canonical(pack.payload)),
  createPublicKey(key.publicKey),
  Buffer.from(pack.signature.value, "base64url"),
)
console.log(ok ? "VERIFIED" : "NOT VERIFIED: the pack was altered")
```

Save the keys once, then run the script:

```bash
curl -o keys.json "https://console.aicodeops.dev/api/evidence?action=keys"
node verify-evidence.mjs evidence-chg_....json keys.json
```

## Keys and rotation

Keys are never deleted. When a key is retired it stops signing new packs, and the packs it signed still verify. Keep a copy of `keys.json` with your audit records if you want verification that doesn't depend on reaching the platform.

## Next steps

- [Approvals and policies](https://aicodeops.dev/docs/approvals-and-policies)
- [Quality gates](https://aicodeops.dev/docs/quality-gates)
- [API](https://aicodeops.dev/docs/api)

## Frequently asked questions

### Does the pack contain our source code?

No. It lists the changed files and a SHA-256 hash of the diff, so you can match it against the pull request without the code leaving your repository.

### Do old packs still verify after a key is retired?

Yes. Keys are never deleted. A retired key stops signing new packs, and the packs it signed still verify.
