# Warrant Attestation

Every warrant answer and every appeal exhibit is signed. A payer, an RCM team, or
an auditor can verify, offline and without an API key, that Verzi issued exactly
those bytes at the stated time and that nothing changed since.

The signature is detached and in-body. The response stays readable JSON, and the
signature travels with the answer when it is filed with a payer. There is no
separate token to keep and no HTTP header that a mail client strips.

## The attestation block

A signed response carries one extra field, `attestation`:

```json
{
  "...": "the warrant answer",
  "attestation": {
    "alg": "Ed25519",
    "kid": "ed25519-1a2b3c4d5e6f7a8b",
    "issued_at": "2026-10-02T15:04:05.123456+00:00",
    "canonicalization": "RFC8785",
    "signed": "the entire response object with the single field attestation.signature removed",
    "public_key_url": "https://api.verzi.health/rules/warrant/signing-key",
    "signature": "base64url, no padding"
  }
}
```

- `issued_at` is the issuance time, inside the signed content. It is distinct
  from the coverage date: `meta.as_of` (the date of service) is what the warrant
  was resolved against, `issued_at` is when Verzi signed the answer.
- `kid` names the key. A key rotation yields a new `kid`, so old and new
  attestations stay distinguishable.
- `signature` is the only field NOT covered by itself. Everything else in the
  response, including the rest of the `attestation` block, is signed.

## Verifying a response

1. Fetch the public key named by `attestation.public_key_url`. Match its `kid` to
   `attestation.kid`. The endpoint returns the raw key (base64url) and a PEM.
2. Take the response object. Remove the single field `attestation.signature`.
   Leave every other field, including the rest of `attestation`, in place.
3. Canonicalize the result with RFC 8785 (JSON Canonicalization Scheme).
4. base64url-decode `attestation.signature` and verify it as an Ed25519 signature
   over the canonical bytes, using the public key.

If the signature verifies, the answer is authentic (Verzi's key), intact (no
field changed), and timestamped (`issued_at` is signed). If any field was altered,
step 4 fails.

RFC 8785 is used because the answer contains numbers (fee amounts). A plain
`sort_keys` JSON dump is not reproducible across languages for floating-point
values; JCS is. Libraries exist for JavaScript, Python, Java, and Go.

### Reference (Python)

```python
import json, base64, rfc8785
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

def verify(response: dict, public_key_raw: bytes) -> bool:
    att = response["attestation"]
    sig = base64.urlsafe_b64decode(att["signature"] + "=" * (-len(att["signature"]) % 4))
    stripped = {**response,
                "attestation": {k: v for k, v in att.items() if k != "signature"}}
    try:
        Ed25519PublicKey.from_public_bytes(public_key_raw).verify(sig, rfc8785.dumps(stripped))
        return True
    except Exception:
        return False
```

## The public key

`GET /rules/warrant/signing-key` (unauthenticated) returns:

```json
{
  "alg": "Ed25519",
  "kid": "ed25519-1a2b3c4d5e6f7a8b",
  "canonicalization": "RFC8785",
  "public_key_b64url": "the 32-byte raw public key, base64url",
  "public_key_pem": "-----BEGIN PUBLIC KEY----- ...",
  "verify": "the recipe above, in one line"
}
```

Pin the key out of band for the strongest guarantee: a verifier that already
holds the published key does not need to trust the live endpoint at verify time.

## Scope and limits

- Signed today: `GET /rules/warrant`, `GET /rules/warrant/appeal`, and
  `GET /rules/warrant/changes`.
- This is attestation, not third-party notarization. `issued_at` is Verzi's own
  clock, signed by Verzi's key. A trusted RFC 3161 timestamp from an independent
  authority is a planned upgrade, not part of v1.
- A deployment without a signing key configured serves responses with no
  `attestation` block. It never emits a fake or empty signature. The presence of
  the block is the signal that the answer is signed.
