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.
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.