Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Blackbox Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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:

  1. database/migrations/* — the schema tells you exactly what is captured (subject morph, causer, event type, payload/diff columns). This is the real API surface.
  2. The plugin class — what it auto-registers (resource, widget, navigation group) and which config keys it reads.
  3. config/filament-blackbox.php (if published) — model list, causer resolution, retention.
  4. The 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.

Implementation Patterns

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.

Gotchas and Tips

  • Pin the version. At 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.
  • The soft-delete upgrade is the risky part. Existing rows written before v0.0.10 may not carry whatever discriminator now separates soft from hard deletes. If your history page branches on that column, backfill it or render old rows with a fallback ("deleted — type unknown") rather than letting them fall through a match.
  • Subject resolution returns null for trashed models. This is the single most common bug: the resource's subject link uses a plain 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.
  • Model events do not fire for everything. 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.
  • Republish migrations deliberately. If you already published migrations on 0.0.9, 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.
  • Registration order matters. Plugin registration, resource discovery, and any custom policy on the blackbox model interact; if the resource 404s or the nav item vanishes, first check that the plugin is in ->plugins([...]) and not just the service provider.
  • Index the table before it gets big. Composite indexes on (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.
  • Write-heavy forms slow down. Every save is an insert plus payload serialisation. For bulk-import screens, either disable recording or accept the cost — and check whether the package records inside or outside the transaction, since an exception in logging should not roll back the user's actual edit.
  • Debugging: confirm the trait/observer is actually attached with 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.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager