The .cgfevidence bundle format

A .cgfevidence file is a single, self-contained, signed compliance artifact. It is a zstd-compressed tar archive with a fixed internal layout and Ed25519 + RFC 3161 cryptography. Any CGF reader can verify it offline, with nothing but the bundle itself and a trust store.

On-disk layout

dossier.cgfevidence            ← tar + zstd
├── manifest.json              ← format / version / hashes
├── graph.json                 ← deterministic, content-addressed graph
├── claims.json                ← claim-pack results, structured
├── narrative.md               ← rendered from graph + claims
├── signatures/
│   ├── ed25519-ops@acme.sig   ← detached signature over signed-payload.bin
│   └── ed25519-cto@acme.sig
├── signed-payload.bin         ← canonical bytes the signatures cover
└── timestamp/
    └── freetsa.tsr            ← RFC 3161 TimeStampToken (optional)

Everything except signatures/ and timestamp/ is hashed into manifest.rootSha256. The signatures cover signed-payload.bin, which is the canonical (sorted-key, LF-terminated) JSON encoding of manifest.json. That gives you a single hash to sign and a single hash to verify.

manifest.json

{
  "format": "cgf",
  "version": "1.0",
  "rootSha256": "9f3a…d27e",
  "createdAt": "2026-04-12T09:14:22Z",
  "ingestor": {
    "name": "@daitai/cgf",
    "version": "1.0.0-rc.1"
  },
  "files": [
    { "path": "graph.json",    "sha256": "1e88…5a02", "bytes": 184211 },
    { "path": "claims.json",   "sha256": "73c0…b119", "bytes":  41098 },
    { "path": "narrative.md",  "sha256": "ab4f…0c7d", "bytes":  22440 }
  ],
  "claimPacks": [
    { "id": "eu-ai-act-high-risk", "version": "1.0.0", "result": "fail" },
    { "id": "gdpr-art-30",         "version": "1.0.0", "result": "pass" },
    { "id": "soc2-cc",             "version": "1.0.0", "result": "pass" }
  ]
}

rootSha256 is sha256(canonicalize({ files, claimPacks, ingestor, createdAt })). If any byte in any file changes, the rootSha256 changes, and every signature fails.

Signature schema

Signatures are Ed25519 over signed-payload.bin. The filename is informational — the binding is the embedded keyId.

// signatures/ed25519-ops@acme.sig.json
{
  "alg":   "Ed25519",
  "keyId": "ops@acme",
  "pub":   "9c4d…ef10",          // 32-byte raw public key, hex
  "sig":   "2c7b…f441",          // 64-byte signature, hex
  "over":  "signed-payload.bin",
  "ts":    "2026-04-12T09:14:22Z"
}

Multi-signer bundles simply contain multiple *.sig.json files. A trust policy decides how many are required and whether each must be in the trust store.

Timestamp (RFC 3161)

If the bundle was built with --tsa <url>, the response from the Time Stamping Authority is stored verbatim as a DER-encoded TimeStampToken:

timestamp/freetsa.tsr           ← raw RFC 3161 TimeStampToken (DER)
timestamp/freetsa.json          ← parsed convenience copy

The parsed copy:

{
  "alg":  "RFC3161",
  "tsa":  "https://freetsa.org/tsr",
  "time": "2026-04-12T09:14:25Z",
  "hash": { "alg": "sha256", "value": "9f3a…d27e" }
}

hash.value must equal manifest.rootSha256. That binds the TSA's "this existed before time T" attestation to exactly this bundle.

Verification algorithm

A conforming reader MUST:

  1. Untar + decompress the bundle into memory.
  2. Recompute sha256 for every entry in manifest.files and compare.
  3. Recompute rootSha256 and compare to manifest.rootSha256.
  4. For each signature, verify Ed25519 with the embedded pub over signed-payload.bin.
  5. If a timestamp/*.tsr exists, parse it, verify the TSA's CMS signature, and check that messageImprint matches rootSha256.
  6. Apply the active TrustPolicy (see Trust store & policy presets) and emit pass or a list of structured violations.

Steps 1–5 are cryptographic and the same on every machine. Step 6 is policy and reflects the verifying organisation's risk appetite.

Example: minimal valid bundle

demo.cgfevidence
├── manifest.json
├── graph.json
├── claims.json
├── narrative.md
├── signed-payload.bin
└── signatures/
    └── ed25519-alice.sig.json

That's it — no TSA, single signer. Verifies as signed, untimestamped, 1/1 signers trusted against any policy that does not require a timestamp.

Reading next