# CROVIA Trust Root

*This file is the public root of trust for the Crovia substrate.
Any signed Crovia artifact (collector seal, AXIOM envelope, verdict)
must verify against the public key declared here. If this file is
absent or tampered with in your local checkout, do not trust the
signed artifacts you are reading.*

---

## Substrate signing key (current)

| Field | Value |
|---|---|
| Algorithm | Ed25519 |
| Key ID | `430895f101d38164` |
| Public key (raw, hex) | `cf742e26f75669dc673cb5c0786a1ae23ae8ca19c347317192ce40c28a7ff25c` |
| Created at | `2026-04-27T16:48:48Z` |
| Hosted at | `/opt/crovia/keys/substrate/{private.pem,public.pem}` (server) |
| Status | active |

The same public key in PEM form lives at
`/opt/crovia/keys/substrate/public.pem` on the production server. Both
encodings yield the same Ed25519 verification key — the hex form is
the canonical reference for verifiers who do not want to handle PEM.

## Key ceremony record

- The keypair was generated by `collectors/seal.py genkey` on
  2026-04-27 at the timestamp recorded above.
- Generation source of randomness: the system CSPRNG via the
  `cryptography` library's `Ed25519PrivateKey.generate()`.
- The private key never leaves `/opt/crovia/keys/substrate/private.pem`
  (mode `0600`, owner `root`).
- This file (`TRUST_ROOT.md`) is the public commitment of the public
  key. Its first canonical commit to the Crovia repository is the
  ceremony witness: any substrate seal whose `sealed_at` precedes
  that commit is considered pre-trust-root and labelled as such.

## What the key signs

The substrate key signs the **canonical bytes of seal payloads**
produced by `collectors/substrate_seal.py`. A seal payload covers a
Merkle root computed over an append-only JSONL of collector records.
This means a single Ed25519 signature attests:

1. The exact set of records that existed in the JSONL at the
   sealing moment (via the Merkle root).
2. The first and last `receipt_hash` of that set (forensic anchors).
3. The `sealed_at` UTC timestamp recorded at signing time.
4. The `collector_run_id` of the producing run (when applicable).

Subsequent seals on the same JSONL form an append-only seal chain.
Any retroactive edit of records breaks the next seal in the chain.

## How to verify any signed Crovia artifact

```bash
# 1. Fetch the JSONL and its seal companion (both public).
curl -O https://croviatrust.com/substrate/wayback/observations.jsonl
curl -O https://croviatrust.com/substrate/wayback/observations.jsonl.seal.jsonl

# 2. Use the seal CLI shipped in collectors/substrate_seal.py.
python3 -m collectors.substrate_seal verify \
    --jsonl observations.jsonl \
    --seal  observations.jsonl.seal.jsonl
```

The verifier:

1. Reads the JSONL line-by-line, parses each line as JSON, computes
   `sha256(LEAF_PREFIX || canonical_json(record))` for each.
2. Builds a Merkle tree (RFC-style odd-leaf duplication, sha256,
   `LEAF_PREFIX=0x00`, `NODE_PREFIX=0x01`).
3. Compares the recomputed root with `seal.merkle_root`.
4. Verifies the Ed25519 signature against the public key declared
   here, over the canonical bytes of the seal payload (excluding
   the `signature` and `sig_algorithm` fields).

If all four checks pass, the seal is valid and the JSONL has not
been modified since sealing.

## Bitcoin timestamp anchor (OpenTimestamps)

Since 2026-04-27, every substrate seal Merkle root is anchored into
the Bitcoin blockchain via [OpenTimestamps](https://opentimestamps.org)
calendar servers. The anchor is free, public, decentralized, and
verifiable offline by anyone with Bitcoin block headers.

| Field | Value |
|---|---|
| Anchor type | OpenTimestamps over Bitcoin |
| Calendar servers | `btc.calendar.catallaxy.com`, `bob.btc.calendar.opentimestamps.org`, `alice.btc.calendar.opentimestamps.org`, `finney.calendar.eternitywall.com` |
| Stamp cadence | Weekly (Sunday 03:00 UTC) on the latest ledger seal |
| Upgrade cadence | Daily (04:00 UTC) — pending → Bitcoin block proof once confirmed (~1-6 h after stamp) |
| Public manifest | `https://croviatrust.com/registry/data/substrate/ots_anchors.json` |
| `.ots` proofs | `https://croviatrust.com/registry/data/substrate/anchors/<merkle_root>.ots` |

### How to verify the Bitcoin anchor

```bash
# 1. Fetch the public manifest to see all anchored seal roots.
curl -sS https://croviatrust.com/registry/data/substrate/ots_anchors.json

# 2. Pick a Merkle root and fetch its .ots proof.
MR=<merkle_root_hex>
curl -O https://croviatrust.com/registry/data/substrate/anchors/${MR}.ots

# 3. Reconstruct the digest file (the 32 raw bytes of the Merkle root)
#    and verify it.
python3 -c "import sys; open('digest.bin','wb').write(bytes.fromhex('$MR'))"
ots verify digest.bin   # uses ots-client; reads digest.bin.ots
```

If the `.ots` proof has already been upgraded to a Bitcoin block
attestation, `ots verify` confirms the timestamp against your local
Bitcoin block headers (or a trusted block explorer). If the proof is
still pending calendar attestation, `ots upgrade digest.bin` will
poll the calendars until Bitcoin confirms the commitment.

The substrate signing key signs the seal payload; OpenTimestamps
provides an independent timestamp witness rooted in Bitcoin's
proof-of-work consensus. Together they answer two distinct
questions:

- *Who said it?* — answered by the Ed25519 signature against the key
  declared above.
- *When did they say it, demonstrably?* — answered by the
  OpenTimestamps proof against Bitcoin.

Neither answer relies on Crovia's own infrastructure remaining online.

## Key rotation policy

- Active keys are listed in the table above.
- A retired key is moved into a "Retired keys" section below with
  its retirement timestamp and reason. Retired keys remain valid
  for verifying historical seals; they MUST NOT be used to produce
  new seals.
- Rotation requires (a) generating a new keypair via
  `collectors/substrate_seal.py genkey`, (b) recording the new key in this
  file, (c) committing the file, (d) rotating the production
  symlink to point at the new private key.
- The Crovia repository commit history of THIS file is the official
  public timeline of substrate trust.

## Retired keys

*(none)*

## The Crovia seal family

The substrate signing key declared above is the trust root for ALL
artifacts in the **Crovia seal family**. The family is one
cryptographic primitive set (Ed25519 + canonical JSON + Merkle +
chained issuance) applied to three complementary subjects:

| `seal_kind` | Subject | Producer | Status |
|---|---|---|---|
| `ai_output` | a single AI output (text, code, image, audio) | `crovia-seal/` v0.5 (open standard, IETF draft target) | spec frozen, pre-deployment |
| `release_artifact` | a model release manifest (training data hash, weights hash, license bundle) | `crovia-seal/` v1.0 (planned vendor-side release ceremony) | planned |
| `substrate_batch` | an append-only JSONL of collector observations | `collectors/substrate_seal.py` | live since 2026-04-27 |

They are siblings, not duplicates. A future unification session
(`crovia_seal_core`) will extract the shared crypto core into a
single library, but the seal payloads of all three already use
compatible canonical JSON, the same Merkle conventions
(`LEAF_PREFIX 0x00`, `NODE_PREFIX 0x01`, sha256), and the same
Ed25519 verification key listed in this document.

When Crovia eventually publishes a Seal product release ceremony or
an AI-output proxy, those signatures will verify against the same
public key declared above. Trust root is one. Subjects are many.

---

*Tamper-evidence is only as strong as the public commitment of the
verification key. Treat changes to this file with the seriousness
of a constitutional amendment.*
