# Digest anchoring — the determination log's independent timestamp

**Since:** migration 222 (written 2026-10-31). **Table:** `digest_anchors`. **Code:** `lib/determination/anchor/ots.ts` (OpenTimestamps, no dependency), `lib/determination/anchor/anchors.ts` (the pass, the statement, the summaries). **Worker:** `digest-anchor`, daily 01:40Z. **Readers:** `GET /api/v1/determinations/anchors[?period=YYYY-MM-DD[&format=ots]]`; the `anchor` field on `GET /api/v1/determinations/log`, on every `/{id}/proof`, and under `sealed` on every reference determination.

## Why

The transparency log (`docs/determination-standard-v1.md` §8b) hashes every day's determination ids into a Merkle root and publishes it. That makes retroactive alteration detectable **to anyone who recorded a root at the time**. It proves nothing about time to anyone who did not, because the log is published by the party it constrains: Calchis could publish one root and later another, and a reader comparing only against Calchis would not notice. §8b says so and names the remedy — an external witness. This is it.

## How it is said

In copy and headings this is an **independent timestamp** or the log's **external witness**. It is never "blockchain": that word promises more than a timestamp and is read by an insurance or public-sector reader as a pitch. Bitcoin is named once, in the technical detail, because a verifier does have to know which ledger's block header to check — and always with the sentence that **no cryptocurrency is held, bought or paid; the public ledger is used only as a clock nobody can edit, and the proof checks with free open-source tools.** API field names (`bitcoin_block`, `block_time`) stay literal: they are for the verifier.

## What is anchored

Not the bare root. A root alone says nothing about which period it closes, so what is anchored is one line that binds them:

```
calchis-determination-digest/1 <period_start> <period_end> <root> <size>
```

with both times as ISO-8601 UTC to the millisecond (JavaScript `toISOString()`), for example:

```
calchis-determination-digest/1 2026-10-30T00:00:00.000Z 2026-10-31T00:00:00.000Z 6e340b9c…fa01d 719
```

The anchored **message** is SHA-256 of that line's UTF-8 bytes. A verifier recomputes the line from the digest the log serves, hashes it, and checks the proof against the hash. Nothing about the binding rests on Calchis.

### What sits under the root

The digest's entries, sorted and de-duplicated, are (worker `determination-log`, `DIGEST_SOURCES`):

| Entry | Source | Since |
|---|---|---|
| `cdt1-…` / `cdt2-…` | customers' trigger determinations (`ils_trigger_events`) and the public catalog's reference determinations | 101 / 219 |
| `cre1-<sha256 of the CSV>` | the revision record's frozen monthly extract | 227 (2026-10-31) |
| `aip1-<content_hash>` | **every body the as-issued archive received that day** (`as_issued_products`): the hash of the federal product as received | 2026-11-06 |

The last line is what turns the archive from a claim into evidence: a reader holding a body hashes it, finds `aip1-<hash>` in a sealed digest, and checks the root's anchor against the public ledger. "Calchis held this product before block N's time" then rests on no word of ours. ~600–800 bodies a day; the proof for any one is a dozen hashes.

## Two witnesses

**OpenTimestamps on Bitcoin.** The message is submitted to the public OpenTimestamps calendars (the reference client's four; at least two must answer). Each calendar aggregates submissions into a Merkle tree and commits the tree's root to Bitcoin in a transaction, roughly every few hours. The returned path is kept as a standard detached `.ots` file. On later runs the worker asks each calendar for the rest of the path (`/timestamp/<commitment>`), merges it in, and — when a Bitcoin attestation appears — fetches that block's header from a public explorer (blockstream.info, then mempool.space) and checks that the path's end equals the block's Merkle root. Only then is the anchor `confirmed`, with the block height, hash and time on the row. Free, no account, no key, and the proof is checkable by anyone for as long as the chain exists.

**A public Git mirror** (optional). With `DIGEST_MIRROR_REPO` (`owner/name`) and `DIGEST_MIRROR_TOKEN` set, each digest is also written as `digests/YYYY-MM-DD.json` — the digest, the statement, the message and the current `.ots` proof — into that repository through the GitHub contents API, and the commit is recorded. This is a repository Calchis holds, so it is **not an independent witness**; it is a human-readable sequence, a second home for every root, and something the Internet Archive can snapshot.

## States

| status | meaning |
|---|---|
| `pending` | Stamped; the calendars hold it. The normal state of the newest digest. |
| `confirmed` | A Bitcoin attestation was found **and matched the block header** fetched independently. Never reverts (trigger-enforced). |
| `failed` | Fewer than two calendars answered. Retried on the next run; the row keeps its attempts and the error. |
| `none` | No anchor row yet (the worker has not reached this digest; newest first, 25 a run). |

A Bitcoin attestation that does **not** match the block header is logged as an error and the row stays `pending` with the reason — a proof that fails its own check is never served as confirmed. An explorer that cannot be reached is a retry, and the two reasons are worded apart.

## Timing

The digest for day D is sealed at 01:20Z on D+1 and stamped at 01:40Z. The calendar commits within hours; the block confirms soon after; the worker's next run (01:40Z on D+2) usually confirms it. So a root is in Bitcoin within about a day of being sealed, and the anchor's time is the block's, not ours.

## Verifying

```sh
# 1. The statement, from the digest the log serves (period, root, size).
curl -s 'https://calchis.com/api/v1/determinations/anchors?period=2026-10-30' | jq -r .statement
# 2. Its hash is the anchored message.
printf '%s' "$STATEMENT" | sha256sum
# 3. The proof file, and the reference client's check against the chain.
curl -sO 'https://calchis.com/api/v1/determinations/anchors?period=2026-10-30&format=ots'
ots verify -d "$MESSAGE" calchis-digest-2026-10-30.ots
```

Or with this repository: `parseDetached` → `bitcoinAttestations` → the attestation's message equals the block's Merkle root in header byte order (explorers print it reversed). The response also names the block height, hash and time once confirmed.

```sql
SELECT period_start, method, status, external_ref, external_time, attempts, left(last_error, 60) AS last_error
FROM digest_anchors ORDER BY period_start DESC, method;
```

## What this proves — and does not

- A confirmed anchor proves the statement — and so the root, and so every determination id sealed under it — **existed before the block's time**. That is evidence of time for a reader who was not watching, which the log alone could not give.
- It proves **nothing about whether the determinations were right**. The evidence for that is each record's own: the verifier recomputes the id from the evidence, and the as-issued archive holds the body it was read from.
- The explorer check is against a public API, not a Bitcoin node of our own. The proof file is the same either way; a reader who trusts no explorer runs `ots verify` against their node.
- The mirror is ours. Treat it as a readable copy, not a witness.
- Digests sealed before 222 was applied are anchored by the first runs after it, newest first, and their anchors carry those later block times — honestly: what the anchor proves is that the root existed before *that* block, not before the day it was sealed.
