Specification · Draft v0.1

The AAES evidence export format

Every claim AAES makes about offline-verifiable evidence reduces to a file format and a verifier. This page is the format, written down: versioned, dated, and precise enough to cite in an RFP or review against a framework. AAES is pre-launch; the format itself is implemented in the offline verifier and exercised by the public sample pack, and production deployments are still ahead of us. Section 9 lists the encoding details this draft does not yet pin down; the reference verifier and the sample pack are the executable definition until it does.

Status: draft v0.1 for public review · Last reviewed:

Status and change policy

Status: draft v0.1. The schema identifier aaes.export/v1 is implemented by the offline verifier and by the demonstration tooling that produced the public sample pack. No client deployment exists yet, so no operational export has been produced outside evaluation tooling. Draft v0.1 identifies this document revision; it is distinct from the wire-format identifiers aaes.export/v1, aaes.records/v1, and aaes.evidence-pack/1, and from verifier software release numbers.

Change policy: aaes.export/v1 has a closed set of line types and fields. This draft does not promise that existing readers will accept new fields or new line types. Changes to the accepted export structure, field semantics, canonicalization, or hashing require a new export schema identifier. Separate pack artifacts, such as the aaes.records/v1 sidecar, carry their own identifiers and do not change the export format. Editorial corrections are recorded in section 9.

Feedback: review comments from auditors and design partners are the v0.2 input. Reach us via the contact page.

1. The export file

An export is a UTF-8 JSON Lines file, one JSON object per line, maximum 4 MiB per line. Every line carries a type discriminator with one of three values: header, entry, or anchor. Exactly one header exists and it is the first line. A reader must reject a file with a missing, duplicate, or late header, a missing discriminator, or an unknown line type.

Header fieldTypeMeaning
typestringRequired; always header.
schemastringAlways aaes.export/v1. Any other value fails with an unsupported-schema error.
log_idstringIdentity of the log this export covers. Distinct from the log_id inside the signed head (section 4): the two have different naming conventions and need not be textually equal.
tenant_idstringThe tenant this export covers.
exported_atRFC 3339 timestampWhen the export was written.
entry_countintegerNumber of entry lines in the file.
headobjectThe signed tree head (section 4) at export time.
public_keystringThe Ed25519 public key for head verification. A verifier still requires the key to be supplied out of band; when both are present, a mismatch fails closed.
signing_key_idstringIdentifies the key that signed the head (for example ed25519-2d7f4034617bfe2e). An identifier does not establish trust.
signature_algorithmstringSignature algorithm for the head (currently ed25519).
pre_anchorboolean, optionalHonesty flag set when the export was written before the first anchor interval. The format supports anchors, so an anchorless export passes only when this flag is present and true; otherwise it fails as incomplete evidence rather than passing as a clean history.

2. Entries, the preimage, and the leaf

Each entry is one journaled decision, with 15 required fields including type, plus an optional tombstone. The preimage contains tenant_id, sequence, record_hash, intent_id, actor_id, capability, tier, allowed, grant_id, amount_usd, occurred_at, and linked_at, plus tombstone when present. It excludes type, chain_hash, and leaf. The chain hash and leaf are the exporter's claims, which a verifier recomputes and cross-checks rather than trusts.

Entry fieldTypeMeaning
typestringRequired; always entry; excluded from the preimage.
tenant_idstringTenant the decision belongs to. Required on every entry.
sequenceintegerPosition in the log. Strictly increasing; gaps are legal only when covered by a tombstone (below).
record_hashstring (hex)Hash of the sealed journal record this entry projects.
intent_idstringIdentifier of the evaluated request.
actor_idstringThe registered agent or human identity that acted.
capabilitystringCapability identifier acted upon.
tierintegerRisk tier assigned to the capability.
allowedbooleanThe authorization decision: permit or refusal. Refusals are first-class entries.
grant_idstringIdentifier of the grant where one was issued; empty when no grant was issued. A grant is authorization material, not proof of execution.
amount_usdnumberEstimated cost committed at the admission decision. This is estimated-cost admission accounting, not a settled charge.
occurred_atRFC 3339 timestampWhen the decision occurred.
linked_atRFC 3339 timestampWhen the entry was linked into the chain.
tombstoneobject, optionalRecords a retention removal. Required tombstone fields: from_sequence, to_sequence, reason, authorised_by, and removed_at; optional: policy_id, record_count, and receipt_count. The tombstone is part of the preimage, so the head signature covers it; the gap it names is the only thing that makes a missing sequence range legal. authorised_by is an attribution field; a recorded value is not proof the deletion was authorized.
chain_hashstring (hex)Claimed chain value (section 3).
leafstring (hex)Claimed leaf value: SHA-256, hex-encoded, of the canonical preimage.

Canonicalization. The preimage is serialized as canonical JSON: object keys sorted, integers emitted exactly, and every JSON number spelling normalized with exact decimal mantissa and exponent arithmetic, so 9007199254740993, 9007199254740993.0, and 9007199254740993e0 are byte-identical while values outside the IEEE-754 53-bit range stay distinct. Non-finite floats are rejected. Canonicalization exists to give two independent encoders the same bytes for the same value; it is the property a second implementation must reproduce first. This draft does not yet pin string escaping, Unicode handling, duplicate-key handling, negative zero, or decimal output syntax to the byte level; those belong to the known-gaps list in section 9.

3. The hash chain

Entries link genesis to head in sequence order. The chain value of an entry is the SHA-256 hex of the concatenation of: the previous entry's chain hash as hexadecimal text (or the ASCII domain separator aaes/genesis for the first entry), one zero byte (0x00), and the entry's canonical preimage bytes. A verifier walks the full export, recomputing each chain value and leaf, and reports the first disagreement with the entry position rather than one opaque failure.

This verifier checks complete exports: the entries must cover the log for the tenant, in sequence order, because a partial window cannot match the head root. That is a deliberate choice for this verification path, not a claim that verifiable subsets are impossible. Verification checks the exported entries against the supplied signed head; a matching root does not establish that the head is the latest head, that no conflicting history exists, or that every relevant decision was captured.

4. The Merkle root and the signed tree head

The export contains entry leaves and signed tree heads. A verifier recomputes the Merkle root over the exported leaves and compares it with the root in the signed head, and checks that the head's tree_size and index agree with the exported entries. This draft does not specify a standalone inclusion-proof or consistency-proof wire format; receipt-level proofs are a separate artifact and a separate future section. The Merkle construction (parent input encoding, empty-tree and single-leaf behavior) needs a byte-level definition and test vectors before another implementation can reproduce it reliably; that is on the known-gaps list in section 9.

Tree head fieldTypeMeaning
log_idstringDerived from the tenant id by a domain-separated SHA-256 derivation (versioned prefix aaes/log-id/1/) shared by producer and verifier. Inside the signed payload, so removing it is a visible signature break; heads predating the field fail closed as legacy material.
indexintegerSequence number of the last entry the head covers.
root_hashstring (hex)Merkle root over the leaves.
tree_sizeintegerNumber of leaves the head covers.
signed_atRFC 3339 timestampWhen the head was signed.
signaturestring (base64)Required Ed25519 signature (RFC 8032) over the canonical JSON of the head with the signature field omitted.

5. Anchors, timestamps, and witnesses

An anchor contains the required fields type (always anchor) and head, plus optional key_id, algorithm, timestamp, and witnesses. Current exports always record key_id and algorithm; exports written before key custody existed may omit them, and the reader then falls back to the key supplied out of band. A head signed by a rotated key fails closed until that key is supplied. Key identifiers do not establish trust, and this draft does not yet specify how an export containing heads signed by multiple keys is verified with the published CLI.

The optional timestamp object contains authority, digest, and time, and may also contain token, noop, and verified. It is designed for an RFC 3161 timestamp authority. A timestamp object is not, by itself, proof of third-party attestation: a noop timestamp is a placeholder, and the exported verified field is metadata, not a substitute for verification under reviewer-selected trust roots. Optional witnesses carry countersignatures (required witness_id, signature, and signed_at; optional public_key and an informational key_holder custody declaration). An embedded public key or key_holder label does not establish independence; witness verification requires a reviewer-pinned identity and key. This draft does not yet fully specify the timestamp digest input, token validation policy, witness signed payload, or witness-trust file format.

The security claim is deliberately narrow. A valid head signature establishes that the holder of the supplied key signed the head; if that is AAES alone, AAES could still substitute a different, internally consistent history. A trusted timestamp can provide evidence about when a commitment existed, and a pinned witness can attest to a commitment under its witness policy. Neither, separately or together, proves capture completeness or excludes conflicting histories; detecting equivocation additionally requires comparison of commitments and a defined consistency and witness policy. No external timestamp authority or independent witness is wired in the default deployment. The verifier reports how many of each were present instead of implying more than the file proves, and --require-independent is a policy gate, not proof of the broader properties: it fails the export unless at least one independent witness or at least one independent timestamp verifies against the trust material the caller supplied.

6. The evidence pack (aaes.evidence-pack/1)

The examiner deliverable is a directory, not a single file. Its manifest (aaes.evidence-pack/1) names the tenant, the export and records schema versions, the population statement, an explicit list of exclusions from the pack's evidentiary scope, and the file inventory, with the verifier binary and its SHA-256 recorded together when one is bundled. An unsigned manifest and a checksum stored beside a binary do not independently authenticate that binary; the pack inventory should arrive through an independently authenticated delivery channel, or the manifest should be separately authenticated.

SCOPE.md states the examination population and exclusions in plain language: journaled decisions for brokered and enforced tool calls in the supplied period, with evals, model behavior, certification, unseen calls, and pass-through observation listed as outside the population. A period-specific examination population must be distinguished from the history covered by the export's signed head.

The sealed journal records an examiner needs to match an export row to stored bytes (policy bundle hash, engine version, capability version, model identity) ship as a sidecar JSONL file, schema aaes.records/v1, that never mixes into the export. The sidecar is a separate artifact with its own identifier: export-only verification does not establish that sidecar records match their corresponding record_hash values; that is a separate sidecar-to-record_hash binding check, performed by tooling that understands the sidecar.

Every pack also prints what it does not prove: that every in-scope effect went through AAES, that agents are certified or "compliant", that the model behaved correctly, that observational receipts were enforced, or independent attestation without a pinned witness or timestamp authority.

7. Verifying

The reference verifier is aaesverify, a standalone verifier binary an examiner runs without the operator CLI. The verification package it builds on may import only the Go standard library plus two small internal packages (hashing and shared types), nothing from the producer; the restriction is enforced by tests that walk the package source and its transitive closure. A verifier that shares code with the producer shares its bugs and its incentives. The same argument applies to the shared hashing and type code the restriction permits, so the restriction reduces common-mode defects but does not make the verifier a fully independent implementation; a second, independently written reader is the stronger check, and this specification exists to make one possible.

go build -o aaesverify ./cmd/aaesverify
./aaesverify --export export.jsonl --pubkey key.pub

Optional flags: --tsa-roots <roots.pem>, --witness-trust <witnesses.json>, --require-independent, and --json. Verification is offline, but trust material must be supplied separately: the signing public key and, when applicable, TSA roots and pinned witness keys. Embedded keys do not establish their own trust.

Verification establishes: every entry's canonical bytes hash to the leaf the export claims; the chain links genesis to head in sequence order; the recomputed Merkle root over the exported leaves equals the root in the signed tree head; the head signature verifies under the public key supplied out of band; and every anchor commits to a prefix of the same tree with a valid signature. It does not establish capture completeness, truth of recorded inputs, correctness of the recorded decision, downstream execution, or independence from AAES without a pinned witness or timestamp authority. Each class of failure is reported separately so a reviewer sees exactly which check failed.

The public sample pack provides a synthetic passing export (16 entry lines) and a tamper demonstration that must produce a verification failure; diagnostic expectations must match the tested verifier version. These are demonstration vectors, not a complete conformance suite. The verifier is open source at github.com/aaes-ai/aaesverify under Apache-2.0; release v0.1.0 implements this draft.

8. Relationship to IETF SCITT

AAES does not claim IETF SCITT conformance. This format does not specify SCITT-compatible COSE signed statements or transparency-service receipts, and an AAES signed tree head, timestamp, or witness signature is not a SCITT artifact merely because it provides a related function. A future integration would need an explicit mapping of statement encoding, registration, receipt format, verifiable data structure, and trust policy to a cited SCITT specification version. AAES also does not claim RFC 6962 conformance; the Merkle construction follows the transparency-log tradition with a deliberately simpler construction rather than a conformant one.

9. Conformance, known gaps, and changelog

This draft does not yet define a complete reader-conformance profile. Structural validation, file-level validation, cryptographic verification, and attestation-policy checks are separate operations, and passing the published line schema is not a verification result. A future conformance profile must specify header placement, accepted fields and line types, count and tenant consistency, sequence and tombstone handling, leaf and chain recomputation, Merkle-root reconstruction, head-signature verification, anchor-prefix verification, and attestation-policy results.

Machine-readable JSON Schemas for the export lines and the evidence-pack manifest are published at /spec/v1/export.schema.json and /spec/v1/evidence-pack-manifest.schema.json. The schemas describe JSON structure. They do not enforce cross-line relationships or cryptographic validity; date-time syntax, base64 decoding, key and signature lengths, and supported algorithm values require explicitly configured validation or verifier checks.

Known gaps, to be pinned in v0.2: the full tenant-to-log_id derivation and its input encoding; canonicalization edge cases (string escaping, Unicode handling, duplicate keys, negative zero, decimal output syntax); whether the previous chain value enters the concatenation as hexadecimal text (current prose) with vectors to prove it; Merkle parent input encoding, empty-tree and single-leaf behavior; tombstone gap reconstruction and its effects on chain, root, index, and tree size; the timestamp digest input, token validation policy, witness signed payload, and witness-trust file format; the valid tier value set; multi-key verification through the published CLI; and the receipt proof format.

Sources

Reviewing AAES for a client or a procurement? The verifier, the sample pack, and this specification are the materials available for technical review.

Talk to us