Verifiable attestations

Every verdict can come with proof. Add attest=true to a safety tool and the answer arrives with an Ed25519-signed attestation your agent can log, cache, hand to a counterparty, or verify later — without trusting Cerberus at the moment it acts. Accountable autonomy needs receipts.

Why

An agent that acts on a verdict is accountable for it. A verdict it can't prove it received is weak — it can't show why it proceeded, and a counterparty can't check the claim independently. A signed attestation turns an ephemeral answer into portable, tamper-evident evidence: the exact verdict, the subject it applies to, and the ledger it was true as of — sealed under a key only this server holds.

How to get one

Pass attest=true to any verdict tool (check_xrpl_token, check_address, check_transaction, resolve_asset). The verdict is unchanged; an attestation object is added:

{
  "verdict": "likely_safe",
  "attestation": {
    "available": true,
    "claim": {
      "tool": "check_xrpl_token",
      "subject": {
        "issuer": "rsoLo2S1kiGeCcn6hCUXVrCpGMWLrRrLZz",
        "currency": "534F4C4F00000000000000000000000000000000"
      },
      "verdict": "likely_safe",
      "confidence": "medium",
      "issued_at": "2026-07-25T23:21:30Z",
      "ledger_index": 105851012,
      "key_id": "cerberus-attest-v1"
    },
    "algorithm": "ed25519",
    "signature": "base64(64-byte ed25519 signature)",
    "public_key": "base64(32-byte raw ed25519 public key)",
    "verify": "https://cerberusindex.com/attestation.html"
  }
}

Verify it — independently

You never have to call back to trust it. The signing key is public at https://cerberusindex.com/api/attestation. Recompute the canonical claim bytes and check the signature yourself:

import json, base64, httpx
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

att = result["attestation"]
claim = att["claim"]

# 1. Fetch the published key (or pin it once).
key = httpx.get("https://cerberusindex.com/api/attestation").json()["public_key"]
pk  = Ed25519PublicKey.from_public_bytes(base64.b64decode(key))

# 2. Recompute the exact signed bytes: canonical JSON of the claim
#    (sorted keys, no spaces), UTF-8.
msg = json.dumps(claim, sort_keys=True, separators=(",", ":")).encode()

# 3. Verify. Raises on failure.
pk.verify(base64.b64decode(att["signature"]), msg)
print("verified:", claim["tool"], claim["verdict"],
      "as of ledger", claim["ledger_index"])

Or, as a convenience, the free verify_attestation tool checks a signature against this server's key and returns the claim — useful when you don't want to implement Ed25519, though independent verification is the point.

The signature covers the claim object canonicalized as JSON with sorted keys and (",", ":") separators, UTF-8. Any change to any field — a flipped verdict, a swapped subject — breaks the signature.

FAQ

What exactly is signed?

The claim: the tool, the subject (issuer/currency/address/etc.), the verdict and confidence, the issue time, and the validated ledger index the verdict was anchored to. Not the surrounding response — just the decisive claim.

Does attesting cost extra?

No. attest=true is included in the tool's normal price; verify_attestation is free.

What does the ledger index give me?

A freshness anchor: the claim states the exact validated ledger it was true as of, so a stale attestation can't be passed off as current.

Can I pin the key instead of fetching it?

Yes — the key is stable (key_id: cerberus-attest-v1). Pin it once and verify entirely offline thereafter.