Provenance
Every action is permanently provable
A mutable audit log is a comfort, not a guarantee. We chain every event with the SHA-256 hash of the previous record, so altering a single entry invalidates every record that came after it. Tampering becomes a self-evident, mathematically demonstrable fact — not a thing you have to detect.
Executive summary
Every audit event — login, seal application, role change, document access — is recorded with a SHA-256 hash linking it to the previous event. Verification walks the chain in O(n) time; tampering with any single record breaks every record after it in a single deterministic check.
On top of the chain we layer 7 prebuilt compliance reports (activity summary, access audit, document lifecycle, credential status, security incidents, data retention, integrity verification), JSON and CSV export, and per-organization retention policies. Reports go through a draft → approved → immutable workflow so a finalized audit cannot be quietly edited.
Our commitments
Five rules for the audit log
Tampering is mathematically detectable
Every record's hash includes the previous record's hash. Modifying one event in the middle of history requires recomputing every event after it — visibly so, in any verification pass.
Verification is open code, not a trust statement
Chain integrity is checked by a deterministic Elixir function whose source is in this repository. Customers can run their own verifier against an exported chain — independent of our infrastructure.
Approved reports are immutable
Compliance reports go through draft → approved. Once approved, the report's content hash is signed and cannot be altered. Adding new content means a new report version with a new hash, never a silent edit.
The log captures who, what, when, and from where
Every event records actor, action, target, timestamp, source IP, user agent, and a full metadata payload. No "system" actor without explicit cause. No "automated" without the worker module name.
Retention is your decision, not ours
Each organization sets its own retention policy. We do not auto-prune below your policy minimum. We do not retain above your policy maximum. The default is "keep forever" — sealing is a professional record.
Implementation — the chain
How the chain is built and verified
Implementation — events
What the log records
Implementation — the mirror pattern
How security and credential events join the chain
Document and seal events write directly to audit_logs. Security events (MFA, SCIM, step-up at sealing) and credential-manifest events (license re-checks, status changes, suspensions, revocations) keep their canonical tables — and mirror into the same per-organization hash-chained audit_logs row-for-row at write time.
The mirror keeps the canonical
security_events
and credential manifest_events
tables as the analytics-friendly stores, while every row also lands in
audit_logs
with the same chained-hash treatment as a sealing event. One verifier, one chain per org, covers all of them. Tampering with any mirrored row is detected by the same chain-linkage check that protects the document and seal events next to it.
Best-effort, never blocking.
If a mirror write fails — advisory-lock timeout, transient DB error, a constraint we did not foresee — the canonical
security_events
or manifest_events
row is still inserted, the user's action still succeeds, and the failure is logged plus reported to Sentry for the on-call to reconcile. The chain stays consistent with what it contains; we never silently substitute or partially write.
Implementation — compliance reports
Seven prebuilt reports for your auditor
The full picture
What is built, what is being built, and what we chose not to build
Live today
SHA-256 hash chain
LiveO(n) verifier over the org's event log; sequential chain walk with parallel per-record hashing.
Security + manifest events mirrored into the chain
LiveSecurity.Event.record/3 (MFA, SCIM, step-up at sealing) and Manifests.append_event/2 (credential re-checks, status changes, suspensions, revocations) both mirror into the per-org hash-chained audit_logs table. Mirror is best-effort: failures are logged + Sentry-reported, never block the canonical insert.
Per-event metadata: actor, IP, UA, target, before/after state
LiveNo silent system actors. Every event traces to a who and a why.
Audit log export (JSON + CSV)
LiveSelf-service export for your SIEM or auditor. Includes the chain hashes for independent verification.
Seven prebuilt compliance reports
LiveActivity summary, access audit, lifecycle, credentials, incidents, retention, integrity.
Draft → approved report workflow
LiveApproved reports lock in their content hash; later edits create new versions, never silent revisions.
Per-organization retention policies
LiveConfigurable per-event-type retention. Auto-archive on schedule; never auto-delete below policy minimum.
Building now
Periodic chain anchoring to an external TSA
Building nowDaily snapshot of the chain head signed by an RFC 3161 TSA. Anchors the chain to a third-party clock so we cannot rewrite history even if we wanted to.
Same TSA pipeline as document seals; reuses verified infrastructure.
SIEM streaming via signed webhooks
Building nowReal-time event push to Splunk, Datadog, Sumo. HMAC-signed, replay-protected. Same hardening as the API webhook surface.
Field-level encryption for high-sensitivity event metadata
Building nowSensitive payloads in audit events (e.g., credential numbers in verification events) get a second AES-256-GCM wrap so partial database read does not leak them.
Roadmap
Custom report builder
RoadmapOrg admins define their own report shapes alongside the prebuilt seven. Same draft → approved → immutable lifecycle.
Cross-organization log federation
RoadmapEnterprise customers with multiple EngineeringID orgs get a federated view across them, with per-org access controls preserved.
Continuous compliance dashboards
RoadmapReal-time dashboards mapped to specific control IDs (SOC 2 / ISO / HIPAA) showing live evidence collection per control.
Considered & rejected
Blockchain-anchored audit log
Considered & rejectedA chained SHA-256 + periodic TSA anchor gives the same tamper-evidence at zero per-write cost.
Why we rejected it: blockchain solves the problem of "no trusted third party exists." We do have a trusted third party — public TSAs — and using them is the eIDAS / 21 CFR Part 11 / common-law-evidence answer to "prove this was unmodified at this time." Blockchain adds cost and latency to a problem that already has a published-standard solution.
Append-only file-based logs without a chain
Considered & rejectedAppend-only filesystems are append-only until they are not. Chunked storage backends, S3 object replacement, restoring a snapshot — many ways to silently rewrite an "append-only" log.
Why we rejected it: filesystem-level append-only is a property of one storage system. A SHA-256 hash chain is a mathematical property of the data, independent of the storage system. We use the latter; storage hardening is defense in depth on top.
Compressing duplicate events
Considered & rejectedA "10x clicked the same button" rollup looks tidier and obscures attack signal.
Why we rejected it: failed-MFA events especially must be recorded individually. Aggregation is what the report layer does — it does not rewrite the underlying log. The log is dense on purpose.
Skipping the chain in dev environments to save cycles
Considered & rejectedCode that runs differently in dev than in prod is the source of every "but it worked locally" production incident.
Why we rejected it: the chain runs everywhere. Dev environments hash a few hundred events per session. The performance argument was "we save 50ms across an entire test suite," and the risk was "production-only code paths." Hard no.
Compliance mappings
Controls this surface satisfies
System Operations — Anomaly Detection
Hash-chained event log with first-divergence reporting
System Operations — Evaluation of Security Events
Seven prebuilt reports map directly to evaluation evidence
Availability — Recovery
Tamper-evident operational records survive disaster recovery
Event logging
Per-event actor, action, time, source — chained for integrity
Administrator and operator logs
Privileged actions are first-class events with full payload
Audit Controls
Hardware, software, and procedural mechanisms to record activity
Audit trail — secure, computer-generated
Tamper-evident chain with attribution; export retains integrity
Records of processing activities
Per-record processing log; retention configurable per data category
For compliance teams
Questions you don't need to call to ask
Can an EngineeringID employee modify the audit log?
What's the smallest tamper that produces a detection?
Credo.Enterprise.Audit.AuditChain.compute_record_hash/7
for the exact field set. Cosmetic changes to resource_name, metadata, or change-state fields are recorded but not yet part of the chained hash — the v2 hash that covers all 14 content fields is implemented but not wired into live writes yet.
Which events are actually in the chain — everything in the system?
audit_logs
table. Three categories of writes land there: (1) document and seal events that write directly, (2) security events (MFA challenges, SCIM provisioning, step-up MFA at sealing) mirrored from
Credo.Security.Event.record/3, (3) credential-manifest events (license re-checks, status changes, suspensions, revocations) mirrored from
Credo.Credentials.Manifests.append_event/2. Out of scope: telemetry events, request logs, signup-form submissions, and other operational logs — those are not chained. The mirror covers events written after this code shipped; rows that existed in
security_events
or manifest_events
before the mirror landed remain in their canonical tables but are not in the chain.
What happens if a mirror write fails?
security_events
or manifest_events
row has already been inserted, so the user's action still completes and the analytics tables stay accurate. The chain does not silently diverge — it simply omits a record we know about, which the on-call investigates from the Sentry alert. Chain verification still passes because the chain stays consistent with what it contains.
Can we run the verifier ourselves against an exported log?
What's the retention default and can we change it?
Are reports themselves tamper-evident?
What happens if the chain verification fails?
Do you support real-time SIEM ingestion?
An audit log that stands up to its own audit
Run the verifier yourself, or have your auditor talk to ours.