Skip to content

Architecture Overview

cardano-keri is structured as four layers. This page covers Layer 1 — the identity plane — and the MPFS value-cage plane it authorizes. The credential layers (2–4) are analyzed in vLEI Bridge and the business cases.

Layer 1 — AID Key-State Registry    (on-chain MPF: trie_key → IdentityLeaf)
Layer 2 — TEL Revocation Registry   (on-chain MPF per issuer: SAID → issued | revoked)
Layer 3 — ACDC Chain Verifier       (Aiken: SAID + signature + MPF proofs, bounded hops)
Layer 4 — Off-chain Proof Builder   (Haskell/WASM: CESR decode + proof generation)

Layer-1 current-authority storage reframed to the sovereign per-AID checkpoint (#92)

The single-UTxO MPF-trie registry (trie_key → IdentityLeaf, identity_root, the sliding-root window) described below is the rejected Candidate-B shared/global shape. Per specs/92-checkpoint-contention/DECISION.md, each AID's current authority is a sovereign, per-AID, quantity-one uniquely-tokenized checkpoint UTxO — asset id (checkpoint_policy_id, aid_asset_name), current keys in the inline CheckpointDatum, delta = 0 rotation (seq + 1). Consumers discover it by a generic (policy_id, asset_name) multi-asset index lookup (any indexer / node / sidecar) and read it as a CIP-31 reference input — no shared-registry inclusion proof against a windowed root, and no bespoke QVI-owned AID → UTxO directory. The mechanical MPF-trie re-cut is downstream #24.

Indexer / discovery trust boundary. The generic (policy_id, asset_name) index lookup supplies only a candidate outref / location for liveness — never identity or current-authority truth. The consuming transaction validates the returned UTxO against the ledger: the exact quantity-one policy + asset; an accepted checkpoint script / version / lineage; a well-formed inline datum with the expected AID / sequence binding and the current weighted key state; and the applicable active / freeze rules (validation rules, not datum fields). A stale or false outref fails ledger validation (it no longer exists, or no longer matches) → refresh / retry; it can never yield forged authority. An indexer outage only blocks transaction construction (liveness) — it never grants false authority.

The freeze registry stays a shared, attacker-contendable UTxO (not sovereign) — the sovereign emergency path must not reintroduce one; a downstream residual.

Two planes with different trust models:

  • The identity plane (Layer 1 + the freeze registry) is permissionless: anyone can incept an identity by posting a deposit and proving key possession. No oracle mediates identity operations.
  • The value plane (MPFS cages) keeps its oracle, but the oracle is necessary-not-sufficient: every leaf mutation requires both the leaf owner's Ed25519 authorization and the oracle signature. The oracle provides liveness; it cannot forge.

Identity Registry (Layer 1)

A single-UTxO MPFS trie mapping:

trie_key → IdentityLeaf { key_state, status }
KeyState {
  cur_pubkey  : ByteArray[32]   -- current Ed25519 public key (raw bytes)
  next_digest : ByteArray[32]   -- blake2b_256(next pubkey), committed not yet revealed
  seq         : Int             -- monotonic rotation counter, starts at 0
  cesr_aid    : ByteArray[32]   -- decoded CESR AID, metadata only (never verified on-chain)
  deposit     : Lovelace        -- locked at inception, returned at close
}

IdentityStatus {
  Active
  FrozenFatal { event_1, sig_1, event_2, sig_2, seq }  -- duplicity proof, irrecoverable
  Closed                                               -- token burnt; deposit returned
}

trie_key = blake2b_256(cbor({cur_pubkey, next_digest})) — derived from inception material, stable across rotations, front-run-proof (see AID Model).

The registry UTxO datum holds a sliding window of recent roots:

RegistryDatum {
  roots : List<ByteArray>  -- [root_t, root_t-1, ..., root_t-k], newest first
}

The sliding window decouples consumers from registry write cadence — a value-write built against an older root remains valid as long as that root is still in the window.

Scope change: list-shaped KeyState

The business-case analyses concluded that KeyState must be list-shaped and threshold-capable from v1 (organizational AIDs are k-of-n multisig; a single key is the 1-of-1 degenerate case). The shape above is the singleton illustration. See the factored core.

Identity operations and their authorization

All identity operations are permissionless — authorized by cryptographic material, not by any operator key:

Operation Authorization
Inception cur_pubkey signature over inc_msg + ADA deposit + absence proof
Rotation blake2b_256(reveal_key) == next_digest and reveal_key signature over rot_msg
Close (burn + refund) cur_pubkey signature over close_msg; deposit returned
Duplicity freeze machine-verifiable DuplicityProof (two conflicting signed KERI events)
Emergency freeze next_key signature — marker in the separate freeze registry

Exact message formats and on-chain checks are specified in Identity Operations.

The preimage alone never authorizes a rotation

Rotation requires both the preimage check and an Ed25519 signature by reveal_key over rot_msg (which binds the new next-key commitment). The preimage becomes public in the KERI KEL the moment the owner rotates there — without the signature requirement, any observer could replay it on-chain with an attacker-chosen new_next and capture the identity. See Identity Operations — Rotation.

A convicted checkpoint token is burnt (a closed identity's token is likewise burnt, its deposit refunded) — everything not spendable, even by reference, is burnt (the burn axiom). There is no tombstone: the conviction is recorded the way everything is on a blockchain — in the convict transaction, in history, forever — and the ledger's permanent footprint stays exactly the live identities. Burning does not bar re-registration. There is no inception absence proof and no global uniqueness gate, so if KERI still carries the AID it may register a fresh checkpoint: Cardano mirrors KERI and never permanently blocks an identity. A duplicate registration is possible but self-defeating — consumers fail closed on more than one live checkpoint, so it only harms the controller who mints it.

Freeze registry

A separate single-UTxO registry for emergency revocation, so a compromise response never queues behind inception/rotation traffic on the main registry. It stores sequence-scoped markers:

FreezeMarker { trie_key, seq, cur_pubkey_hash, next_digest }

A marker is authorized by the next key (the thief holds the current one) and dissolves automatically once the on-chain rotation lands (seq advances). Value cages must check both registries: identity leaf Active and no active FreezeMarker. See Identity Operations — Emergency freeze for the canonical definition and Veridian Bridge — Synchronization lag for the compromise-window workflow.

Value Cages (MPFS plane)

Existing MPFS cage UTxOs storing domain-specific leaf data, where leaves are owned by AIDs. Every leaf mutation requires two signatures:

  1. Leaf owner — Ed25519 authorization against the identity registry key-state, in one of two modes (native signer or detached signature); see Value Authorization.
  2. Cage oracle — the MPFS operator co-signs. The oracle is necessary-not-sufficient: it cannot mutate a leaf without the owner's authorization.

When a cage checks AID authorization it takes the identity registry (and the freeze registry) as CIP-31 reference inputs and:

  1. Checks the registry datum's root window for a root matching the supplied inclusion proof
  2. Verifies trie_key → leaf where leaf.status == Active
  3. Verifies no active FreezeMarker for trie_key (absence proof against the freeze root)
  4. Reads cur_pubkey from leaf.key_state
  5. Verifies the owner's authorization in the mode the flow requires (see the mode matrix)

The cage is configured at deploy time with the specific registries it trusts.

Residual oracle trust (value plane only)

The cage oracle controls liveness: it can reject or delay writes, garbage- collect, squat unowned value keys, and end the cage. It cannot forge data — no leaf mutation passes without the owner's signature. "Cannot forge" is not "cannot censor"; protocols that need censorship-resistance on the value plane must design for oracle exit (see Trust Model).

On-chain interaction

flowchart TD
    subgraph "Identity Plane (permissionless)"
        IR_UTxO["Identity Registry UTxO<br/>thread token<br/>datum: {roots}<br/>MPF trie: trie_key → IdentityLeaf"]
        FR_UTxO["Freeze Registry UTxO<br/>freeze token + freeze_root<br/>FreezeMarker entries"]
        IR_Script["Registry Script<br/>verify inception self-auth + deposit<br/>verify rotation (preimage + rot_msg sig)<br/>verify close / duplicity freeze"]
    end

    subgraph "Value Cage (oracle + owner)"
        VC_UTxO["Cage UTxO<br/>cage thread token<br/>datum: value_root"]
        VC_Script["Cage Script<br/>owner auth (Option A/B)<br/>+ oracle co-signature<br/>+ Active + no freeze marker"]
    end

    subgraph "MPFS Plugin / Sidecar"
        Snapshot["Cage UTxO snapshot<br/>tx_in + value_root @ chain point"]
        Builder["Value-write builder<br/>proof + unsigned tx"]
        Rebuild["If cage advanced first<br/>discard snapshot + rebuild"]
    end

    Owner["AID Owner<br/>(KERI wallet)"] -->|"inception / rotation / close"| IR_UTxO
    Owner -->|"emergency freeze<br/>(next_key sig)"| FR_UTxO
    VC_UTxO -->|"read stable pre-state"| Snapshot
    Snapshot --> Builder
    Owner -->|"authorizes value-write"| TX
    Oracle["Cage Oracle"] -->|"co-signs"| TX
    Builder -->|"builds value-write against snapshot"| TX
    TX -->|"spends snapshotted tx_in"| VC_UTxO
    VC_UTxO -->|"another write wins first"| Rebuild
    Rebuild -->|"newer snapshot"| Snapshot
    IR_UTxO -->|"CIP-31 reference input<br/>(root window + inclusion proof)"| VC_Script
    FR_UTxO -->|"CIP-31 reference input<br/>(freeze absence proof)"| VC_Script

    IR_Script --> IR_UTxO
    VC_Script --> VC_UTxO

    style IR_UTxO fill:#1e3a5f,stroke:#4a90d9,color:#e0e0e0
    style FR_UTxO fill:#3a1e1e,stroke:#d94a4a,color:#e0e0e0
    style VC_UTxO fill:#1e3a2f,stroke:#4a9040,color:#e0e0e0
    style Snapshot fill:#3a2f1e,stroke:#d9a04a,color:#e0e0e0

The registry UTxOs are not consumed by value-writes. Value-writes use them as non-spending CIP-31 reference inputs.

The cage UTxO is consumed, so the MPFS plugin/sidecar builds each value-write against a cage snapshot (tx_in + value_root) and rebuilds from a newer snapshot if another write spends that UTxO first.

What lives off-chain

  • Full KERI KEL history
  • CESR AID ↔ trie_key binding verification (KEL replay — see Binding verification protocol)
  • KERI watchers (monitor KELs for rotations and duplicity)
  • Witness receipts and duplicity detection
  • Settlement depth tracking

The on-chain layer is a minimal root of trust: trie_key-keyed identity, pre-rotation binding, and key possession proofs — permissionless on the identity plane, owner-plus-oracle on the value plane.