# Receipt chain: format and verification rule

This is everything needed to write your own verifier. The two verifiers in this bundle were written from it.

## 1. A receipt

The chain is a text file with one JSON object per line, in order. Each object has exactly six fields:

| Field | Type | Meaning |
|---|---|---|
| `sequence` | integer | Position in the chain, starting at 0 |
| `timestamp_utc` | string | When the operation was recorded (ISO 8601, UTC) |
| `operation` | string | `inject`, `edit` or `forget` |
| `previous_hash` | string or null | `receipt_hash` of the receipt before; null for the first |
| `payload` | object | What the operation did (section 3) |
| `receipt_hash` | string | 64 lowercase hexadecimal characters |

## 2. The hash

`receipt_hash` is the SHA-256 of the receipt's first five fields, serialised as JSON with:

- object keys sorted by Unicode code point, at every level;
- no whitespace: `,` between items and `:` between key and value;
- strings escaped as in JSON, with every character outside ASCII written as `\uXXXX` (lowercase hex;
  characters above U+FFFF as a surrogate pair);
- integers in plain decimal. Payloads contain no fractional numbers;
- the result encoded as UTF-8.

In Python this is `json.dumps(body, sort_keys=True, separators=(",", ":"))`.

## 3. Payloads

| Operation | Fields |
|---|---|
| `inject` | `fact_id`, `prompt`, `subject`, `relation`, `answer`, `target_id`, `slot` |
| `edit` | `fact_id`, `previous_answer`, `answer`, `target_id` |
| `forget` | `fact_id`, `answer`, `reverted_to_base` |

`target_id` is the model's token number for the answer. `slot` is the memory position used.

## 4. The verification rule

A chain is **link-valid** if, for every receipt at position *i*:

1. `sequence` equals *i*;
2. `previous_hash` equals the `receipt_hash` of receipt *i - 1* (null for *i* = 0);
3. `receipt_hash` equals the hash of section 2.

A chain is **verified** if it is link-valid **and** the `receipt_hash` of its last receipt (the head) equals
the head you were given through a separate channel.

Rule 3 alone is not enough. Anyone who holds the file can change a receipt and recompute every hash after
it, or cut receipts off the end; the result is still link-valid. Only the head, held by someone else,
detects that. Files 5 and 6 in `tampered/` show both cases.
