Audit Log Verification
Every Arbitex audit log entry is protected by an HMAC chain. The chain cryptographically links each entry to the one before it, so that any modification, deletion, or reordering of entries is detectable without requiring access to the original request data. This guide explains the chain construction, how to run verification against the API, and how to interpret the results.
Overview
Section titled “Overview”Audit log integrity is enforced using HMAC-SHA256. When an audit entry is written, Arbitex computes an HMAC over the entry’s content fields and the HMAC of the immediately preceding entry. The resulting digest is stored alongside the entry. Because each entry incorporates the previous digest, the entries form a tamper-evident chain: altering any entry invalidates its HMAC and breaks the chain link into every subsequent entry.
The HMAC chain proves:
- Integrity — the content of each entry has not been modified since it was written.
- Completeness — no entries have been deleted from the middle or end of the sequence.
- Ordering — entries have not been reordered; the chain is valid only in
created_at ASCorder.
The chain does not prove that entries were never written with incorrect data at capture time. It proves only that the stored entries have not changed after the fact.
HMAC chain structure
Section titled “HMAC chain structure”Chain fields
Section titled “Chain fields”Three fields are injected into each audit log entry by the integrity service:
| Field | Type | Description |
|---|---|---|
hmac | string | HMAC-SHA256 hex digest for this entry |
previous_hmac | string | HMAC of the preceding entry, or the genesis sentinel for the first entry |
hmac_key_id | string | Identifier of the HMAC key used to compute hmac |
hmac_key_id corresponds to the key ID embedded in the AUDIT_HMAC_KEY environment variable. It is stored with the entry so that verification can select the correct key if you rotate keys over time.
Genesis sentinel
Section titled “Genesis sentinel”The first entry in the chain has no predecessor. In place of a real previous_hmac, it carries the genesis sentinel: 64 consecutive zero characters.
0000000000000000000000000000000000000000000000000000000000000000This value is a fixed constant, not a key or secret. Its presence confirms that an entry is the chronological first in the chain.
HMAC message construction
Section titled “HMAC message construction”The HMAC is computed over a canonical message string assembled from three components:
key_id + ":" + json.dumps(content, sort_keys=True, default=str) + previous_hmacWhere:
key_idis the string identifier fromhmac_key_idcontentis the entry’s field dictionary, withhmac,previous_hmac, andhmac_key_idexcludedjson.dumpsusessort_keys=Trueto ensure deterministic field ordering;default=strserializes any non-JSON-native types as stringsprevious_hmacis appended as a plain hex string, not as a JSON value
The fields included in content for HMAC computation are:
| Field |
|---|
timestamp |
user_id |
action |
conversation_id |
model_id |
provider |
prompt_text |
response_text |
token_count_input |
token_count_output |
cost_estimate |
latency_ms |
metadata |
tenant_id |
ip_address |
ip_address is always null — it is included in the HMAC computation for schema stability but is never populated.
The chain is ordered by created_at ASC. Verification must process entries in this order to correctly thread previous_hmac values through the sequence.
Step-by-step verification procedure
Section titled “Step-by-step verification procedure”Verification is performed through a single API call. No request body is required. The endpoint recomputes the HMAC for every entry in the chain and checks that each previous_hmac matches the stored hmac of the preceding entry.
Prerequisites:
- Your API token must have the admin role.
- The
AUDIT_HMAC_KEYenvironment variable must be configured on the Arbitex server.
Run verification:
curl -X POST https://your-arbitex-instance.example.com/api/admin/audit/verify \ -H "Authorization: Bearer your-arbitex-api-key" \ -H "Content-Type: application/json"The endpoint accepts no request body. The response is returned synchronously. For large audit logs, the request may take several seconds to complete.
Reading the verification result
Section titled “Reading the verification result”The response is a JSON object with three fields:
{ "valid": true, "total_entries": 48291, "errors": []}| Field | Type | Description |
|---|---|---|
valid | boolean | true if the chain is intact across all entries; false if any integrity violation was detected |
total_entries | integer | Number of entries evaluated during this verification run |
errors | array of strings | Human-readable descriptions of each integrity violation; empty when valid is true |
When valid is false, errors contains one entry per violation. Review each error message to determine the scope and location of the problem. A single tampering event typically produces one error at the modified or deleted entry and cascading errors on every subsequent entry in the chain.
If AUDIT_HMAC_KEY is not configured on the server, the endpoint returns HTTP 400 before performing any verification. This is a configuration error, not an integrity failure.
What tamper evidence looks like
Section titled “What tamper evidence looks like”The verification engine identifies four categories of integrity violation. Sample error messages are illustrative; exact wording may include entry identifiers and timestamps from your audit log.
Modified entry
Section titled “Modified entry”An entry’s content was changed after it was written. The stored hmac no longer matches the HMAC recomputed from the current field values.
Hash mismatch on entry id=ae4f2c1b at 2026-03-07T11:42:08Z: stored hmac does not match recomputed valueThe entry at the reported position is suspect. All subsequent entries are also suspect because their previous_hmac chain was seeded from the modified entry’s HMAC.
Deleted entry
Section titled “Deleted entry”An entry was removed from the chain. The previous_hmac on the entry following the deletion point does not match the hmac of the entry now preceding it.
Chain gap on entry id=9d3e7a22 at 2026-03-07T11:42:09Z: previous_hmac does not match hmac of preceding entryThe deletion is detectable at the first entry after the gap. Entries before the gap remain valid; entries after the gap are suspect because the chain origin for that segment is broken.
Reordered entries
Section titled “Reordered entries”Entries were moved to a different position in the sequence. The previous_hmac of the displaced entry does not match the hmac of the entry that now precedes it.
Chain gap on entry id=7b1c4d09 at 2026-03-07T11:42:11Z: previous_hmac does not match hmac of preceding entryReordering produces the same error signature as deletion. Both represent a broken chain link. Distinguishing between the two requires cross-referencing with your SIEM archive.
Appended or injected entries
Section titled “Appended or injected entries”An entry inserted into the middle of the chain breaks the previous_hmac link on the entry that originally followed that position.
Chain gap on entry id=2f8a6b44 at 2026-03-07T11:42:10Z: previous_hmac does not match hmac of preceding entryAn injected entry cannot carry a valid HMAC because the attacker would need the HMAC secret to recompute a consistent chain from the injection point forward.
Export formats
Section titled “Export formats”Arbitex provides three export formats for audit log data. All formats include the hmac, previous_hmac, and hmac_key_id fields so that offline verification is possible independent of the API endpoint.
JSON
Full event structure as a JSON array. Suitable for programmatic processing, offline HMAC recomputation, and long-term archival. Each array element is a complete audit entry object.
CSV
Flattened field representation. Suitable for spreadsheet analysis and ad-hoc querying. Nested objects (such as metadata) are serialized to string representations.
SIEM-ready
Structured for direct SIEM ingestion. Events are formatted for compatibility with OCSF-aware platforms and include the HMAC fields alongside the normalized event schema.
Export is available through the admin API. See Audit log for export endpoint details and query parameters.
Requirements
Section titled “Requirements”AUDIT_HMAC_KEY environment variable
The HMAC key must be set in the AUDIT_HMAC_KEY environment variable on the Arbitex server. This variable supplies both the key material and the key identifier used in hmac_key_id. If this variable is absent, new entries are written without HMAC fields and the verification endpoint returns HTTP 400.
Rotate this key according to your key management policy. After rotation, entries written before the rotation carry the old hmac_key_id; the verification engine uses hmac_key_id to select the correct key for each entry. Historical keys must remain available to the server for verification to succeed across rotation boundaries.
Admin role
The POST /api/admin/audit/verify endpoint requires an API token with the admin role. Standard user tokens receive HTTP 403.
Key escrow
Store HMAC keys outside the Arbitex platform — in your SIEM, a secrets manager, or an HSM. If the platform were compromised, an attacker with access to both the stored entries and the HMAC key could recompute a valid-appearing chain. Out-of-band key storage is what makes the tamper evidence meaningful.
See also
Section titled “See also”- Audit log — audit entry fields, export API, retention, and SIEM integration
- DLP Overview — how the 3-tier DLP pipeline produces findings recorded in audit entries
- Policy Engine overview — how policy evaluation fields are generated