Documentation

Signed reports

Every scan produces a cryptographically signed report. Reports are dual-signed — HMAC-SHA256 for Cavexia's internal integrity audits, and Ed25519 for offline verification by anyone holding the published Cavexia public key.

What gets signed

The signature covers the full report payload returned by GET /api/report/{id}: findings, risk score, scan metadata, and the report version. The signatures array itself is excluded from the canonicalized input, so a report can be re-signed for verification.

  • All findings (severity, title, description, remediation, excerpt, category)
  • Per-server risk scores and the aggregate risk score
  • Scan timestamp, scan ID
  • The literal field version: 2 — verifiers that don't recognise the version must refuse to verify rather than guess.

Algorithms

Signature schemes
HMAC-SHA256 — symmetric, Cavexia-verified
Ed25519 — asymmetric, publicly verifiable

Canonicalization sorts object keys alphabetically at every nesting level. Arrays preserve order. Two semantically-identical reports always produce the same signature regardless of how the JSON was serialized.

The HMAC signature lets Cavexia internally verify report integrity using a server-side secret. The Ed25519 signature lets anyone — your CI pipeline, an auditor, a customer — verify a report offline using the public key. Both signatures cover the same canonical payload; either one verifying is sufficient proof of authenticity.

Public key

The current Cavexia public key is published at a stable, cacheable URL:

GET https://cavexia.com/.well-known/cavexia-signing.pub

-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA...
-----END PUBLIC KEY-----

Historical public keys are addressable by keyId so older signed reports continue to verify after key rotation:

GET https://cavexia.com/api/signing-keys/cavexia-2026-05

-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----

Response shape

Signatures are carried in the JSON body and the response header so both archived bodies and proxy-stripped HEAD responses can verify.

HTTP/1.1 200 OK
Content-Type: application/json
X-Cavexia-Signatures: [{"alg":"HMAC-SHA256","value":"..."},{"alg":"Ed25519","value":"...","keyId":"cavexia-2026-05"}]
Cache-Control: public, max-age=86400, immutable

{
  "version": 2,
  "id": "cf3777e4-2b6f-4aa8-92c9-a6d743e29eac",
  "scannedAt": "2026-05-21T15:00:00.000Z",
  "riskScore": 100,
  "totalFindings": 6,
  "findings": { /* ... */ },
  "signatures": [
    { "alg": "HMAC-SHA256", "value": "<base64>" },
    { "alg": "Ed25519",     "value": "<base64>", "keyId": "cavexia-2026-05" }
  ]
}

How to verify

Three paths depending on your threat model and your access to the Cavexia API:

1. cavexia verify (recommended)

Run the CLI against any local or hosted report. Fetches the public key (cached after first use), verifies the Ed25519 signature offline, exits 0 on success.

$ cavexia verify ./cavexia.json
✓ Signature valid (Ed25519, keyId=cavexia-2026-05)
  report: cf3777e4-2b6f-4aa8-92c9-a6d743e29eac
  scanned: 2026-05-21T15:00:00.000Z
2. In CI (GitHub Action)

Drop the verify action into your workflow. Fails the PR check if cavexia.json is missing, unsigned, or tampered.

- uses: cavexia/verify-action@v1
  with:
    report: cavexia.json
3. Re-fetch from Cavexia

Hit GET /api/report/{id} and compare the returned signature bytes against your archived copy. Simplest path; requires Cavexia's availability and the original scan ID.

Key rotation

Cavexia rotates the signing keypair quarterly. New reports are signed under the new key; reports signed under prior keys remain verifiable indefinitely via the keyed lookup endpoint. If a key is ever compromised, the keyId scopes the blast radius to a single rotation period.

Verifiers should always pin a specific keyId from the report's signatures array — not the current public key — so a verified report stays verified across rotations.

Audit use cases

  • SOC 2 evidence: attach signed reports to control reviews as proof of scan history.
  • Customer assurance: send a signed scan to a customer who asks about MCP supply-chain risk.
  • EU AI Act (high-risk systems): archive periodic scans of agent tooling as part of a risk-management file.
  • Incident response: a signed pre-incident scan lets you prove the configuration you were running before a compromise.
  • Supply-chain provenance: include the report ID in your release notes so downstream consumers can verify the security posture you scanned at release time.