Reference · report format
The kolm signed report format
Every kolm evidence report is one JSON envelope, signed with Ed25519 over a canonical serialization of its fields. This page is the specification of that envelope: the exact field inventory the signature covers, the canonicalization rules, the detached counter-signals, and how anyone verifies a report with no kolm server in the path. It is written so that an independent implementation can be built from this page and the published verifiers alone.
Last reviewed 10 June 2026 · current shape: kolm-audit-report-1 / asr-report/0.1
Designed to outlive its issuer.
A piece of evidence is only durable if it can be checked without the issuer's help. So the format is built around one rule: verification must never require kolm to be reachable, or in business. The public key travels inside the report, the verifiers are published and inspectable, and the canonicalization is simple enough to reimplement from this page.
Open
Apache-2.0, published, reimplementable
The reference verifiers ship open source under Apache-2.0: the repository governs the whole tree, including the Python and Go SDKs. kolm publishes this spec and welcomes independent implementations; nothing in the format depends on kolm-only knowledge.
Offline
No server in the verification path
Tier-1 verification needs only the report itself: the envelope embeds the public key and the verifier rebuilds the signed bytes from the envelope's own fields. It runs in a browser, on an isolated box, or in CI, with no account and no network call.
Governed in the open
Versioned, logged, never silent
Every change to the envelope shape is versioned and recorded in the changelog at the bottom of this page. Additive fields are minor revisions; a breaking change would bump the major version and the schema string itself, so old verifiers fail loudly, never wrongly.
One mechanism, two artifacts. The same Ed25519 signature over canonical JSON that seals this signed report also seals the portable .kolm model the compiler builds from your live API calls. A model carries its recipe, evals and receipt; a report carries its findings and evidence digest. Both are bytes you own, both verify offline, and both check through the same path at /verify.
Every signed field, and what it means.
The envelope is a single JSON object. The Ed25519 signature covers every top-level field except the four detached blocks listed after this table. Field names below are the literal keys in the JSON.
| Field | Meaning |
|---|---|
| schema | Fixed type marker, "kolm-audit-report-1". Verifiers reject any other value before checking anything else. |
| report_version | The envelope shape that produced the report. Currently "asr-report/0.1". |
| spec_version | The audit spec the underlying run followed. Currently "asr-audit/0.1"; null on inputs that predate it. |
| report_id | Stable identifier with an asrr_ prefix, time-prefixed so ids sort roughly by issue time. |
| generated_at | ISO 8601 time the audit ran. The signature block's signed_at must equal it; verifiers fail a mismatch as a post-signing timestamp edit. |
| tier | "scan" (the free Scan) or "report" (the paid Signed Readiness Report). Inside the signed bytes. |
| watermark | Boolean, true on the free Scan. Signed, so the watermark cannot be stripped without breaking the signature. |
| evidence_tier | Evidence-quality grade { grade: "A"|"B"|"C", method, basis[] }, signed alongside the findings. See section 04. |
| subject | What was assessed: { name, source, records, events }. |
| summary | The graduated readiness rollup: readiness_pct, findings by severity, tamper_evident, assessed controls, not-assessed controls with reasons, blocking count. |
| findings | Framework-mapped findings: severity, pillar, title, detail, ASR control, framework references, opaque evidence ids (never raw log bodies). |
| frameworks | Per-framework coverage rollup: controls touched, finding counts, worst severity. |
| remediation | Prioritized fix roadmap derived from the findings, worst first, each item carrying its framework references. |
| caveats | What the report does and does not claim, stated in plain terms. Findings map to frameworks a reviewer cites; the report is not a certification. |
| asr_checklist | The ASR control catalog the run assessed against (id, name, required evidence). |
| evidence_digest | { alg: "sha256", value, event_count }: binds the report to the exact input events analyzed, without carrying their possibly sensitive bodies. |
| passport | The agent identity passport: who acted, on which models and vendor surface, through what delegation graph, over which retrieval sources. |
| red_team | The injection-resistance battery: score, per-status counts, and the probe table with opaque evidence ids. |
| contact | The single contact surface: dev@kolm.ai. |
| verify_url | Where any holder can verify the report; defaults to https://kolm.ai/verify. |
The four detached fields
Exactly four top-level fields are excluded from the signed bytes: signature_ed25519, timestamp_evidence, log_checkpoint, and co_signatures. The reason is structural, not a loophole: each one attests the envelope, so it cannot sit inside the bytes it attests. A signature cannot cover itself; a timestamp and a transparency-log entry are issued after signing and reference the sha256 of the signed canonical payload; each co-signature is itself an Ed25519 signature over that same payload. Because they bind to the signed digest, none of them can be re-pointed at a different report, and attaching or appending them never disturbs the primary signature.
The exact bytes the signature covers.
kolm canonical JSON is recursive key-sorted JSON with no insignificant whitespace, signed over its UTF-8 bytes. It is deliberately not RFC 8785 (JCS). The difference is simplicity: the entire algorithm is one short recursive function a reviewer can read in a single sitting and reimplement without a dependency, and the awkward edges below are pinned by cross-implementation fixtures rather than by a long standard.
- Key order. Object keys sort by UTF-16 code unit, JavaScript's default sort. For supplementary-plane characters this differs from raw UTF-8 byte order, and every implementation reproduces it.
- Whitespace. None. {"a":1,"b":[2,3]}, never pretty-printed.
- Numbers. ECMAScript number-to-string: shortest round-trip digits, exponent notation only at the JSON.stringify thresholds. Non-finite values serialize to null.
- Strings. ECMAScript JSON quoting (JSON.stringify): the quote, the backslash, C0 control characters, and unpaired surrogates are escaped; every other code point passes through unescaped.
- Dropped values. undefined members are dropped, matching JSON.stringify. null is preserved: it is a value, not an absent key.
- Exclusions. The four detached fields above are removed before serialization. Everything else, present or future, is covered.
Four implementations are locked byte-for-byte: the Node signer (src/attestation-report-builder.js), the browser verifier (kolm-audit-verify.js), the Python SDK, and the Go SDK. Each SDK's test suite asserts byte-exact equality against the reference output, including the number-formatting and key-order edges.
One signature, two trust tiers, three evidence grades.
The signature is Ed25519 (RFC 8032) over the canonical bytes. The block that carries it records spec: "kolm-ed25519-v1", alg, the PEM public_key, its key_fingerprint, the signature, and signed_at. Verification then answers three separate questions, and the format keeps them separate.
Tier 1 · integrity
Is this byte-for-byte what was signed?
Rebuild the canonical payload from the envelope itself, verify the Ed25519 signature with the embedded public key, and confirm signed_at equals the signed generated_at. Fully offline. A single altered byte fails it.
Tier 2 · issuer provenance
Is the embedded key a kolm key?
Tier 1 alone would accept a forgery re-signed with the attacker's own key. Tier 2 checks the key against the published keyring (kolm-issuers.json) or the live GET /v1/audit/issuer-key. Key lifecycle is public: live, rotated (history stays verifiable), or revoked. A revoked key forces trusted:false.
Evidence tiers · A / B / C
How strong was the input evidence?
A separate axis from authenticity, graded inside the signed payload. A: events captured by the kolm gateway at runtime. B: vendor-supplied logs with a verified hash chain. C: vendor logs accepted as provided. A report built from as-provided logs cannot be upgraded to gateway-captured without breaking the signature.
Envelopes issued before tiered evidence carry no evidence_tier field. They verify unchanged, and renderers show them as "Evidence tier: not graded (issued before tiered evidence)". No grade is ever invented.
Independent evidence that the signature is not alone.
Beyond the primary signature, the format carries four counter-signals. Each is optional, each binds to the signed report digest, and none of them can flip a failing verification to passing: they add confidence, never substitute for it.
log_checkpoint
Transparency-log inclusion
At signing time, the sha256 of the signed canonical payload is appended to kolm's public, append-only Ed25519/Merkle transparency log. The checkpoint records origin, tree_size, root_hash, leaf_hash, and seq, and a holder fetches an inclusion proof from GET /v1/transparency-log/proof/<seq> against the published signed tree head. Anchoring is best-effort: a log failure omits the field rather than blocking the report.
timestamp_evidence
RFC 3161 trusted timestamp
An RFC 3161 token over the signed report digest (the message_imprint), proving the report existed no later than the time authority's clock, independent of kolm's. Status "timestamped" carries token_b64 and the TSA URL; status "offline" means this additive evidence is absent. Verifiers report it; the verdict stays with the signature.
co_signatures
Named co-signers (Reviewed Attestation)
A named reviewer attests the same canonical bytes the issuer signed: co_signatures is excluded from the canonical payload, so the bytes are identical whether zero or N co-signatures are attached, and a co-signer can never invalidate the issuer's signature. Each entry records name, role, and a full Ed25519 block, so it verifies offline with no extra schema. The co-signer key is deliberately independent of the issuer key.
key revocation
Public key lifecycle
Every issuer key has a public status: live, rotated, or revoked, served by GET /v1/audit/issuer-key/<fingerprint>/status. Rotated keys remain valid for the reports they signed; a revoked key forces trusted:false with reason issuer_key_revoked across the verify endpoint, the browser verifier, and the SDK keyrings.
Three offline verifiers, one shared byte stream.
Each path below rebuilds the same canonical bytes and checks the same signature. Use whichever fits the environment; they agree by construction and by test.
Browser
kolm.ai/verify
Paste or drop the report JSON at /verify. The check runs locally in your browser's WebCrypto; the page works with the network disconnected. The verifier source is one inspectable file: kolm-audit-verify.js.
Python SDK
kolm.verify_report
from kolm import verify_report
result = verify_report(report)
result.ok # tier1 AND tier2
Zero network. result.tier1_signature and result.tier2_issuer expose the two tiers; act on result.ok, never on tier 1 alone.
Go SDK
kolm.VerifyReport
v, err := kolm.VerifyReport(
reportJSON, kolm.DefaultKeyring())
// act only when v.Trusted()
Standard library only, zero third-party dependencies. v.Trusted() is tier 1 and tier 2 together.
Fetching the artifact: a shareable Trust link serves the bare signed envelope at GET /v1/trust/<slug>?format=json, ready to feed any of the verifiers above. A server-side convenience, POST /v1/audit/report/verify, returns the same two-tier verdict plus the revocation check; prefer the offline paths, which require no trust in the server.
Additive by default, loud when breaking.
Three identifiers version the format. schema is the hard gate: "kolm-audit-report-1" today, and a breaking change would mint a new value so old verifiers reject it explicitly. Within a schema, report_version (currently "asr-report/0.1") and spec_version (currently "asr-audit/0.1") move with semver discipline: additive fields are minor revisions.
Additive fields never break old verifiers, because canonicalization is generic: the signed bytes are rebuilt by key-sorting whatever fields the envelope carries, never from a fixed field list. A verifier written against envelope v1 verifies an envelope carrying fields it has never heard of, byte for byte.
| Date | Version | Change |
|---|---|---|
| 2026-06-10 | asr-report/0.1 | Added evidence_tier (grade A/B/C, method, basis) inside the signed payload. Additive: earlier envelopes verify unchanged and render as "Evidence tier: not graded (issued before tiered evidence)". |
| 2026-06 | asr-report/0.1 | Envelope v1 under schema kolm-audit-report-1: the signed field inventory above, tier and watermark inside the signed bytes, evidence_digest, passport and red_team blocks, and the four detached fields (signature, RFC 3161 timestamp, transparency-log checkpoint, co-signatures). |
Questions about the format, or building an independent implementation? dev@kolm.ai
Read the bytes, not the brochure.
Verify a signed report or a .kolm model in your own browser, then go own the AI you're renting: capture, compile, and carry out an artifact that verifies through this exact format.
Caveats: Scope is contractual. Permission posture, redaction and audit-trail integrity are assessed. Injection is tested and reported, not warranted.