What the audit chain is
The audit chain is a per-merchant append-only log. Every time an auditable action occurs — a configuration change, a credential rotation, a data residency update, an AI Operator auto-action — a new row is inserted into audit_events. The row stores a hash that includes the prior row's hash, making retroactive edits detectable without consulting an external oracle.
The hash formula is: SHA-256(prev_hash || material), where material is the JSON-serialized event body (event_type, resource_id, actor, timestamp, payload). The first event in any chain uses null as prev_hash — its hash is the genesis block. Concurrent inserts from multiple workers are serialized via pg_advisory_xact_lock so the chain never has a gap or reorder.
-- audit_events · simplified schema
CREATE TABLE audit_events (
event_id TEXT PRIMARY KEY, -- ae_9a2f1
merchant_id TEXT NOT NULL,
prev_hash TEXT, -- null for genesis
hash TEXT NOT NULL, -- SHA-256(prev_hash || material)
event_type TEXT NOT NULL,
resource_id TEXT,
actor TEXT NOT NULL,
timestamp TIMESTAMPTZ NOT NULL,
payload JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);The insert trigger fires on every auditable action. Because the advisory lock ensures only one insert commits at a time per merchant_id, prev_hash always points to the real last event — no fork, no race, no gap.
How to verify the chain
Chain verification recomputes SHA-256(prev_hash || material) for every event in sequence and compares the result to the stored hash. A match means the chain is unbroken. Any divergence flags the chain as compromised.
There are two verification paths: the API and the dashboard UI. Both are read-only and do not modify state.
API: GET /v1/audit/verify
curl -H "Authorization: Bearer $TRACKLAYER_API_KEY" \
"https://api.tracklayer.io/v1/audit/verify?merchant_id=m_abc123"
# Response
{
"merchant_id": "m_abc123",
"chain_status": "valid",
"total_events": 14832,
"break_count": 0,
"first_event": "ae_9a2f1",
"last_event": "ae_9b4e3",
"last_verified_at": "2026-04-23T14:02:11Z"
}
# Chain broken — example
{
"merchant_id": "m_abc123",
"chain_status": "broken",
"total_events": 14832,
"break_count": 2,
"first_event": "ae_9a2f1",
"last_event": "ae_9b4e3",
"last_verified_at": "2026-04-23T14:02:11Z",
"broken_events": ["ae_9b1d9", "ae_9b2da"]
}Dashboard: /security/audit-verify
Open Settings → Compliance → Audit Chain. Click “Verify chain” to run the check and display the visual chain view. The UI shows the latest 20 events, chain status badge, total event count, break count, and a timestamp of last successful verification.
The WC (WebCargo) subscription example: a compliance officer verifying the WC merchant chain sees 14,832 events spanning 11 months, zero breaks, and a last_verified_at within the last hour. That is the evidence package for a SOC2 audit on the audit trail control.
What gets logged
The chain captures every significant configuration and identity event. Below is the full event type inventory:
All Phase 9.2 AI Operator auto-actions use the same event type (ai_operator_auto_action) with the full action context in payload — what was detected, what was decided, what was applied, and the operator confidence score.
Wave 9 P9.3 audit search UI lets you filter the audit log by event_type, actor, date range, and resource_id. The search is surfaced at /security/audit-search. Results are paginated and exportable as CSV for auditor submission.
Weekly R2 export for offline retention
When your CI token has R2 bucket permissions, TrackLayer exports all audit_events for each merchant to your private R2 bucket every Sunday at 03:00 UTC. The export is gzip-compressed JSON with full chain material — event_id, prev_hash, hash, material, timestamp — so you can re-run verification against the offline copy at any point.
# R2 export path pattern
tracklayer-audit-{account}/backup/YYYY/MM/DD/audit-events-{merchant_id}-{date}.json.gz
# Example
tracklayer-audit-abc123/backup/2026/04/19/audit-events-m_abc123-2026-04-19.json.gz
# Export metadata in the JSON envelope
{
"merchant_id": "m_abc123",
"exported_at": "2026-04-19T03:00:11Z",
"export_id": "exp_7f3b2a",
"events": [
{ "event_id": "ae_9a2f1", "prev_hash": null, "hash": "h8f3...", ... },
{ "event_id": "ae_9b3e2", "prev_hash": "h8f3...", "hash": "ha1c...", ... }
]
}Retention is 90 days by default. You can configure longer retention via your R2 lifecycle rules. The Phase 7 follow-up will add configurable export frequency and custom prefixes.
To enable, go to Settings → Compliance → Audit Export. Paste your R2 bucket path and save. TrackLayer validates write access before activating the schedule.
Pre-trigger rows and the Phase 7.6 backfill
Audit_events rows that were created before the Phase 6.4 insert trigger was deployed do not have hash chain entries. To give compliance auditors a complete chain, the Phase 7.6 backfill Durable Object iterates all existing audit_events and computes retroactive hash entries for them.
The backfill writes a synthetic genesis event and then computes prev_hash chains for all pre-existing rows. A backfill_completed event is appended after the backfill finishes, marking the import boundary. This event has event_type backfill_completed and payload.import_id for auditability.
# Backfill completion event — written after Phase 7.6 DO finishes
{
"event_type": "backfill_completed",
"resource_id": "audit_events",
"actor": "system/backfill_do",
"timestamp": "2026-04-01T04:12:08Z",
"payload": {
"import_id": "bf_4e7c1",
"rows_imported": 8834,
"chain_from": "ae_9a2f1",
"chain_to": "ae_9f0ab",
"backfill_triggered_by": "Phase 7.6 DO"
}
}You can verify the backfill boundary by checking for the backfill_completed event with the matching import_id in the audit log.
Tying the audit chain to SOC2 evidence collection
SOC2 Type II requires documented evidence of operating effectiveness. The audit chain contributes to the following trust service criteria:
- CC7.2 — System operations:audit_events proves which configuration changes occurred, who initiated them, and when — without relying on a team member's memory or a Slack thread.
- CC7.4 — Change management: every schema change, destination pause, and credential rotation is an auditable event in the chain. SOC2 auditors can query the chain for a date range and see the full change log.
- CC6.1 — Logical access: api_key_created, api_key_rotated, and api_key_revoked events document identity lifecycle. If an access review is needed, the chain provides the authoritative record.
- A1.2 — Availability: destination_pause and destination_resumed events document uptime-affecting actions. Cross-reference with your incident log to prove SLA continuity.
To prepare for a SOC2 audit: run GET /v1/audit/verify for the full observation window, download the weekly R2 export for the period, and share the verify response (chain_status: valid) with your auditor as the audit trail control evidence.
FAQ
When does the audit chain start recording?
The insert trigger is deployed with Phase 6.4. All new events from that point are chained. Rows created before the trigger are backfilled by the Phase 7.6 Durable Object — you will see a backfill event in the chain marking the import timestamp.
Can I modify or delete an audit event?
No. The audit_events table is write-once. The trigger fires on INSERT only. UPDATE and DELETE operations are rejected at the database level. If advisory_lock_enabled is true on the merchant row, even system_worker cannot append without the advisory key.
What happens if the verify endpoint returns break_count > 0?
A break_count > 0 means stored_hash !== SHA-256(prev_hash || material) for at least one event. This indicates either a database integrity issue or a tampering attempt. Contact TrackLayer support immediately and do not clear the events. The platform will investigate the chain segment.
How does the weekly R2 export help SOC2?
SOC2 Type II requires evidence of operating effectiveness over a period. The weekly export gives your auditor an immutable offline copy of the chain. You can re-run verification against the exported JSON at any point, proving the chain was unbroken during the audit window.
Does the AI Operator auto-action use the same chain?
Yes. All Phase 9.2 AI Operator actions — auto-destination pausing, suggested schema fixes, anomaly resolutions — are written to audit_events with event_type ai_operator_auto_action and the full action context in the payload.
How do I give my auditor access?
Create a read-only team member with the Viewer role and scope it to the audit log only. The auditor can use the GET /v1/audit/verify endpoint or the dashboard verify UI. They cannot append, modify, or delete events.