{
  "keys": [
    {
      "kid": "ed25519-8e7eca434c9ab5a3",
      "alg": "Ed25519",
      "public_key_pem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAVig7BtiInI/uUI12sqGNisoG9Jj2o9JNAXGgiErFdcI=\n-----END PUBLIC KEY-----\n",
      "public_key_jwk": {
        "crv": "Ed25519",
        "x": "Vig7BtiInI_uUI12sqGNisoG9Jj2o9JNAXGgiErFdcI",
        "kty": "OKP"
      }
    }
  ],
  "fields": {
    "signature": "Our server-side seal (HMAC). Only this server can check it, through POST /api/v1/verify. It is not an ID and you cannot check it yourself.",
    "public_signature": "Ed25519 signature you can check yourself with the key below. This is the one to use for offline checks.",
    "audit_chain_root": "Hash of the last audit chain entry. Recompute the chain and compare."
  },
  "test_vector": {
    "about": "A made-up certificate signed with the live key. Your code should produce exactly canonical_json from certificate_without_signatures, and the signature should verify with the key above. Ed25519 is deterministic, so sig never changes for this key.",
    "certificate_without_signatures": {
      "assistant": "test-vector",
      "closed_at": "2026-09-24T00:00:00.000Z",
      "desk": "Legal Intake Desk",
      "note": "café – unicode stays as-is",
      "passed": true,
      "score": 87.5,
      "steps": [
        3,
        1,
        2
      ]
    },
    "canonical_json": "{\"assistant\":\"test-vector\",\"closed_at\":\"2026-09-24T00:00:00.000Z\",\"desk\":\"Legal Intake Desk\",\"note\":\"café – unicode stays as-is\",\"passed\":true,\"score\":87.5,\"steps\":[3,1,2]}",
    "canonical_utf8_sha256": "32fe138f52c3b22a3d1ebb142e13bf8f95f067569d1afbb274ca62c2cae56aa3",
    "public_signature": {
      "alg": "Ed25519",
      "kid": "ed25519-8e7eca434c9ab5a3",
      "sig": "rl9vgqQIdy3hwB4zewNg1iSNDdBa8WYtE2KOmwo5lXz7Dl7VVUHR1UmmHxo62M3jPZALMXKtRJCZw8DaJZ5bCg"
    }
  },
  "how_to_verify": "Take the certificate, remove its signature and public_signature fields, and serialize the rest as canonical JSON: keys sorted, no whitespace, strings and numbers as JSON.stringify writes them. Check public_signature.sig (base64url) as an Ed25519 signature over those UTF-8 bytes with the key whose kid matches. For the audit chain, recompute each entry hash as sha256 of canonical JSON of {seq, ts, actor, event, payload_hash, prev_hash}, check each prev_hash links to the previous hash, and check the last hash equals certificate.audit_chain_root.",
  "example_python": "import json,base64; from cryptography.hazmat.primitives.serialization import load_pem_public_key\nc=dict(cert); ps=c.pop('public_signature'); c.pop('signature',None)\nmsg=json.dumps(c,sort_keys=True,separators=(',',':'),ensure_ascii=False).encode()\nload_pem_public_key(pem.encode()).verify(base64.urlsafe_b64decode(ps['sig']+'=='),msg)  # raises if invalid"
}