Skip to main content
The transparency log is an append-only public record of every canonical card identity Mnemom has ever composed. Each row carries a signed AAP attestation token + the Merkle leaf hash + the tree size at integration time. Inclusion proofs are computed on demand from rows ordered by log_index.

What it proves

For any historic timestamp t and any agent_id:

Endpoints

All of these are unauthenticated — the log is meant to be publicly verifiable.

GET /v1/transparency/root

Returns the current signed Merkle root + tree size + published_at, signed by the active AAP signing key under a distinct typ (AAP-TransparencyRoot/v1) so the per-card attestation and the root commitment can’t be confused.

GET /v1/transparency/log/{agent_id}?at=<ISO>[&card_kind=alignment|protection]

Point-in-time query. Returns the row whose composed_at ≤ at is greatest, plus a freshly-computed inclusion proof against the current root.

GET /v1/transparency/log/{agent_id}/{log_index}

Direct by-index lookup; returns the row + an inclusion proof against the current root. Use this when you already know the log_index (e.g., from a previous response).

GET /v1/transparency/log/{agent_id}/recent?limit=<n>[&card_kind=alignment|protection]

Returns the last N entries for an agent, newest first (log_index descending), each with a freshly-computed inclusion proof. limit defaults to 10 and is server-clamped to 100. An agent with no attestations returns 200 { "data": [] }.

GET /v1/transparency/consistency?first=<n>[&second=<n>]

Returns an RFC 6962 / RFC 9162 §2.1.4 consistency proof that the tree of size second contains the tree of size first as a prefix — i.e. the first first entries were not reordered, altered, or removed. second defaults to the log’s current size. This is the only endpoint that can distinguish “the log grew” from “the log was rewritten and re-signed”: an inclusion proof verifies against whatever root it’s given, so it can’t detect a wholesale reissue. Chaining consistency proofs across successive signed roots from GET /v1/transparency/root is what makes the append-only claim checkable rather than promised. It does not prove Mnemom published every root it computed — detecting a “split view” (a consistent-but-different chain shown to someone else) requires gossip between independent monitors, which this log doesn’t yet implement.

Row shape

Mirrors the schema. One row per canonical card identity:
merkle_leaf_hash = SHA-256(0x00 || canonical_json({agent_id, card_kind, content_hash, version, composed_at})). The 0x00 prefix is the RFC 6962-style domain separator.

Append discipline

  • Append-only in the database, not just the application — the service role has only SELECT, INSERT grants on card_attestations (no UPDATE/DELETE/TRUNCATE), and a BEFORE UPDATE OR DELETE trigger additionally blocks mutation at the table level — including for the table owner, which a REVOKE alone doesn’t cover.
  • Idempotent — unique index on (agent_id, card_kind, content_hash, version) makes re-append a no-op. The compose-hook and the 5-minute reconciler are both safe to retry.
  • Best-effort from the compose path — the canonical write commits first; the log append + signing happen post-commit. A 5-minute reconciler closes any gap that arose from a worker crash mid-flight.

Merkle tree

Sigstore Rekor compatibility

The row shape is intentionally Sigstore Rekor-shaped so the future migration is a data move rather than a schema rewrite. The mapping is documented in the schema spec and the migration plan at migration/sigstore-rekor-migration. Until that migration ships (post-V1-GA), the thin Postgres log serves as Mnemom’s authoritative public log. Backups land in S3 with object-lock to defend against database compromise + rewrite.

See also