redfieldchristabel/filament-blackbox
A Laravel/Filament package that adds a black-box recorder to your admin panel, capturing requests, user actions, and errors so you can inspect and debug what happened. Designed for Filament apps, it helps trace issues and review activity without leaving your dashboard.
filament-blackbox is a Filament plugin that records model activity (create/update/delete) into a blackbox table and ships a Filament resource for browsing that history. It is still pre-1.0, so treat the source as the documentation.
Minimal setup:
composer require redfieldchristabel/filament-blackbox
php artisan vendor:publish --tag="filament-blackbox-migrations"
php artisan migrate
Register the plugin on the panel you want the audit UI in:
// app/Providers/Filament/AdminPanelProvider.php
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugins([
\RedfieldChristabel\FilamentBlackbox\BlackboxPlugin::make(),
]);
}
Look at these first, in this order:
database/migrations/* — the schema tells you exactly what is captured (subject morph, causer, event type, payload/diff columns). This is the real API surface.config/filament-blackbox.php (if published) — model list, causer resolution, retention.BlackboxResource — how entries are listed and whether a subject link is resolved.First use case: enable recording on one model you already trust, edit a record through a Filament form, then open the Blackbox resource and confirm the entry renders with a correct causer and subject link. Then — new in v0.0.10 — soft-delete that same record and confirm the entry is distinguished from a hard delete and that the subject still resolves.
Attach recording per model, not globally. Opting in model-by-model keeps the table small and makes it obvious what is audited:
class Invoice extends Model
{
use SoftDeletes;
use \RedfieldChristabel\FilamentBlackbox\Concerns\LogsBlackboxActivity;
}
or, if the package exposes it that way, list models in config:
'models' => [
App\Models\Invoice::class => ['create', 'update', 'delete', 'restore'],
],
Distinguish soft delete from hard delete explicitly. As of v0.0.10 the package is SoftDeletes-aware, so your delete() and forceDelete() produce different entries. Model your UI around that: a trashed invoice should show "deleted", then "restored", then possibly "force deleted" as three separate events in one timeline rather than one confusing delete row.
Use it as the restore trail, not the restore mechanism. The common Filament workflow is: archived list page → view blackbox history → restore. Let Filament's RestoreAction/ForceDeleteAction do the work and let Blackbox be the read-only record of who did it. Do not drive restores off blackbox rows; they are a log, not a replica of the model.
Scope the resource to your panel's tenancy. In a multi-tenant panel, add a modifyQueryUsing() (or the resource's getEloquentQuery()) restriction on the blackbox table so a tenant admin cannot page through another tenant's activity. The blackbox table typically stores subject_type/subject_id but no tenant key — you will usually need to join or store tenant_id yourself.
Treat the causer carefully in jobs and console commands. Recording triggered from a queued job has no auth()->user(). Store an explicit causer when you dispatch (Auth::login() hackery is not worth it) or accept a null causer and render it as "System".
Assert on it in tests. Because it is just a table, feature tests can assert directly:
$this->actingAs($admin)->put(...);
$this->assertDatabaseHas('blackbox', [
'event' => 'updated',
'subject_id' => $invoice->id,
]);
This is the cheapest way to verify wiring after upgrading, since the package's own test surface is thin at 0.0.x.
Pair with a retention job. Add a scheduled blackbox:prune (or a plain DB delete) early — audit tables grow monotonically and the package is unlikely to ship retention in 0.0.x.
0.0.x there is no BC promise; 0.0.9 → 0.0.10 already changed delete semantics. Use "redfieldchristabel/filament-blackbox": "0.0.10" and review each diff, not just the release notes.match.morphTo, which excludes soft-deleted parents. Resolve with withTrashed() in the resource's relation/getSubject(), otherwise rows for soft-deleted invoices render blank exactly when you need them most.DB::table()->update(), mass deletes outside Eloquent, and onDelete('cascade') foreign keys produce no blackbox entries. If you rely on the log for compliance, that gap matters — be explicit about it in your own docs.forceDelete() on a soft-deletable model is a different event. Make sure your UI and any match on the event column handle forceDeleted (and restored), not just deleted. v0.0.10 is the release that makes this distinction meaningful.vendor:publish will not overwrite them; a new migration may be needed for added columns. Check database/migrations/ after upgrading instead of assuming you are current, and never --force on a populated audit table.->plugins([...]) and not just the service provider.(subject_type, subject_id, created_at) and (causer_type, causer_id) are worth adding in your own migration. Retro-fitting them on a million-row audit table is a deployment of its own.Model::getEventDispatcher()->hasListeners('eloquent.updated: App\Models\Invoice'); verify config is loaded via config('filament-blackbox'); and check that the panel's auth guard produces the causer you expect. Most "it's not logging" reports are a missing trait or a causer that resolves to null under a non-default guard.How can I help you explore Laravel packages today?