# Chronicle Audio Integrity Evidence Contract v2

Status: **specified for #736; no runtime emits v2 evidence yet**

This contract extends [Integrity Evidence v1](integrity-evidence-v1.md) for [Audio Source Record v2](source-record-v2.md). All v1 commitments and bundles retain their original meaning and bytes. A chain may contain both historical v1 entries and v2 audio entries. The chain entry hash, genesis hash, witness leaf, inclusion proof, COSE signature algorithm, and timestamp procedure retain their v1 formulas, except where this document explicitly versions the commitment and signed statement. V2 preserves v1's seven substantive verification checks and adds an eighth check: whether each selected exact audio file matches its committed digest and byte length.

## Exact commitment bytes

Use v1's byte definitions: `DS(label)` is ASCII label plus `0x00`; `LP(s)` is UTF-8 byte length in ASCII decimal, `:`, then UTF-8 bytes; `RS` is `0x1e`. For the fields below, integers render as ASCII base-10 with no sign, spaces, or leading zeros. UUIDs render lowercase hyphenated; timestamps render UTC with exactly three fractional digits and `Z`. Strings are not normalized. A conforming runtime must reject a malformed canonical value rather than repair it during hashing.

```text
audioContentCommitment = SHA256(
  DS("origo/chronicle/content/v2") ||
  LP(id)                              || RS ||
  LP(audio.sha256)                    || RS ||
  LP(audio.byteLength)                || RS ||
  LP(audio.mediaType)                 || RS ||
  LP(audio.encoding.codec)           || RS ||
  LP(audio.encoding.container)       || RS ||
  LP(audio.encoding.sampleRateHz)    || RS ||
  LP(audio.encoding.channels)        || RS ||
  LP(audio.encoding.bitrateMode)     || RS ||
  LP(audio.encoding.targetBitrateBps)|| RS ||
  LP(audio.encoding.encoder.name)    || RS ||
  LP(audio.encoding.encoder.version) || RS ||
  LP(createdAt)                       || RS ||
  LP(captureSource)                   || RS ||
  LP(sourceCapturedAt)                || RS ||
  LP(preservation)                    || RS ||
  LP(fidelity))
```

The digest in `audio.sha256` is ordinary SHA-256 over the plaintext Ogg Opus file, **without** a domain prefix. The v2 commitment has the domain prefix. The source client computes the file digest before encryption. The chain writer computes the commitment and chain hash from the submitted immutable logical fields and does not need the plaintext audio or its decryption key. A matching digest proves only that the exported bytes match the committed digest; it cannot establish who spoke, surrounding capture completeness, transcription fidelity, or truth.

## Mixed chain and receipt

Every v2 chain export entry explicitly states `commitmentVersion: 1` or `2`. Version 1 uses the original seven-field `origo/chronicle/content/v1` encoding, never a retrofit. Version 2 uses the encoding above. In both cases, the chain computes `entryHash = SHA256(DS("origo/chronicle/entry/v1") || prevHash || U64(chainSeq) || contentCommitment)` exactly as v1. `genesisPrevHash(chainId)`, witness leaf and Merkle proof are unchanged. Thus a v2 publication can witness a chain containing both kinds without rebuilding historical positions. A v1 receipt can still verify its historical v1 head; it must never be issued for a chain head containing a v2 source or claim that an audio source was included.

Separate v1 and v2 signed root statements may cover the same complete tree of opaque chain heads at one publication time. Sign and timestamp each statement separately. The root statement does not assign a content format to other chains in that tree: issue a v1 receipt only for a chain head containing solely v1 entries, and a v2 receipt for a chain head containing a v2 source. Neither version may omit active chain heads from its tree to select only chains of one format.

The v2 receipt uses `evidenceFormatVersion: 2` in both bundle and signed root statement, including the COSE payload defined by v1 §3.5.1 with that integer set to `2`. Re-sign and timestamp the v2 statement; never relabel a v1 signature or token. The schema is [`witness-receipt-bundle-v2.schema.json`](../schemas/witness-receipt-bundle-v2.schema.json). A verifier must compare signed payload, readable rendering, signature, accepted external anchors, and RFC 3161 token as in v1. A bundle's `publisherKeys` and a token's certificates are untrusted input; the caller may supply, replace, or decline anchors. No Origo service or hosted page is required to verify.

The per-entry export view carries all chain positions through the witnessed head in order but may elide unrelated content. Each row has `chainSeq`, `id`, `commitmentVersion`, `contentCommitment`, `prevHash`, `entryHash`, and `materialStatus`:

- `included`: selected, live v2 source; `source` is the complete Source Record v2 and its exact file is in the export. Recompute file digest, byte length, and v2 commitment.
- `elided`: unrelated position whose content is intentionally withheld in a per-entry export. Verify its stored commitment and chain links, but make no content claim for this position.
- `erased`: content has been deleted. Verify its commitment and chain position without reconstructing content. Never report its bytes as verified or confuse it with `elided`.

An `included` source must be among the entry's selected source IDs. Every selected live source must be `included`; an erased selected source yields only position/erasure evidence and an **incomplete audio-content verdict**. An unrelated `elided` or `erased` position does not make an otherwise complete selected-source verdict incomplete. No row may claim both erasure and included bytes. The schema is [`integrity-evidence-export-v2.schema.json`](../schemas/integrity-evidence-export-v2.schema.json).

## Verification and claim

The v2 check order is: (1) format, (2) publisher signature and accepted key, (3) timestamp, (4) selected source commitment, (5) selected audio bytes, (6) chain, (7) blinded leaf, and (8) inclusion proof. The audio-byte check is new; the chain, leaf, and inclusion checks move from v1 steps 5–7 to v2 steps 6–8.

First decrypt and authenticate the protected export. Check payload structure, exact source IDs, all selected files, each file's SHA-256 and byte length, every selected v2 commitment, every chain link, the receipt's blinded leaf and inclusion proof, signature against caller-accepted publisher anchors, and timestamp against caller-accepted timestamp anchors. A failed file digest, commitment, chain link, or witness check is **not verified**. Missing selected bytes or unavailable/declined anchors is **incomplete**, with the unperformed step named. No evidence bundle (Pocket's local profile) can support an external-witness verdict; local digest match is a narrower result. The result must separate selected-byte match, chain/witness status, and transcript derivations. The transcript is not authenticated as a verbatim rendering of speech by the audio digest.

Web receipt material must use ADR-0034's authorized one-workspace scoped owner export path. Do not grant broad `SELECT` on `chronicle_chain_heads` or `chronicle_witness_publication_leaves`, or stage assembled receipt material where `genesis_runtime` can read it. The resulting bundle is delivered in the person's protected export. Published roots remain public.
