Comprehensive moderation-enforcement tools for Laravel applications.
Laravel Exile supports account, IP, CIDR network, and device bans; temporary and permanent enforcement; warnings and strike escalation; login, posting, read-only, and shadow restrictions; appeals, evidence, moderator tracking, audit history, events, notifications, middleware, and scheduled maintenance.
$user->ban(
reason: 'Repeated harassment',
expiresAt: now()->addDays(7),
moderator: $moderator,
);
$user->strike(
reason: 'Spam',
points: 3,
);
$user->restrict(
RestrictionType::Posting,
reason: 'Posting cooldown',
);| Package version | PHP | Laravel / Illuminate |
|---|---|---|
| Current | ^8.2 |
^12.0 || ^13.0 |
Composer automatically resolves compatible Laravel and Illuminate versions for the consuming application.
composer require eloquent-works/exile
php artisan exile:install --migratePublish the customizable notification templates during installation:
php artisan exile:install --migrate --viewsAdd the Bannable trait to the account model:
<?php
namespace App\Models;
use EloquentWorks\Exile\Traits\Bannable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Bannable;
}Configure a dedicated hash key:
EXILE_HASH_KEY=base64:replace-with-a-dedicated-random-keyDo not rotate this key casually. Existing IP and device hashes will no longer match after rotation.
- Account, exact-IP, CIDR network, device, and combined bans
- Configurable
anyor strictallmatching for combined bans - Temporary and permanent enforcement
- Transactional enforcement writes and after-commit side effects
- Login, posting, read-only, and shadow restrictions
- Warnings, strike points, and automatic escalation
- Appeals with approval, denial, withdrawal, and automatic revocation
- Evidence uploads with SHA-256 integrity checksums
- Moderator attribution, internal notes, metadata, and audit history
- Queued ban notifications with replaceable notification classes
- Publishable Markdown mail templates
- Middleware, expiration processing, and retention pruning
- Configurable models, table names, categories, and aliases
Block active account, IP, network, and device bans:
use Illuminate\Support\Facades\Route;
Route::middleware(['auth', 'exile'])->group(function (): void {
Route::get('/dashboard', DashboardController::class);
});Block a restricted action:
Route::post('/posts', StorePostController::class)
->middleware(['auth', 'exile', 'exile.allowed:posting']);Mark shadow-restricted requests without rejecting them:
Route::post('/comments', StoreCommentController::class)
->middleware(['auth', 'exile.shadow']);
$shadowed = (bool) request()
->attributes
->get('exile.shadowed', false);$ban = $user->ban(
reason: 'Repeated harassment',
expiresAt: now()->addDays(7),
moderator: $moderator,
category: 'harassment',
internalNotes: 'Case EX-1042',
metadata: [
'case_number' => 'EX-1042',
],
);
$user->isBanned();use EloquentWorks\Exile\Facades\Exile;
Exile::banIp(
'203.0.113.10',
reason: 'Automated abuse',
);
Exile::banNetwork(
'203.0.113.0/24',
reason: 'Abusive network',
);
Exile::banDevice(
'application-device-token',
reason: 'Ban evasion',
);
Exile::banAccountAndIp(
account: $user,
ipAddress: request()->ip(),
reason: 'Ban evasion',
);Register a device observation without storing the raw token:
$user->registerDeviceFingerprint(
fingerprint: $request->header('X-Device-Fingerprint'),
ipAddress: $request->ip(),
label: 'Chrome on Windows',
);use EloquentWorks\Exile\Enums\WarningSeverity;
$user->warn(
reason: 'Please review the community rules.',
severity: WarningSeverity::High,
);
$user->strike(
reason: 'Spam',
points: 3,
category: 'spam',
);
$user->activeStrikePoints();Escalation thresholds are configured in config/exile.php.
use EloquentWorks\Exile\Enums\RestrictionType;
$user->restrict(
RestrictionType::Posting,
reason: 'Posting cooldown',
expiresAt: now()->addDay(),
);
$user->restrict(RestrictionType::ReadOnly);
$user->restrict(RestrictionType::Login);
$user->restrict(RestrictionType::Shadow);
$user->isRestricted(RestrictionType::Posting);
$user->isShadowBanned();use EloquentWorks\Exile\Enums\AppealStatus;
use EloquentWorks\Exile\Facades\Exile;
$appeal = Exile::submitAppeal(
$ban,
$user,
'I believe this enforcement was issued in error.',
);
Exile::resolveAppeal(
$appeal,
AppealStatus::Approved,
$reviewer,
'Appeal accepted.',
);Approving an appeal revokes the related ban.
Attach an existing stored file:
$evidence = Exile::attachEvidence(
subject: $ban,
disk: 'private',
path: 'moderation/case-1042/report.pdf',
originalName: 'report.pdf',
uploadedBy: $moderator,
);Or store an uploaded file through Exile:
$evidence = Exile::storeEvidence(
$ban,
$request->file('evidence'),
$moderator,
);Exile::revokeBan($ban, $moderator);
Exile::revokeRestriction($restriction, $moderator);
Exile::revokeStrike($strike, $moderator);Records remain available as moderation history until explicitly pruned.
php artisan exile:expire
php artisan exile:prunePruning is disabled by default. Enable it in configuration or explicitly force a retention period:
php artisan exile:prune --force --days=365Notifications are disabled by default. Enable them in config/exile.php:
'notifications' => [
'enabled' => true,
'channels' => ['mail'],
],The affected model must support Laravel notifications. Database notifications also require Laravel's notifications table.
BanIssuedBanRevokedBanExpiredRestrictionIssuedRestrictionRevokedStrikeIssuedWarningIssuedAppealSubmittedAppealResolved
Run the complete quality pipeline:
composer qualityOr run each check separately:
composer format
composer analyse
composer testValidate Composer metadata before publishing:
composer validate --strictcomposer analyse must complete with zero PHPStan errors. Do not disable valid findings globally merely to make the command pass. Prefer correcting native types, generic PHPDoc annotations, impossible comparisons, redundant instanceof conditions, and iterable value types.
See Testing and Quality and PHPStan Remediation.
Full documentation is available in the docs directory:
- Documentation index
- Installation
- Configuration
- Architecture
- Bans
- IP, Network, and Device Enforcement
- Restrictions
- Warnings, Strikes, and Escalation
- Appeals
- Evidence
- Middleware
- Events, Notifications, and Audit History
- Commands and Scheduling
- Customization
- Security
- Testing and Quality
- PHPStan Remediation
- Release Checklist
Keep EXILE_HASH_KEY private. Exile uses keyed HMAC hashes for IP and device matching. Human-readable IP addresses and CIDR ranges are encrypted at rest using Laravel's encryption system.
Do not use device fingerprints as a sole identity signal. Treat them as one moderation indicator alongside account history, IP context, and human review.
Security vulnerabilities should be reported privately according to SECURITY.md, not through a public issue.
See CONTRIBUTING.md and CODE_OF_CONDUCT.md.
Built by Eloquent Works.
Laravel Exile is open-source software licensed under the MIT License.