# Chronicle Integrity Evidence Contract v1

Status: **proposed for CHR-INT / ADR-0026**

## Purpose

Chronicle integrity evidence is the minimum durable, product-independent
representation of the claim that a preserved human record **still holds the
position, order and content it held when it was preserved**, expressed so that a
party who trusts neither the runtime nor its operator can check it.

It answers a deliberately narrow question:

> What must a conforming Chronicle runtime produce so that someone holding only
> an export and a receipt — and no access to the runtime — can determine for
> themselves whether the record changed?

This contract does not describe how a runtime stores rows, computes hashes,
schedules publication, or protects keys. Those are runtime and adapter concerns.
[ADR-0026](../../../docs/02-architecture/decisions/0026-chronicle-witness-evidence-format-and-minimum-slice.md)
records the server-runtime decisions; this contract records what any conforming
runtime must mean by them.

## Core rule

> Commit to the logical record, never to its storage. Publish evidence that
> reveals nothing. Make verification a function of files, never of a service.

The third clause is the one that gives the contract its value. If checking the
claim requires calling the runtime, the runtime is inside the trust loop the
evidence exists to leave, and what has been built is a status page.

## Notation

All values are byte strings. `||` is concatenation. `SHA256` is FIPS 180-4.

| Term | Definition |
|---|---|
| `DS(label)` | the UTF-8 bytes of the ASCII `label`, followed by one `0x00` octet |
| `LP(s)` | the ASCII decimal UTF-8 byte length of the Unicode string `s`, then `0x3a` (`:`), then the UTF-8 bytes of `s` |
| `RS` | one `0x1e` octet |
| `U64(n)` | `n` as an 8-octet unsigned big-endian integer |
| `UUIDBYTES(u)` | the 16 octets of UUID `u` in RFC 9562 network byte order |

`DS` exists so a value produced for one purpose can never be valid for another.
`LP` exists so no combination of field values can be re-partitioned into a
different record with the same encoding.

## 1. Content commitment

A conforming runtime computes, once, at the moment a source record becomes
durable:

```text
contentCommitment =
  SHA256( DS("origo/chronicle/content/v1")
          || LP(id)               || RS
          || LP(content)          || RS
          || LP(createdAt)        || RS
          || LP(captureSource)    || RS
          || LP(sourceCapturedAt) || RS
          || LP(preservation)     || RS
          || LP(fidelity) )
```

All seven are canonical **logical** fields of
[Source Record v1](source-record-v1.md), rendered as that contract defines them:

| Field | Rendering |
|---|---|
| `id` | lowercase, hyphenated canonical UUID string |
| `content` | the exact accepted source text, after acceptance, unmodified |
| `createdAt` | UTC RFC 3339, **exactly three** fractional digits, `Z` suffix — `2026-08-19T12:15:30.412Z` |
| `captureSource` | the canonical token, `typed_text` or `voice_transcript` |
| `sourceCapturedAt` | same rendering as `createdAt` |
| `preservation` | the canonical token, `explicit_capture` or `explicit_approval` |
| `fidelity` | the canonical token, `exact_text` or `approved_transcript` |

### 1.1 Why these seven, and not more or fewer

The four provenance fields are committed because they are **not derived, not
re-derivable, and load-bearing**. A commitment over content alone would let a
runtime relabel an approved voice transcript as typed text, or move the moment
of articulation, and the chain would not notice — while those are precisely the
facts any later human-authorship assertion would rest on.

Every one of the seven is **required** by Source Record v1. That is deliberate
and load-bearing in its own right: because no committed field can be absent,
this contract needs no encoding for an absent value, and there is no way for two
different records to produce the same encoding by one omitting what another
supplies.

Deliberately **excluded**:

- `preservedBy`, `author`, `captureActor` — identity. The chain binds an entry
  to a chain, not to a person (§8). Committing an identity reference here would
  make the evidence depend on a custody model this contract does not define, and
  would break when that model changes.
- `custodyRef` — optional, and chain membership is carried by `chainId` (§2.1).
- `schemaVersion` — constant for a given contract version; versioning is carried
  by `evidenceFormatVersion` (§9).
- Everything in Source Record v1's "explicit non-fields" list: embeddings,
  classifications, summaries, inferred provenance, ciphertext and key metadata,
  storage identifiers. Committing any of them would make re-derivation, model
  changes or storage migration appear as tampering.

The domain-separation label is bound to this field set. **A change to the field
set requires a new label and a new `evidenceFormatVersion`**, never a quiet
redefinition of what `origo/chronicle/content/v1` covers.

### 1.2 What committing provenance does not buy

Committing `preservation` and `fidelity` makes a later relabeling detectable. It
does **not** make the original label true. If a runtime records an approved
transcript as `exact_text` at the moment of preservation, the chain faithfully
commits the wrong claim and every subsequent verification agrees with it.

Evidence binds a claim to a moment. It does not audit the claim. §8 holds
unchanged.

### 1.3 Why the commitment, and not the content

The chain (§2) covers this commitment and never the stored representation.
Three consequences follow, and they are the reason the contract is shaped this
way:

- **Storage may change freely.** Encryption, key rotation, compression, physical
  schema migration and export re-serialization cannot alter the commitment,
  because none of them alter the logical record it was taken over.
- **Verification needs no decryption authority.** Anyone holding the logical
  record can recompute the commitment. The runtime is not consulted.
- **Erasure becomes expressible.** Content can be destroyed while the commitment
  and position survive. See §6.

### 1.4 The rendering is the interchange rendering, deliberately

`createdAt` and `sourceCapturedAt` are committed at millisecond precision because that is the precision
of the canonical logical record. A runtime whose physical timestamps are finer
truncates for the commitment and must not commit its physical rendering
instead.

This is not a detail. **The commitment must be recomputable from the export
alone.** A commitment taken over a precision the export does not carry cannot be
checked by the holder of that export, and the evidence is then unverifiable in
exactly the situation it was built for.

## 2. The chain

A **chain** is an ordered sequence of chain entries belonging to one custody
scope — one workspace, one device, one account, as the runtime's custody model
defines. Chronicle does not define custody here; it defines only that a chain
has exactly one scope and that the scope does not change.

### 2.1 Chain identity

At chain genesis the chain is assigned an immutable random 128-bit `chainId`.

```text
genesisPrevHash = SHA256( DS("origo/chronicle/chain/v1") || UUIDBYTES(chainId) )
```

A chain must not begin from a fixed or all-zero value. If every chain began
identically, a segment of one chain would be structurally valid as a segment of
another, and splicing would be undetectable.

### 2.2 Chain entries

`chainSeq` is a positive integer beginning at 1 and increasing by exactly 1.
`prevHash` of the entry at `chainSeq = 1` is `genesisPrevHash`; otherwise it is
the `entryHash` of the entry at `chainSeq - 1`.

```text
entryHash =
  SHA256( DS("origo/chronicle/entry/v1")
          || prevHash || U64(chainSeq) || contentCommitment )
```

`chainSeq` is inside the hash. Linkage alone constrains order but not
numbering; committing the position makes renumbering detectable on its own.

The custody identifier is deliberately **not** inside the hash. Chain membership
is carried by `chainId` through `genesisPrevHash`, which is stronger and does
not depend on a field that a runtime's ownership-attribution path may legitimately
set.

### 2.3 Ordering at genesis

When a chain is established over records that already exist, they are ordered by
`createdAt` ascending, ties broken by the bytewise ordering of the canonical
`id` string.

Ordering by identifier is **prohibited**. Chronicle identifiers are stable, not
chronological; a runtime whose identifiers are random would otherwise commit a
false chronology and then witness it permanently.

Records preserved before chain genesis carry **no positional proof**. A
conforming runtime states the date from which positional proof exists rather
than implying the chain covers everything.

### 2.4 Chain head

The chain head is the entry with the greatest `chainSeq`. A runtime that
witnesses (§3) maintains the head as `(chainId, headEntryHash, headChainSeq)`.

## 3. Witness publication

A **publication** takes the heads of every chain in a publication set and
produces one root, one signed statement, and one inclusion proof per chain.

### 3.1 The tree is not Chronicle's to define

The verifiable data structure is the binary Merkle tree of **RFC 9162 §2.1**, as
registered by **RFC 9942** under the identifier `RFC9162_SHA256`:

```text
leafHash     = SHA256( 0x00 || leafData )
interiorHash = SHA256( 0x01 || left || right )
```

Chronicle does not define, extend or vary this structure. Inclusion proofs
follow RFC 9162 §2.1.3 and consistency proofs §2.1.4.

### 3.2 Leaves are blinded

Each chain holds an immutable random 32-octet `witnessSalt`, delivered to the
record's owner and published nowhere.

```text
leafData = witnessSalt || headEntryHash || U64(headChainSeq)
```

Blinding is required, not optional. A publication set of one produces a root
equal to its only leaf hash, and a small set is brute-forceable against a
guessed head. Without the salt the root discloses exactly what publishing a root
instead of a head was meant to avoid.

### 3.3 Every chain appears in every publication

A chain with no new entries since the last publication contributes a leaf
carrying its unchanged head. A publication must not include only chains that
changed: which leaves move between roots would otherwise disclose who wrote and
when.

Leaves are ordered by ascending bytewise `chainId`, which is random and so
imposes no meaningful ordering on the set.

### 3.4 Residual disclosure, stated

`treeSize` reveals how many chains exist in the publication set. This is a
disclosed property of the format, not a defect to be discovered later. A runtime
that cannot accept it pads the set to a fixed leaf count and says so.

A publication over an empty set is not produced.

### 3.5 The signed root statement

Each publication produces a statement over `(evidenceFormatVersion, vds, root,
treeSize, publishedAt, keyId)`, signed by the publisher, and one inclusion proof
per chain expressed as an RFC 9942 COSE Receipt with `vds = RFC9162_SHA256`.

The statement is carried as a COSE_Sign1 object, RFC 9052 §4.2. Throughout this
contract **the signed root statement** means that tagged COSE_Sign1 envelope --
not the payload alone, and not the readable rendering a bundle carries under §4.

The signed root statement is submitted to at least one independent RFC 3161
Time-Stamp Authority and the returned token is retained. The token's
`messageImprint` is `SHA256` over the envelope octets of §3.5.1, so a token
attests the signature and not merely the root. Two independent authorities are
recommended: public authorities offer no availability guarantee, and the cost of
a second is zero.

#### 3.5.1 Canonical encoding

Verification requires reconstructing, byte for byte, the octets the publisher
signed. A field list does not determine them, so the encoding is stated here at
the precision §1 states the content commitment.

All CBOR is **deterministically encoded** per RFC 8949 §4.2.1: shortest-form
arguments, definite lengths only, and map keys sorted in bytewise lexicographic
order of their encoded form.

```text
payload = CBOR map, six entries, in this order:
  "vds"                   => tstr   the identifier of §3.1
  "root"                  => bstr   the 32 raw octets of the root, not hex
  "keyId"                 => tstr
  "treeSize"              => uint
  "publishedAt"           => tstr   rendered as §1 renders createdAt
  "evidenceFormatVersion" => uint

protectedHeader = CBOR map, two entries, in this order:
  1 => -8                           alg, EdDSA, RFC 9053 §2.2
  4 => bstr( UTF-8 octets of keyId ) kid

toSign = CBOR array, four elements:
  [ "Signature1", bstr(protectedHeader), bstr(zero length), bstr(payload) ]

signature = Ed25519( toSign ), the 64 octets of RFC 8032

signed root statement = CBOR tag 18 over the array:
  [ bstr(protectedHeader), {}, bstr(payload), bstr(signature) ]
```

Four points an independent implementation otherwise gets wrong:

- **The payload key order above is the sorted order, not the reading order.**
  The six keys sort by encoded length first, so the fields do not appear in the
  order the opening sentence of §3.5 names them. RFC 8949 §4.2.1 bytewise
  ordering and the older length-first ordering of §4.2.3 coincide for this fixed
  key set, so either rule reaches the same octets.
- **`root` is raw octets in the payload and lowercase hex in the §4 rendering.**
  It is the only field whose two representations differ.
- **`external_aad` is a zero-length byte string**, encoded `0x40`. It is present
  and empty, never absent.
- **The unprotected header is an empty map**, encoded `0xa0`, and is not covered
  by the signature. Nothing a verifier relies on may be carried there.

Worked example, for an implementation to check itself against. The statement
`evidenceFormatVersion = 1`, `vds = "RFC9162_SHA256"`, `treeSize = 1`,
`publishedAt = "2026-01-02T03:04:05.678Z"`, `keyId = "example-key-1"`, and
`root` the 32 octets `00112233445566778899aabbccddeeff` repeated twice, signed
by the Ed25519 key whose public octets are
`03a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8`, encodes as:

```text
payload
  a6637664736e524643393136325f53484132353664726f6f74582000112233445566
  778899aabbccddeeff00112233445566778899aabbccddeeff656b657949646d6578
  616d706c652d6b65792d31687472656553697a65016b7075626c6973686564417478
  18323032362d30312d30325430333a30343a30352e3637385a7565766964656e6365
  466f726d617456657273696f6e01

protectedHeader
  a20127044d6578616d706c652d6b65792d31

toSign
  846a5369676e61747572653152a20127044d6578616d706c652d6b65792d31405896
  a6637664736e524643393136325f53484132353664726f6f74582000112233445566
  778899aabbccddeeff00112233445566778899aabbccddeeff656b657949646d6578
  616d706c652d6b65792d31687472656553697a65016b7075626c6973686564417478
  18323032362d30312d30325430333a30343a30352e3637385a7565766964656e6365
  466f726d617456657273696f6e01

signature
  49bc8186942a3ff98a88ac6c849b4eed7fc070f5958c5bfc95a20bb553bc7362b8e0
  ed8e6ba331cb293c4a1cd5352391c9beb4e00a1afcd49f8937eebabdf40e
```

#### 3.5.2 The readable rendering is derived, never authoritative

A bundle carries the signed root statement twice: as the envelope of §3.5.1,
carried in `signature` per §4.1, and as the readable `signedRootStatement`
object of §4. Only the envelope is signed.

A verifier takes `root`, `treeSize`, `publishedAt` and `keyId` from the decoded
payload of the envelope it has just verified, never from the readable object.
Where it uses the readable object at all -- for display, or because an
implementation finds it convenient -- it first checks each field equal to the
payload's under the renderings of §3.5.1, and fails step 2 on any disagreement.

Without this rule a bundle can carry an honest signature over one root while the
readable object asserts a different one, and steps 6 and 7 would then check the
record faithfully against the value an attacker chose. A signature is not
evidence about a value nobody compared against it.

### 3.6 Cadence is the exposure window

The interval between publications is the period during which a change is
unwitnessed. It is a disclosed property, stated to the record's owner in those
terms, never implied to be continuous.

## 4. The receipt bundle

The receipt bundle is what makes the owner a witness the runtime cannot
retract. It is delivered on the publication cadence and included in **every**
export.

A conforming bundle carries:

| Element | Required | Purpose |
|---|---:|---|
| `evidenceFormatVersion` | yes | `1` for this contract |
| signed root statement | yes | binds root, tree size and publication time to a key |
| inclusion proof | yes | proves this chain's leaf is in that root |
| `chainId` | yes | reconstructs `genesisPrevHash` |
| `witnessSalt` | yes | reconstructs the leaf |
| `headEntryHash`, `headChainSeq` | yes | states what was witnessed |
| timestamp tokens | yes | independent attestation of time |
| publisher public keys | yes | verifies the signed root statement |
| key-transition statements | when any | verifies roots signed by a retired key |
| plain-language statement | yes | what the bundle proves and does not prove |

The plain-language statement is a required element, not documentation. Evidence
delivered without a stated boundary will be read as proving more than it does,
and §8 exists because that misreading is commercially attractive.

### 4.1 How the cryptographic elements are carried

The table names elements by purpose. A verifier also needs their serialization,
and a bundle that satisfies a schema without it is still not verifiable from
this document alone.

Every octet string a bundle carries is **lowercase hexadecimal**. What those
octets are:

| Element | Carries |
|---|---|
| `signature` | the complete COSE_Sign1 envelope of §3.5.1, tag and all -- not the 64 signature octets alone |
| `timestampTokens` | one DER-encoded RFC 3161 token per authority |
| `publisherKeys` | one entry per key, shaped as below |

A `publisherKeys` entry carries exactly:

| Field | Value |
|---|---|
| `keyId` | the identifier the signed root statement names in its `keyId` and its protected `kid` |
| `algorithm` | the token `Ed25519` |
| `publicKey` | 64 lowercase hex characters: the 32 public octets of RFC 8032 |

Three consequences worth stating, because each is otherwise left to a reader
to assume:

- **A verifier never reconstructs the protected header.** `signature` carries
  it, so the octets §3.5.1 defines are recoverable from the bundle alone, and
  the §3.5.2 comparison is always possible.
- **The protected header's `alg` governs, never `algorithm`.** The entry's
  token is a label for a reader. A verifier finding the two in disagreement --
  an entry marked `Ed25519` against a protected `alg` other than `-8` -- fails
  step 2 rather than choosing between them.
- **A portable fixture may carry `signature: null`; a delivered bundle may
  not.** An absent signature is a step-2 **failure**, never a step that was
  not performed. A verifier reports `incomplete` for what it has not
  implemented, not for what the evidence does not contain.

## 5. Verification

Verification takes exactly two inputs: **the export** and **the bundle**. A
conforming verification procedure makes no network call to the runtime, requires
no account, and completes offline.

Steps, in order, each with the tampering it detects:

| # | Step | Detects |
|---:|---|---|
| 1 | Check `evidenceFormatVersion` is understood | wrong or unsupported format |
| 2 | Verify the signed root statement against a publisher key, following key-transition statements to a key the verifier accepts | a root nobody is accountable for |
| 3 | Verify the timestamp tokens over the signed root statement | a root back-dated after the fact |
| 4 | Recompute `contentCommitment` per §1 for every non-erased entry | altered content |
| 5 | Recompute the chain from `genesisPrevHash(chainId)` per §2, checking linkage and that `chainSeq` runs 1..N with no gap or repeat | reordering, removal from the middle, renumbering, splicing from another chain |
| 6 | Recompute `leafData` and `leafHash` per §3.2 from `witnessSalt` and the head | a head that is not the one witnessed |
| 7 | Verify the inclusion proof against `root` and `treeSize` per RFC 9162 §2.1.3 | a head absent from the witnessed root |
| 8 | Report the result and the witnessed time | — |

Step 2 takes `root`, `treeSize`, `publishedAt` and `keyId` from the signed
payload, per §3.5.2. A verifier that reads them from the bundle's readable
rendering has not performed step 2, whatever it reports.

### 5.1 Reporting

A verifier reports a **single** outcome and the date through which it holds, for
example *verified through 4 August 2026*.

- Any failing step yields **not verified**, naming the step. A verifier must not
  report partial success as success, and must not downgrade a failure to a
  warning.
- A verifier that **cannot perform** a step — because the bundle omits the
  element the step needs, or because the implementation does not yet implement
  it — reports **incomplete**, naming the steps not performed. `verified`
  requires every step to have been performed and passed.

  `incomplete` exists so that an implementation can be honest about its own
  coverage rather than quietly redefining `verified` to mean "the parts I do".
  It is not a passing outcome and must never be presented as one. A conforming
  implementation states plainly which steps it performs.
- The reported date is taken from the timestamp token, not from the runtime's
  own claim of when it published.
- The count of entries covered, and separately the count of erased entries
  (§6), are reported alongside.

### 5.2 The negative path is part of the contract

An implementation that has never been observed to fail is not evidence that it
works. A conforming implementation demonstrates rejection of each case in
`../conformance/integrity-evidence-v1.md` against the published fixtures.

### 5.3 Verifier independence

A verifier that reuses the producing runtime's encoding implementation cannot
detect a defect in that implementation: both sides would be wrong in the same
direction and agree. A conforming verifier is written against this contract, not
against a runtime's source.

## 6. Erasure

Owner-directed erasure destroys `content` and retains `contentCommitment`,
`chainSeq`, `prevHash` and `entryHash`.

The chain therefore continues to verify across an erased entry: step 4 is
skipped for it, step 5 is unaffected. What survives is that *something* was
preserved at that position at that time. What does not survive is any means of
establishing what it was — the commitment is a one-way function of content that
no longer exists, so **nobody, including the runtime operator, can afterwards
prove the erased content**. That is the intended property and must be stated to
the owner rather than discovered by them.

A verifier reports erased entries as erased. It must not report them as verified
content.

**Scope limit.** This defines only what erasure does to integrity evidence. It
does not define Chronicle's deletion lifecycle, which remains open at
`../../../docs/02-architecture/chronicle-core-convergence-matrix.md` and belongs
to a deletion contract. Nothing here authorizes retaining a tombstone in a
runtime whose deletion contract forbids one; a runtime that must leave no trace
of an erased record declines the witness capability for it under §7 rather than
retaining a commitment.

## 7. Witnessing is a declared capability, not a requirement

A conforming Chronicle runtime may implement:

- **no integrity evidence** — valid; it makes no verifiability claim;
- **chain only** (§1–§2) — internally tamper-evident, self-attested;
- **chain and witness** (§1–§3) — externally verifiable.

Declining is conforming. **Claiming without implementing is not.** A runtime
that describes its record as independently verifiable while producing no bundle
a third party can check is non-conforming under this contract regardless of how
its storage behaves.

A runtime must not represent a self-computed digest as external verification.
Holding both the data and the only proof of the data means a party able to alter
one can alter the other, and the two continue to agree.

## 8. What integrity evidence does not claim

A conforming bundle proves that a record held a position, in an order, at or
before a witnessed time, and has not changed since.

It does **not** prove:

- **that a human produced the content.** The evidence attests preservation, not
  authorship. Machine-generated text preserved through a conforming runtime
  produces an indistinguishable bundle.
- **that the content is true, accurate, or original.**
- **who preserved it.** Identity and custody are separate contracts; the chain
  binds an entry to a chain, not to a person.
- **when the thought occurred.** Per Source Record v1 §3, capture time is
  articulation time, not formation time.
- **that alteration is impossible.** A party holding the runtime's
  schema-owner rights can rebuild a chain. Evidence makes that detectable; it
  does not prevent it. Conforming language is **tamper-evident**, never
  tamper-proof.

Human-authorship evidence, if a runtime offers it, is a separate assertion with
its own provenance and authority class. It may use this evidence as its carrier.
It is not delivered by it, and a runtime must not present it as though it were.

## 9. Versioning

`evidenceFormatVersion` changes when any committed byte sequence changes: the
domain-separation labels, the field set or rendering in §1, the chain
construction in §2, the leaf construction in §3.2, the canonical encoding of the
signed root statement in §3.5.1, or the serialization or meaning of a bundle
element in §4.

A bundle already delivered can never be re-issued, because the owner holds it
and that is the point. A format change therefore does not migrate old bundles;
it produces new ones alongside, and a conforming verifier accepts every version
it has ever issued.

Publication cadence, timestamp authority selection, signing key rotation, and
transport are **not** versioned properties.

## 10. Runtime mapping

### Genesis / server Chronicle

| Genesis behavior | Contract mapping | Notes |
|---|---|---|
| `chronicle_entries.id`, `content`, `created_at`, `capture_source`, `source_captured_at` | §1 commitment inputs | timestamps truncated to milliseconds for the commitment, per §1.4 |
| `preservation`, `fidelity` | §1 commitment inputs, **not currently stored** | Genesis derives these from capture type through the Source Record v1 mapping. A commitment input must be a stored fact, not a mapping that can drift: materialize both as columns at insert, and add them to the immutability trigger's protected field list. If they stay derived and the derivation is ever changed, every commitment taken before the change becomes unverifiable. |
| workspace | custody scope of one chain | one chain per workspace |
| `chronicle_chain_heads` | §2.4 head, plus `chainId` and `witnessSalt` | |
| database-boundary trigger | §2 computation | a runtime mechanism; the contract requires only that the application cannot supply chain values |
| daily publication job | §3 publication | cadence is §3.6, disclosed |
| `scripts/chronicle-integrity.sql` | **not** conforming evidence | a set digest over current state; proves neither order nor position, and is self-attested |
| `public.chronicle_entry_content_commitment` | §1 `contentCommitment`, recomputed at export-read time | `contentCommitment` is not a stored column -- it is a local variable inside the #497 database-boundary trigger, never persisted. The export (#508) recomputes it through this owner-owned SQL function, and step 4's independent verifier recomputes it again in JavaScript from the same exported fields. Because both recomputations run over export-time content, this is a genuine SQL-versus-JavaScript cross-implementation check on the §1 encoding -- it will catch a precision or token-rendering divergence between the two implementations. It is **not** a tamper-evidence mechanism on its own: altered content moves both recomputations together, so a tampered export can still produce a `contentCommitment` that matches itself. Tamper detection for a record's content rests entirely on step 5, where `entry_hash` (`chronicle_entries.entry_hash`) is a stored, immutable value the #497 trigger fixed at write time -- recomputing it from an altered `contentCommitment` is what actually fails. A reader of §5's step table would reasonably assume step 4 alone detects content tampering; for Genesis specifically, it does not. |

### Pocket Chronicle

| Pocket behavior | Contract mapping | Notes |
|---|---|---|
| local-only operation, no networked witnessing | §7 capability declaration | declining is conforming |
| immediate deletion with no tombstone | §6 scope limit | a runtime that must leave no trace declines the capability rather than retaining a commitment |
| AES-GCM per-record authenticity | not integrity evidence under this contract | content authenticity against a storage fault, not evidence against an operator |

## Settled during drafting

**Whether the commitment covers source provenance — settled 2026-08-29:
it does.** The draft initially committed `id`, `content` and `createdAt` alone,
following ADR-0017 §2. That would have left an approved voice transcript
relabelable as typed text without the chain noticing, which is the one kind of
alteration the later human-authorship work cannot tolerate. The four provenance
fields were added to §1 and ADR-0026 §2 was amended to match. Cost at the time
of the decision: zero. Cost after v1 ships: a format version and a second
verifier code path forever.

**Millisecond rendering against the existing server digest.** §1.4 commits at
the canonical logical precision, while `apps/genesis/scripts/chronicle-integrity.sql`
renders microseconds. Resolved in favour of the interchange rendering, because a
commitment the export cannot reproduce is unverifiable in exactly the situation
the evidence exists for. ADR-0026 §2 was amended to match. The legacy set digest
is a separate artifact and is superseded by this work.

## Conformance principle

A runtime conforms to integrity evidence v1 when an independent verifier,
written from this document and holding only an export and a bundle, reaches the
correct verdict on every fixture in `../fixtures/` — including every negative
case — regardless of the runtime's database, language, cryptographic library,
or deployment topology.
