Skip to content

Laravel Exile v1.1.0 — Enforcement Hardening & Custom Notifications

Latest

Choose a tag to compare

@SignalByNick SignalByNick released this 19 Jul 01:22

Laravel Exile v1.1.0 🎉

Laravel Exile v1.1.0 focuses on enforcement reliability, evidence integrity, safer escalation, and fully customizable notifications.

This release strengthens the package's internal moderation workflow while preserving the existing public API and default combined-ban behavior.

✨ New Features

  • Transactional enforcement writes
  • After-commit lifecycle events
  • Queued ban notifications
  • Customizable Markdown mail templates
  • Replaceable notification classes
  • Configurable notification subjects and content
  • Configurable notification action buttons
  • Configurable notification date formats and timezones
  • Configurable combined-ban matching
  • Strict account and IP matching
  • Strict account, device, and IP matching
  • SHA-256 evidence checksums
  • Evidence integrity verification
  • Concurrency-safe strike escalation
  • Dedicated applied-escalation records
  • Default strike expiration support
  • Expanded PHPUnit regression coverage
  • Updated documentation and release guidance

🔄 Transactional Enforcement

Ban, restriction, warning, strike, and revocation writes now use database transactions.

The new enforcement sequence is:

Validate
→ Save enforcement
→ Save audit record
→ Commit transaction
→ Dispatch event
→ Send notification

If enforcement persistence or audit logging fails, the transaction rolls back instead of leaving incomplete moderation data.

📣 After-Commit Events

Lifecycle events now implement Laravel's after-commit event contract.

Updated events include:

  • BanIssued
  • BanRevoked
  • BanExpired
  • RestrictionIssued
  • RestrictionRevoked
  • StrikeIssued
  • WarningIssued
  • AppealSubmitted
  • AppealResolved

Events are not processed for enforcement actions that fail or roll back.

✉️ Queued Notifications

The bundled ban notifications now:

  • Implement ShouldQueue
  • Use Laravel's Queueable trait
  • Run after database commits
  • Support configurable delivery channels
  • Support optional silent failure handling
  • Support replaceable notification classes

Bundled notification classes include:

  • BanIssuedNotification
  • BanRevokedNotification
  • BanExpiredNotification

When notifications are enabled, run a queue worker:

php artisan queue:work

🎨 Customizable Mail Templates

Laravel Exile now includes publishable Markdown mail templates.

Publish them with:

php artisan vendor:publish --tag=exile-views

Published templates are placed in:

resources/views/vendor/exile/mail/ban-issued.blade.php
resources/views/vendor/exile/mail/ban-revoked.blade.php
resources/views/vendor/exile/mail/ban-expired.blade.php

Applications can customize:

  • Email subjects
  • Headings
  • Introductory text
  • Reason and expiration labels
  • Permanent-enforcement text
  • Action button text
  • Action button URLs
  • Closing text
  • Salutations
  • Date formats
  • Timezones
  • Notification classes
  • Notification channels
  • Markdown views

🔗 Configurable Combined-Ban Matching

Combined bans now support two matching modes:

'security' => [
    'combined_ban_match' => 'any',
],

any

Preserves the original Laravel Exile behavior.

A combined ban matches when any stored identifier matches.

For AccountAndIp, either the account or IP address may match.

all

Requires every identifier belonging to the combined ban type to match.

'security' => [
    'combined_ban_match' => 'all',
],

For AccountAndIp, both the account and IP address must match.

For AccountDeviceAndIp, the account, IP address, and device fingerprint must all match.

The default remains any for backward compatibility.

🔐 Evidence Integrity

Newly uploaded evidence files now receive a SHA-256 checksum.

$evidence->checksum_sha256;

Applications can verify that a stored file has not changed:

$evidence->hasValidChecksum();

Evidence checksum validation:

  • Reads files through Laravel's filesystem
  • Supports local and compatible cloud disks
  • Uses SHA-256 hashing
  • Uses hash_equals() for secure comparison
  • Returns false for missing or modified files
  • Returns false for legacy evidence without a checksum

If checksum calculation or evidence creation fails, the newly stored file is removed.

📈 Concurrency-Safe Escalation

Automatic escalation now protects against duplicate enforcement caused by concurrent requests.

The escalation engine now:

  • Starts a database transaction
  • Locks the affected account row
  • Calculates active strike points
  • Selects the highest applicable threshold
  • Reserves the threshold atomically
  • Applies one ban or restriction
  • Records an audit action

A new exile_escalations table stores:

  • Account model type
  • Account model ID
  • Threshold points
  • Applied action
  • Metadata

A unique database constraint prevents the same account and threshold from being applied twice.

⚠️ Strike Expiration

The existing strike expiration configuration now applies to newly issued strikes:

'strikes' => [
    'default_points' => 1,
    'expire_after_days' => null,
],

Example:

'expire_after_days' => 180,

An explicitly supplied expiration date still overrides the configured default.

🗃️ Database Changes

Laravel Exile v1.1.0 includes two new migrations.

Evidence checksum column

Adds:

exile_evidence.checksum_sha256

The column is nullable so existing evidence records remain valid.

Applied escalations table

Adds:

exile_escalations

The unique constraint covers:

escalatable_type
escalatable_id
threshold_points

🧪 PHPUnit Coverage

Additional PHPUnit coverage was added for:

  • Transaction rollback
  • Audit write failures
  • After-commit event contracts
  • Queued notification contracts
  • Custom notification classes
  • Configurable mail content
  • Combined-ban any behavior
  • Combined-ban all behavior
  • Invalid combined-ban configuration
  • Evidence checksum creation
  • Evidence tampering detection
  • Missing evidence files
  • Legacy evidence records
  • Escalation reservations
  • Escalation deduplication
  • Highest-threshold selection
  • Disabled escalation
  • Default strike expiration

📚 Documentation

The documentation has been expanded and refreshed with icon-led headings.

Updated documentation includes:

  • Installation
  • Configuration
  • Architecture
  • Bans
  • IP, network, and device enforcement
  • Restrictions and shadow bans
  • Warnings, strikes, and escalation
  • Appeals
  • Evidence
  • Middleware
  • Events, notifications, and audit history
  • Commands and scheduling
  • Customization
  • Security
  • Testing
  • Release checklist
  • Upgrade guide
  • Contribution guide

✅ Supported Versions

  • Laravel 12 with PHP 8.2+
  • Laravel 13 with PHP 8.3+

🚀 Installation

Install Laravel Exile through Composer:

composer require eloquent-works/exile

Install the package and run migrations:

php artisan exile:install --migrate

Install and publish customizable notification templates:

php artisan exile:install --migrate --views

⬆️ Upgrading from v1.0.0

Update the package:

composer require eloquent-works/exile:^1.1

Publish the new migrations:

php artisan vendor:publish --tag=exile-migrations

Run migrations:

php artisan migrate

Publish the optional notification templates:

php artisan vendor:publish --tag=exile-views

Clear cached configuration and views:

php artisan optimize:clear

Review the new configuration options:

'security' => [
    'combined_ban_match' => 'any',
],
'notifications' => [
    'enabled' => false,
    'channels' => ['mail'],
    'issued' => true,
    'revoked' => true,
    'expired' => true,
    'fail_silently' => true,
],

Existing evidence records will have a null checksum until they are backfilled or replaced.

🧰 Quality Checks

Before deploying, run:

composer validate --strict
composer format
composer analyse
composer test

Or run the complete quality suite:

composer quality

❤️ Thank You

Thank you for using Laravel Exile.

Feedback, bug reports, feature requests, documentation improvements, and pull requests are always welcome.