Audit Trail Rebuild — New Endpoints
Reference for the Phase 1+2+3 Audit Trail rebuild API surface (2026-05). All endpoints additive; existing
/api/audit/*and/api/hash-chain/*endpoints remain unchanged.
Authenticated endpoints (admin/owner)
Chain integrity
| Method | Path | Description |
|---|---|---|
GET | /api/v1/audit-integrity/snapshot?namespace=decision|operations|all | Per-org chain integrity snapshot. Returns chainStatus + heartbeat history + OTS status + Tessera status. Accepts optional namespace filter (per ADR-AT-05 two-chain architecture). |
GET | /api/v1/audit-integrity/heartbeat/history?limit=N | Per-org heartbeat verification history. |
Notary attestation (Governance+/Enterprise/Custom)
| Method | Path | Description |
|---|---|---|
GET | /api/v1/notary-attestation | List notary attestations for the org. |
GET | /api/v1/notary-attestation/:id | Get specific attestation metadata. |
GET | /api/v1/notary-attestation/:id/pdf | Download signed attestation PDF. Response carries Cache-Control: no-store + X-Content-Type-Options: nosniff. |
POST | /api/v1/notary-attestation/generate | On-demand attestation generation (admin/owner). |
Erasure (GDPR Art. 17, ADR-AT-04)
GDPR Art. 17 erasure is a mandatory data-subject right; these endpoints are
available on every plan tier (no requireFeature gate). All four
endpoints require the admin or owner role — erasure-ledger metadata
is not readable by every org member.
| Method | Path | Description |
|---|---|---|
POST | /api/v1/erasure/acknowledge-and-execute | Two-step protocol: customer pre-flight acknowledgment + payload nullification + erasure-ledger append. Body: { traceIds, acknowledgment, reason? }. Batch capped at 5 000 trace IDs per call (ERASURE_BATCH_TOO_LARGE 400 if exceeded; configurable via ERASURE_BATCH_MAX). Each traceId must be a non-empty string ≤200 chars. |
GET | /api/v1/erasure/verify | Full erasure-ledger chain verification. |
GET | /api/v1/erasure/status | Erasure ledger summary stats. |
GET | /api/v1/erasure/trace/:traceId | Lookup erasure entry for a specific trace. |
Customer-portable bundle export
| Method | Path | Description |
|---|---|---|
POST | /api/v1/audit-bundle/generate | Generate signed .tar.gz bundle for offline regulator verification. Body: { periodStart, periodEnd }. Admin/owner only. The bundle is built in-memory; periods that contain more than 500 000 chain entries return BUNDLE_PERIOD_TOO_LARGE (HTTP 413) — split the period or queue the streaming generator (Phase 3.1). Cap configurable via AUDIT_BUNDLE_MAX_ROWS. |
GET | /api/v1/audit-bundle/:bundleHash/download | Download generated bundle. Cardinal Rule #1 enforced (cross-org access rejected with 403). Response carries Cache-Control: no-store + X-Content-Type-Options: nosniff — signed compliance artefacts must not persist in browser disk cache or HTTPS-inspecting proxies. |
Insurance feed (Custom tier)
| Method | Path | Description |
|---|---|---|
GET | /api/v1/insurance-feed | List monthly insurance feed records. |
GET | /api/v1/insurance-feed/:id | Get specific feed metadata. |
Bias-stratified metrics
cohortField must be one of the allow-listed dimensional attributes safe to
surface as a fairness-cohort label (Cardinal Rule #4 — prevents extracting
arbitrary DecisionTrace payload fields into the cohort key):
agentId(default)outputDecision.decisionTypeoutputDecision.actionmetadata.regionmetadata.localemetadata.environment
Anything else returns INVALID_COHORT_FIELD (HTTP 400).
| Method | Path | Description |
|---|---|---|
GET | /api/v1/bias-metrics?cohortField=agentId&from=...&to=... | Per-cohort chain integrity metrics with fairness alerts (>5pp deviation). |
Witness admin (Adjudon admin/owner only)
| Method | Path | Description |
|---|---|---|
POST | /api/v1/witness-admin/register | Register external EU-domiciled witness. Returns {witness, sharedSecret} — sharedSecret shown ONCE, transmit out-of-band. |
GET | /api/v1/witness-admin | List registered witnesses. |
DELETE | /api/v1/witness-admin/:id | Deactivate witness. |
Public endpoints (Cardinal Rule #2 documented exceptions)
All public CT endpoints are rate-limited at 1 000 requests per IP per
15 minutes (apiLimiter). Public per RFC 6962 model — proof generation
is computationally non-trivial, so the rate-limit prevents scraping +
DoS without blocking legitimate auditors or monitors.
merkleRoot, fromSTHRoot, toSTHRoot MUST match /^[0-9a-f]{64}$/
(64-char lowercase hex). Length-only validation was tightened in 2026-05.
Witness STH (CT pattern)
These endpoints proxy to the Adjudon Tessera Personality service running at
tessera.adjudon.com (Frankfurt eu-central-1,
Ed25519-signed c2sp.org/tlog-checkpoint
format). Public verifier key:
/keys/tessera-public.txt. The
underlying log can also be queried directly via the standard tlog-tiles
paths at https://tessera.adjudon.com/{decision|operations}/checkpoint,
/tile/<L>/<N>, /tile/entries/<N> for any compatible client tooling.
| Method | Path | Description |
|---|---|---|
GET | /api/audit-witness/sth?namespace=decision|operations|all | Latest Signed Tree Head for the chain namespace. |
GET | /api/audit-witness/inclusion-proof?namespace=...&merkleRoot=<hex> | Merkle inclusion proof for a given root. |
GET | /api/audit-witness/consistency-proof?namespace=...&fromSTHRoot=<hex>&toSTHRoot=<hex> | Consistency proof between two STHs. |
GET | /api/audit-witness/status | Witness service status. |
Witness gossip (HMAC-authenticated)
| Method | Path | Description |
|---|---|---|
POST | /api/audit-witness/gossip/observe | External witness POSTs STH observation. Headers: X-Adjudon-Witness-Secret + hmacSignature over canonical JSON payload. Replay window: payload MUST include timestamp (ISO-8601 or epoch ms) within ±10 minutes of server time; outside the window returns 401 with sentinel "outside acceptable replay window". Configurable via WITNESS_GOSSIP_REPLAY_WINDOW_MS. |
GET | /api/audit-witness/gossip/consortium | Public consortium status (active witnesses, mismatches). |
Insurance partner-pull (HMAC-authenticated)
| Method | Path | Description |
|---|---|---|
POST | /api/v1/insurance-feed/partner-pull | Insurance underwriter pulls signed feed. Headers: X-Adjudon-Partner-Name + X-Adjudon-Partner-Secret + hmacSignature. Replay window: body payload.timestamp (ISO-8601 or epoch ms) MUST be within ±10 minutes of server time, else PARTNER_REPLAY_WINDOW (HTTP 401). Configurable via PARTNER_PULL_REPLAY_WINDOW_MS. The shared-secret comparison and HMAC verification both use crypto.timingSafeEqual (constant-time). |
Response envelope
All endpoints follow the standard Adjudon envelope:
{ "success": true, "data": { /* ... */ } }
{ "success": false, "error": "human-readable", "code": "REGISTERED_CODE" }
Error codes from this rebuild are registered in
backend/utils/errorCodes.js (57 new codes including the
2026-05 hardening pass: BUNDLE_PERIOD_TOO_LARGE,
ERASURE_BATCH_TOO_LARGE, INVALID_TIMESTAMP,
PARTNER_REPLAY_WINDOW). SDKs can react
programmatically to specific codes (see
Error Codes).
Operational notes
qSeal failure mode (Enterprise + Custom tier)
When AUDIT_TRAIL_QSEAL_ENABLED=true and the configured D-Trust qSeal
endpoint is unreachable, the qSeal scheduler does not fall back to a
mock seal. The eIDAS Art. 35 qSeal that establishes German §371a Abs. 3
statutory presumption is either real or absent — a mock seal that looks
real on the dashboard but offers zero legal effect would silently
downgrade the customer's compliance posture.
The chain itself does not break. Per ADR-AT-02, the qSeal is layer 4 (additive German legal-presumption), while Merkle anchor (layer 1) + OpenTimestamps (layer 2) + Tessera witness (layer 3) carry the integrity baseline. A single-day D-Trust outage records "qSeal pending" for that day and is retried by the scheduler on the next run; the chain-integrity dashboard surfaces the degraded state explicitly.
Smoke-heartbeat verification (chains > 1 000 entries)
chainHeartbeat.smokeVerifyOrg verifies the last SMOKE_TAIL_SIZE
(default 1 000) chain entries every 5 minutes. The range-query path
through hashChainService.verifyChain looks up the predecessor's
chainHash via Mongo to seed the bootstrap — a missing predecessor
correctly reports a chain break, not a false alarm. Customers with
chains larger than SMOKE_TAIL_SIZE get the same correctness guarantee
as a full nightly replay, with bounded latency per heartbeat.
Methodology
Full specification:
github.com/adjudon/audit-trail-methodology
(version adjudon-audit-trail-methodology/v1.0).
Open-source verifier: github.com/adjudon/audit-trail-verifier (Rust, Apache 2.0).
External witness reference monitor: github.com/adjudon/witness-reference-monitor (Go, Apache 2.0).