Configuration

Every audit system has different requirements. A startup tracking page edits needs different settings than a bank logging financial transactions. The configuration file lets you tune AuditChain to your exact use case — from choosing a dedicated database connection to setting up Slack alerts when chain integrity fails.

After installation, publish the config file if you haven't already:

php artisan vendor:publish --tag="audit-chain-config"

This creates config/audit-chain.php in your application.

Quick Reference

Key Default Description
connection null DB connection for audit logs (separate recommended)
table audit_logs Table name
state_table null Sentinel table for chain state (null defaults to _state)
chain_seed env('AUDIT_CHAIN_SEED', 'genesis') Secret seed for genesis hash
queue.enabled true Dispatch audit recording to queue
queue.connection null Queue connection
queue.queue default Queue name
events.log_reads false Capture retrieved events
anonymization.replacement [ANONYMIZED] GDPR anonymization replacement string
retention.days 90 Days to keep logs (audit:prune)
notifications.channels ['mail'] Notification channels: mail, webhook
notifications.mail_to [] Email addresses for mail alerts
notifications.webhooks [] Webhook URLs (Slack, Teams, Discord, custom)

Database Connection

'connection' => null,

By default, AuditChain uses your application's default database connection. For production systems, a separate database connection is strongly recommended. This isolates audit data from application data, making it harder for a compromised application to tamper with the audit trail.

To use a separate connection, define it in config/database.php and reference it here:

// config/database.php
'connections' => [
    'audit' => [
        'driver' => 'mysql',
        'host' => env('AUDIT_DB_HOST', '127.0.0.1'),
        'database' => env('AUDIT_DB_DATABASE', 'audit'),
        'username' => env('AUDIT_DB_USERNAME', 'audit_user'),
        'password' => env('AUDIT_DB_PASSWORD', ''),
        // ...
    ],
],

// config/audit-chain.php
'connection' => 'audit',

Tip: For maximum immutability, configure the audit database user with INSERT and SELECT only permissions — no UPDATE or DELETE. AuditChain enforces immutability at the Eloquent layer, but database-level restrictions protect against direct SQL tampering.

Table Name

'table' => 'audit_logs',

The database table used to store audit log entries. Change this if audit_logs conflicts with an existing table in your application.

State Table

'state_table' => null, // defaults to '{table}_state'

The sentinel table used to store chain state (last_hash, checkpoint_hash). This single-row table is used by HasAuditTrail to track the latest hash in the chain via SELECT last_hash FOR UPDATE, avoiding the need to scan the audit_logs table. If set to null, it defaults to your audit table name suffixed with _state (e.g., audit_logs_state).

Chain Seed

'chain_seed' => env('AUDIT_CHAIN_SEED', 'genesis'),

The chain seed is used to compute the genesis hash — the first link in the cryptographic hash chain. Set this to a random, secret value in production via your .env file:

AUDIT_CHAIN_SEED=your-random-secret-value-here

You can generate a suitable value with:

php artisan tinker --execute="echo Str::random(64);"

Important: A predictable chain seed weakens the tamper-evidence guarantees of the hash chain. If an attacker knows the seed, they could reconstruct a valid genesis hash. Always set this to a secret value in production.

Note: The chain seed only matters when using HasAuditTrail (full mode). If you are only using HasActivityLog (light mode), no hash chain is computed and the seed is not used.

Queue Configuration

'queue' => [
    'enabled' => true,
    'connection' => null,
    'queue' => 'default',
],

By default, audit log recording is dispatched to a queue to avoid slowing down HTTP requests. This is the recommended setting for production.

Key Default Description
queue.enabled true When true, audit logs are recorded via a queued job. Set to false to record synchronously.
queue.connection null The queue connection to use. null uses your application's default queue connection.
queue.queue default The queue name to dispatch audit jobs to.

If you want audit logs recorded on a dedicated queue (to avoid delays from other jobs), set a specific queue name:

'queue' => [
    'enabled' => true,
    'connection' => 'redis',
    'queue' => 'auditing',
],

Note: When queue is enabled and you are using HasAuditTrail, the hash chain is still computed synchronously at dispatch time to maintain chain ordering. The queued job handles the database write.

Events

'events' => [
    'log_reads' => false,
],

Controls which Eloquent events are captured.

Key Default Description
events.log_reads false When true, captures retrieved events (model reads).

Warning: Enabling log_reads is very verbose. Every Eloquent query that loads an auditable model will generate an audit log entry. This can produce a massive volume of records. Only enable this if you have a specific compliance requirement to track data access, such as GDPR Article 15 (right of access) or Article 33 (breach notification — knowing who accessed what data).

Anonymization

'anonymization' => [
    'replacement' => '[ANONYMIZED]',
],

Settings for GDPR anonymization. When you call $model->anonymize(), string personal data fields are replaced with this string followed by a random suffix (via Str::random(8)) to avoid UNIQUE constraint violations. Non-string fields are set to null.

For example, with the default replacement:

$user->anonymize();
// email => '[ANONYMIZED]-a1b2c3d4'
// name  => '[ANONYMIZED]-x9y8z7w6'
// age   => null  (non-string fields become null)

You can customize the replacement string if needed:

'anonymization' => [
    'replacement' => '[REDACTED]',
],

Retention

'retention' => [
    'days' => 90,
],

Controls automatic pruning of old audit logs via the audit:prune Artisan command.

Key Default Description
retention.days 90 Number of days to retain audit logs. Logs older than this are deleted by audit:prune.

To prune logs, run the command manually or schedule it:

# Delete logs older than the configured retention period
php artisan audit:prune

# Override with a custom retention period
php artisan audit:prune --days=365

# Prune only a specific model type
php artisan audit:prune --type="App\Models\User"

Schedule it in routes/console.php:

Schedule::command('audit:prune')->daily();

Note: Check your regulatory requirements before setting a retention period. GDPR generally requires data minimization (shorter retention), while financial regulations may require longer retention (e.g., 7 years). Set retention.days to a value that satisfies your most demanding compliance obligation.

Notifications

'notifications' => [
    'channels' => ['mail'],
    'mail_to' => [env('AUDIT_ALERT_EMAIL', '')],
    'webhooks' => [],
],

Configures how you are alerted when audit:verify --notify detects chain integrity failures.

Key Default Description
notifications.channels ['mail'] Which channels to use: mail, webhook, or both.
notifications.mail_to [] Array of email addresses to receive mail alerts.
notifications.webhooks [] Array of webhook URLs (Slack, Teams, Discord, or custom endpoints).

Mail Alerts

Add the AUDIT_ALERT_EMAIL variable to your .env:

AUDIT_ALERT_EMAIL=security@yourcompany.com

Or specify multiple recipients directly in the config:

'notifications' => [
    'channels' => ['mail'],
    'mail_to' => [
        'security@yourcompany.com',
        'cto@yourcompany.com',
    ],
],

Webhook Alerts

Webhook payloads are compatible with Slack, Microsoft Teams, Discord, and custom endpoints. Both text and content keys are included for cross-platform compatibility.

'notifications' => [
    'channels' => ['mail', 'webhook'],
    'mail_to' => [env('AUDIT_ALERT_EMAIL', '')],
    'webhooks' => [
        env('AUDIT_ALERT_SLACK_WEBHOOK'),
        env('AUDIT_ALERT_TEAMS_WEBHOOK'),
    ],
],

Scheduling Verification

For continuous monitoring, schedule the verification command in routes/console.php:

Schedule::command('audit:verify --notify')->hourly();

This runs chain verification every hour and sends notifications through your configured channels if any integrity failures are detected.

Full Config File

For reference, here is the complete default configuration:

// config/audit-chain.php

return [
    'connection' => null,
    'table' => 'audit_logs',
    'state_table' => null, // defaults to '{table}_state'
    'chain_seed' => env('AUDIT_CHAIN_SEED', 'genesis'),
    'queue' => [
        'enabled' => true,
        'connection' => null,
        'queue' => 'default',
    ],
    'events' => [
        'log_reads' => false,
    ],
    'anonymization' => [
        'replacement' => '[ANONYMIZED]',
    ],
    'retention' => [
        'days' => 90,
    ],
    'notifications' => [
        'channels' => ['mail'],
        'mail_to' => [env('AUDIT_ALERT_EMAIL', '')],
        'webhooks' => [],
    ],
];

What's Next?