laravel-chronicle/filament
Filament plugin for Laravel Chronicle that surfaces audit logs in your admin panel. Browse, search, filter, and inspect recorded model events and field-level changes, with a dedicated resource for reviewing activity history and debugging data updates.
Install the panel package alongside the core ledger and register the plugin on a Filament panel:
composer require laravel-chronicle/filament
public function panel(Panel $panel): Panel
{
return $panel
->plugin(ChroniclePlugin::make());
}
Look at three things before you let anyone near it:
verifyEntry, verifySegment, verifyChain, verifyAnchor, verifyAllAnchors, exportLedger, verifyExport, downloadLatestExport, complianceReport, downloadLatestReport, eraseSubject.->visible() calls — these are your authorization surface, one per capability.First use case: open the resource, run verifyChain over a segment, then exportLedger and pull the artifact via downloadLatestExport. Before that, map each gate to a role in your app so the panel isn't gated on the package defaults.
Upgrading to 1.5.0 is a plain composer update laravel-chronicle/filament. No API or config changes; authorized callers see identical behaviour.
One gate, two enforcement points. 1.5.0 makes every gated action re-check, at execution, the same gate its button visibility already used. That is the pattern to preserve: define a single Gate::define('chronicle-export', ...) (or policy method) and consume it for both ->visible() and the execution-time check. Two separate predicates will drift, and drift is exactly the failure mode this release closes.
Keep verification cheap and scheduled. Layer the verification actions — verifyEntry for a single hash, verifySegment for a range, verifyChain for the full chain, verifyAnchor for one external anchor, verifyAllAnchors for the set. Scheduled full-chain verification plus a dashboard widget is the common shape; the granular actions are for triage when the aggregate check fails.
Treat exports and reports as asynchronous. exportLedger and complianceReport do not return data synchronously — they enqueue work, and downloadLatestExport / downloadLatestReport fetch the artifact later. Drive them from a queued context or accept the two-step UX. Note the pre-1.5.0 exposure here was limited to enqueuing work an unauthorized caller couldn't see, because the export runs through a queued job rather than returning decrypted data to the caller.
Erasure is the one write path. eraseSubject was already enforcing execution-time authorization before this release and is unchanged. Treat it as privileged regardless: it is the only action that mutates anything, and it warrants an approval step and its own audit trail in your application.
Custom actions are your responsibility. If you register your own actions against the resource, 1.5.0 does not cover them. Give each one an execution-time gate check, and write a denial test for it — the structural tripwire in ExecutionTimeGuardTest pins the package's action set, not yours.
Mirror the enforcement test for your own code. The package's ExecutionTimeGuardTest is a good template: reach the real handler while bypassing the visibility guard exactly as a crafted request would, force the gate off, and assert (a) a denial notification was sent and (b) no work happened — nothing verified or recorded, no job dispatched, no report written, no proof entry appended. In your own tests that means Notification::fake(), Queue::assertNothingPushed(), Storage::assertMissing(), and a ledger entry count that did not move.
Copy the tripwire idea. Pin the exact set of actions you expect to be gated in a test. When someone adds an action and forgets the denial check, the test fails at the right moment instead of after the fact.
Scope gates per tenant. Where the panel is multi-tenant, the gate should resolve through a policy that scopes to the current tenant, so a crafted invocation referencing another tenant's entry fails at execution rather than relying on the record not being listed.
->visible() is not authorization. It controls whether a button renders; it does not stop a crafted request that invokes the handler directly. This is the entire substance of 1.5.0. Never treat a hidden button as a security boundary.downloadLatestExport and downloadLatestReport are in the gated set. Don't lean on unguessable storage URLs as your access control; keep the gate and the disk permissions aligned.Gate::define(..., fn () => false) override or a Gate::before returning false; assert denial and absence of side effects, since a passing notification assertion alone doesn't prove nothing happened.How can I help you explore Laravel packages today?