v2.0.0
Application-level provenance and change history for queue-heavy, distributed Laravel apps. Every change is recorded with the real actor, the origin that survives the queue, and a correlation chain back to the one action that caused it — with a verifiable, tamper-evident history on top.
Provenance (the core)
- Multi-level actor attribution: user / job / command / scheduler / system, resolved by an extensible provider chain.
- Origin that survives the queue: a job dispatched by a user keeps that user, serialized into the payload across real workers — proven by a database-worker test.
- Correlation id + cross-model trace: one id per request / command / job cascade, drawn as a ladder indented by job-nesting depth.
- Impersonation-aware: "Jane Doe (impersonated by Support Admin)" — login-as is attributed to both, not just the victim.
- Foreign-key label snapshots ("John Doe", not
user_id: 5), captured at event time and surviving row deletion.
Forensics and compliance
- Time machine: reconstruct the exact state a record had at any past date, read-only.
- Tamper evidence: SHA-256 hash chain +
audit-log:verify; pruning keeps a chain anchor so verification stays strict. - Signed integrity digests (CloudTrail-style): detect deletion of a whole signed segment or the entire table.
- GDPR subject reports in one command (
audit-log:subject-report, NDJSON / HTML). - Recursive secret redaction (passwords, tokens, api keys, including nested JSON) before values reach the database.
- Access logging (who viewed a record) and change reason (
withReason, covered by the hash chain).
Operations
- Anomaly detection: change burst, mass delete, off-hours, cascade weight (write-amplification), plus detection-as-code custom rules; alerts to Slack, signed webhook or mail, on demand or on a cron.
- SIEM streaming to Splunk / Datadog / Elastic / generic HTTP — off the request path, queued, fail-soft.
- Native multi-tenancy: tenant stamped at capture (survives the queue), every read auto-scoped.
- Retention with archive-before-delete to any disk (S3 included), and an optional dedicated database connection with a
transfer-datacommand.
Governance
AuditPolicyper model:ignore()noisy fields, capturewhen()a condition holds,sample()a fraction of high-churn models.- Sampling decided per correlation, so one record's history within a unit of work is kept or dropped together — never left with holes.
event_versionschema contract on every record (and in the API / SIEM payloads) so consumers can branch on the layout.- Fluent query DSL (
AuditLog::query()->field('status')->from('pending')->to('paid')->get()) over the same filter path as the array API.
Analytics and performance
- Indexed changed-keys table: field-level searches seek an index instead of scanning the JSON of every row;
audit-log:backfill-changed-keyscovers existing rows (chunked, resumable). - Correlation analytics: "Top cascades" ranks the heaviest root actions (writes / models / depth), each linking to its trace.
- Change hotspots: most-changed models and most-changed fields, scoped to the current filters.
- Deliberate write path: one record insert plus one batched changed-key insert; capture is fail-open and never blocks the host.
Dashboard and developer experience
- Optional dashboard (
audit-log:ui enable): filters, value-transition search, statistics with heatmap, pagination, anomalies, trace, time machine — vendored assets, no CDN. - In-app documentation page covering every feature, plus a live facade Playground.
- Facades for everything the dashboard shows, and an opt-in JSON API for your own admin panels.
Non-goals (permanent boundaries)
No event sourcing or state replay, no backpressure engine, no in-package search engine, no distributed observability platform, no query profiler. Each would force the audit log to become a source of truth or a real-time system, breaking the invariant that makes it safe to install: capture is fail-closed, off your write path, additive, and never changes your data.
Requirements
PHP 8.1 / 8.2 / 8.3, Laravel 9–13. CI: Pint, PHPStan level 8, the unit suite across the PHP matrix, and a 90%+ line-coverage gate.