Install
Works with Drupal: ^10.6 || ^11.3Using Composer to manage Drupal site dependencies
Alternative installation files
Release notes
First stable release of Audit Chain: tamper-evident, hash-chained audit logging that any Drupal module can write to.
Each entry's hash covers its own content and the previous entry's hash, so a later insertion, deletion or edit breaks the chain and is detectable by an independent verification pass. With an HMAC key configured, forging a repair also requires the key — an edit made straight in the database cannot be quietly papered over.
Provenance
This is not new code. The chain was extracted from MCP Sentinel 1.13, where it had run in production as the audit trail for AI-agent traffic, and the behaviour is carried over unchanged: the same canonical payload and key order, the same HMAC-SHA256 over prev_hash|canonical with a plain SHA-256 fallback, encryption applied after hashing so enabling it later does not invalidate earlier entries, and the same append lock preventing two concurrent writers from forking the chain.
It was extracted because the capability was never specific to AI agents. Personnel-record reads, permission grants, configuration changes and break-glass logins all want the same guarantee, and none of them should have to install an AI-governance module to get it. MCP Sentinel remains its first consumer.
What is in it
AuditChainLoggerInterfaceand its implementation, writing to theaudit_chain_logtable. Consumers are identified by a channel, which is bound into the row hash so an entry cannot be re-attributed to a different channel after the fact.- Optional HMAC signing via a Key entity, and optional at-rest encryption of entry metadata via an Encryption Profile.
drush audit-chain:verify— walks every entry and verifies the chain. The exit code is the contract: non-zero meany, so it can be wired into monitoring ora deploy gate without parsing output.- A settings form at Configuration → System → Audit Chain for the signing key, the encryption profile, and streaming entries to the
audit_chainlogger channel for a SIEM.
API stability
The 1.x contract is AuditChainLoggerInterface: verify(),decodeMetadata() and prune(). verify() deliberately takes no channel argument — the chain is global, entries from every consumer are interleaved in one sequence, so a single channel cannot be verified in isolation without the entries between its own, and a per-channel walk could not tell a deletion from a gap. A
Two constraints forconsumers
Do not log per access check. A hook such as hook_entity_field_access() fires per field, per entity, per render; an entry each time producea chain nobody can read and a write-amplrequest and flush once atkernel.terminate.
Rotating the encryption profile orphans existing entries. The chain is computed over the plaintext, so entries encrypted under the previous profilestop verifying — and the failure lg. Export or re-encrypt first.
What it does not do
- It does not make deletion impossible. Nothing at the application layer can; it makes deletion evident, as a seam at the next verification.
- It does not order events acrosservers. The chain is one sequence in ons not replace dblog or syslog. Those areoperational logs; this is an evidentiary one, and deliberately narrower.
Verified
The kernel suite runs on Drupal 10.6, 11.3 and 11 (PHPUnit 9.6 and 11.5), and covers chain verification, detection of an edited entry, a deleted channel, keyed versus unkeyed chains,channel-scoped pruning, and compatibility with entries migrated from a consumer's own table. Drupal coding standards are enforced in CI.
Insser requiredrupal/audit_chain
Requires Drupal 10.6+ or 11.3+, Encrypt.