Audit Chain Integrity
Arbitex uses an HMAC-SHA256 hash chain to provide tamper evidence for the audit trail. Any modification, deletion, or reordering of audit entries breaks the chain and is reported by the verification API. This page describes how the chain works, how to verify it, and how to manage the signing key.
For the full audit log schema and event type taxonomy, see Audit Data Model Reference.
Why a hash chain
Section titled “Why a hash chain”Standard database audit logs are susceptible to tampering by privileged users: an attacker with database write access can modify, delete, or reorder entries without leaving a trace. An HMAC chain anchors each entry to the content of all previous entries. Changing any entry changes its HMAC, which then invalidates every subsequent entry’s previous_hmac linkage.
The chain does not prevent deletion — it detects it. If entries are deleted from the middle of the sequence, the gap is visible when adjacent entries are verified because the previous_hmac of the entry after the gap no longer matches the hmac of the entry before it.
Chain architecture
Section titled “Chain architecture”GENESIS_HMAC sentinel
Section titled “GENESIS_HMAC sentinel”The chain starts with a fixed sentinel value:
GENESIS_HMAC = "0000000000000000000000000000000000000000000000000000000000000000"This is a 64-character hex string (256 zero bits). The first entry in every org’s audit chain has previous_hmac = GENESIS_HMAC. Any audit chain that doesn’t start with this value has been tampered with or was not properly initialized.
Per-entry HMAC computation
Section titled “Per-entry HMAC computation”For each audit event, the platform computes an HMAC-SHA256 over a deterministic message:
message = hmac_key_id + ":" + json_canonical(event_fields) + previous_hmachmac = HMAC-SHA256(AUDIT_HMAC_KEY, message.encode("utf-8"))Where:
hmac_key_idis the key version identifier (default:"default")json_canonical(event_fields)isjson.dumps(event, sort_keys=True, default=str)over the event content, excluding thehmac,previous_hmac, andhmac_key_idfieldsprevious_hmacis the HMAC of the preceding entry (orGENESIS_HMACfor entry 0)AUDIT_HMAC_KEYis the secret signing key (from environment or Azure Key Vault)
Including hmac_key_id as a prefix ensures that entries signed under different keys produce different HMACs, preventing cross-key splicing attacks.
Three fields are written to each audit entry after computation:
| Field | Value |
|---|---|
hmac_key_id | Key version used to sign this entry |
previous_hmac | HMAC of the immediately preceding entry |
hmac | HMAC-SHA256 digest for this entry |
Chain linkage
Section titled “Chain linkage”Entry 0: previous_hmac = GENESIS_HMAC hmac = H(key, "default:" + json(e0) + GENESIS_HMAC)
Entry 1: previous_hmac = hmac(Entry 0) hmac = H(key, "default:" + json(e1) + hmac(Entry 0))
Entry 2: previous_hmac = hmac(Entry 1) hmac = H(key, "default:" + json(e2) + hmac(Entry 1))...Each entry is cryptographically linked to its predecessor. Any modification to entry N will produce a different hmac(Entry N), making previous_hmac on entry N+1 incorrect, which cascades through all subsequent entries.
Verification algorithm
Section titled “Verification algorithm”The verify_chain function checks three invariants across an ordered sequence of events:
Invariant 1: Genesis check
Section titled “Invariant 1: Genesis check”The first event’s previous_hmac must equal GENESIS_HMAC. A mismatch indicates that the start of the chain has been tampered with or that the events were not retrieved in full sequence order.
Invariant 2: HMAC correctness
Section titled “Invariant 2: HMAC correctness”For each event, the verifier recomputes the HMAC from the event’s content and checks it against the stored hmac field:
verifier = HMACChain(key=AUDIT_HMAC_KEY, key_id=event["hmac_key_id"])verifier._previous_hmac = event["previous_hmac"]expected_hmac = verifier.compute_hmac(event)
if event["hmac"] != expected_hmac: # Content has been modified errors.append(f"Event {idx}: HMAC mismatch")A mismatch means the event content has been modified after the chain was written.
Invariant 3: Chain linkage
Section titled “Invariant 3: Chain linkage”Each event’s previous_hmac must equal the hmac of the immediately preceding event:
if event["previous_hmac"] != expected_prev_hmac: errors.append(f"Event {idx}: previous_hmac mismatch")A mismatch indicates a gap (missing entries) or a reordering attack.
Error reporting
Section titled “Error reporting”The verifier continues checking after finding errors (rather than stopping at the first failure). This means a single call returns all integrity violations found in the checked range. Each error includes the event index and a description of the specific invariant that failed.
Verification API
Section titled “Verification API”POST /api/admin/audit/verifyAuthorization: Bearer arb_live_your-api-key-hereContent-Type: application/json
{ "start": "2026-01-01T00:00:00Z", "end": "2026-01-31T23:59:59Z"}Success response:
{ "valid": true, "events_checked": 4821, "errors": []}Integrity failure response:
{ "valid": false, "events_checked": 4821, "errors": [ "Event 142: HMAC mismatch (expected 'a3f...', got 'c7d...')", "Event 143: previous_hmac mismatch (expected 'a3f...', got 'c7d...')" ]}The verification API requires Org Admin role. It verifies all entries for the calling admin’s org within the specified time range, ordered by created_at ascending.
Key management
Section titled “Key management”Configuration
Section titled “Configuration”The HMAC signing key is configured via the AUDIT_HMAC_KEY environment variable. In production, this is sourced from Azure Key Vault via the platform secrets backend.
When AUDIT_HMAC_KEY is empty or unset, HMAC chaining is silently disabled — entries are written without hmac, previous_hmac, or hmac_key_id fields. This is acceptable for development deployments. Production deployments must configure this key.
Key rotation
Section titled “Key rotation”To rotate the signing key:
- Generate a new 256-bit (32-byte) random key:
openssl rand -hex 32 - Deploy the new key with a new
hmac_key_idvalue (e.g.,"v2") - New entries are signed with the new key and
hmac_key_id = "v2" - Existing entries retain their original
hmac_key_id = "default"and continue to verify against the old key
The chain verifier reads hmac_key_id from each entry and uses the corresponding key for verification. Verification of entries across a key rotation requires access to both the old and new keys. Arbitex stores both keys simultaneously during transition periods.
There is no need to re-sign old entries during key rotation — the hmac_key_id field provides per-entry key identification.
Key security considerations
Section titled “Key security considerations”The HMAC key must be kept secret. An attacker who possesses the HMAC key can forge signatures and create an internally consistent tampered chain. Key storage in Azure Key Vault with access logging provides the audit trail for key access.
GeoIP enrichment and chain membership
Section titled “GeoIP enrichment and chain membership”Observed network facts (src_ip, dst_ip) are included in the HMAC chain. They are immutable facts about the connection captured at request time.
GeoIP enrichment fields (country, city, ISP, ASN) are excluded from the HMAC chain. GeoIP databases are updated periodically — if enrichment fields were part of the chain, re-enriching entries (e.g., after a database update) would break the chain. Only the raw IP is signed.
This means GeoIP enrichment data can be re-derived without invalidating the audit chain, but the IP address itself cannot be changed.
Outpost audit chain
Section titled “Outpost audit chain”For Hybrid Outpost deployments, audit events are generated on-premises and synced to the Cloud control plane. Synced entries are included in the org’s audit chain:
- The chain is per-org across all event sources (cloud and outpost)
- Outpost entries are distinguished by
source="outpost"and a non-nulloutpost_id - HMAC computation is identical to cloud entries — the same algorithm applies
The chain is contiguous for events inserted in created_at order. Out-of-order insertions (e.g., delayed outpost sync) will produce chain verification errors for the out-of-order entries. The Outpost sync mechanism is designed to deliver events in order.
Compliance uses
Section titled “Compliance uses”| Framework | How HMAC chain satisfies the requirement |
|---|---|
| PCI-DSS Req 10.5 | Tamper-evident audit trail; HMAC detects modification of stored log entries |
| SOX ICFR | HMAC chain provides evidence that IT general controls logs have not been altered |
| SEC 17a-4(f) | Non-rewritable record property — HMAC detects any modification |
| HIPAA 45 CFR § 164.312(b) | Audit control integrity — detection of unauthorized modification of audit records |
See Compliance Frameworks for the full regulatory mapping.
See also
Section titled “See also”- Audit Data Model Reference — Full audit log schema, all fields, OCSF mapping
- Audit log export — Admin API for audit search, filter, and export
- Security Overview — Full security architecture overview
- Encryption — Encryption in transit and at rest
- Compliance Frameworks — Regulatory requirements the audit chain satisfies