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 Activity Timeline Laravel Package

laboiteacode/filament-activity-timeline

A semantic activity timeline for Filament that turns raw spatie/laravel-activitylog entries into clear, localized business sentences. Show "Client: ACME France -> Dupont Conseil" instead of "customer_id: 14 -> 27". Drop-in widget, page, or infolist support, with customizabl...

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Package: laboiteacode/filament-activity-timeline — MIT, Laravel/PHP, Filament panel plugin. Renders activity-log rows (attribute diffs such as customer_id: 14 -> 27) as localized, human-readable business events (Client: ACME France -> Dupont Conseil).

Assessment basis: v1.0.2 (security release). Prior assessment was against the pre-1.0.2 line; the changes below are material to the risk section and to the version floor we should enforce.

Architecture fit

  • Correctly shaped as a Filament plugin: Composer package, auto-discovered service provider, panel registration via ->plugin(...), publishable config and translations. This is the standard, low-friction extension point for Filament admin panels — no core patching, no forked UI.
  • It is a presentation layer over an existing activity log (almost certainly spatie/laravel-activitylog or a compatible schema). That is architecturally the right call: it does not own persistence, so adoption is reversible and does not create a new system of record.
  • The differentiator — semantic/label mapping plus i18n — is genuinely the gap in the underlying package. Raw properties JSON is unusable for business users and is the usual reason activity logs get built and then abandoned.
  • New in v1.0.2: the widget is now hardened at the Livewire boundary — every public property is #[Locked], the visible window is bounded by pagination.max_per_page (default 500), and filterByEvent() only accepts an event exposed as a filter tab. This does not change the architecture, but it converts the widget from "a view with unvetted public state" into "a view with an explicit, configurable trust boundary." That boundary is now a config surface we own.

Integration feasibility

  • Low technical effort if the panel already runs Filament and already writes activity rows. Effort is dominated by configuration (label maps, attribute humanizers, translation catalog, now plus the max_per_page policy), not by engineering.
  • Blocked or degraded if the team has no activity logging in place, uses a bespoke audit table, or logs only to files/observability tooling. In that case the real project is "introduce structured activity logging," and this package is the last 20% of it.
  • Verify peer requirements before committing: Filament major version, PHP version, Laravel version, and whether spatie/laravel-activitylog is required or optional.
  • Upgrade friction is near zero for a standard install (no code change required), but two narrow cases need checking: (a) any place we set widget properties from our own JS via $wire.set(...) will now throw CannotUpdateLockedPropertyException; (b) if a saved link or bookmark filters by an event that is not exposed as a tab, that filter silently stops returning that event. Both are easy to detect and easy to fix, but neither is caught by a smoke test — grep for $wire.set and review any hardcoded timeline URLs.

Technical risk

  • Adoption/vendor risk remains the dominant risk, but v1.0.2 is the first evidence against it. A single-maintainer package shipping a self-initiated, well-scoped security release with an explicit upgrade note and an exception-propagation fix is behaving better than most packages in this size class. One data point is not a track record — keep the assessment of "pre-community, no third-party production usage, high bus-factor" and treat responsiveness as improving but unproven.
  • Security risk is now partially mitigated but not eliminated, and the disclosure is instructive. The pre-1.0.2 flaw was real and not theoretical: a crafted Livewire request could replace presentationOverrides, which dropped the hiddenAttributes the widget was mounted with and exposed those attributes in the change list. Attributes listed in the hidden_attributes config were never affected, and the record scope was never swappable. TPM implications:
    • Hard version floor: >=1.0.2. No exception. If we already run an earlier version, this is a priority patch, not a routine bump.
    • If pinned below 1.0.2 for any reason, the compensating control is to declare hidden attributes in the hidden_attributes config rather than passing them as mount-time overrides — config-driven suppression was outside the vulnerability.
    • The general lesson generalizes to every Livewire/Filament plugin we install: any unlocked public widget property is browser-writable state. Add "audit public widget properties" to the plugin intake checklist, not just to this package's review.
    • The vulnerability was disclosed and fixed rather than silently patched, which is the behavior we want; it also means we should be watching this repo's releases rather than waiting to hear about issues.
  • Framework coupling risk. Filament ships breaking majors on a regular cadence; a small plugin must chase them. Budget for a version-lock step at every Filament upgrade, and confirm the package's constraint range explicitly rather than assuming.
  • Render-time resolution semantics. If labels are resolved at display time, historical events re-render under today's label map. That is usually desirable, but it means the timeline is not an immutable audit record — a compliance-relevant distinction if this is ever positioned as an audit trail. v1.0.2 does not change this, and nothing in the release suggests the maintainer intends the tool as an audit system of record.
  • Data-volume risk is now partially bounded by the package. The unbounded page size — previously growable from the "load more" button or a crafted request — is capped at 500 by pagination.max_per_page. That is a meaningful denial-of-service and query-cost mitigation. It does not address the underlying table growth or the N+1 cost of eager-loading causer/subject morphs; index and retention work is still ours. Note the escape hatch: a per_page configured above the ceiling is honoured in full, so our own config can re-open the hole — set both deliberately.
  • Authorization surface. A readable timeline is an information-disclosure feature. Field-level changes (pricing, PII, role assignments) become visible to anyone who can see the widget unless scoping is deliberate. Two changes here:
    • Better: AuthorizationException, AuthenticationException, ValidationException, HttpResponseException and HttpExceptionInterface now propagate to the exception handler. A denied timeline answers 403 instead of masquerading as "this record has no history." The old behavior was a silent-failure mode that could hide a misconfigured policy indefinitely — this is a genuine correctness improvement.
    • New operational wrinkle: a 403 raised inside a dashboard widget may take down the surrounding page rather than just the widget, depending on how our panel handles widget exceptions. Verify before placing the timeline anywhere a partially-privileged role can see it. Any custom source that throws an authorization error will now surface as 403 rather than the error panel — intended, but a visible behavior change for anyone who built custom sources.

Key questions for the team

  1. Do we already persist activity rows for the models we want to display, with subject_type/causer populated?
  2. Which Filament major are we on, and does the package declare support for it?
  3. Are we on >=1.0.2 everywhere, and if not, what is the patch window? (If we cannot patch immediately, have we moved hidden attributes into config as the compensating control?)
  4. Is this a UX feature or an audit/compliance artifact? The answer changes whether render-time localization is acceptable.
  5. Who owns the label/translation catalog, and what is the process when a new model or attribute is logged?
  6. Who owns pagination.max_per_page and the hidden_attributes config — and do we have a rule preventing local config from raising the ceiling above the package default?
  7. What is our exit cost if the maintainer stops shipping? (MIT + small surface area = forkable, but only if we keep our customization behind an adapter.)
  8. Has anyone on the team used $wire.set(...) against this widget, and does any internal link filter by an event that is not an exposed tab?

Fit verdict: Good conceptual fit, weak-but-improving supply-side maturity. The security release is a positive signal on maintainer responsiveness and a reminder that this is a dependency we must track rather than install and forget. Appropriate for an internal admin panel at >=1.0.2 where the team accepts ownership; still not appropriate as a load-bearing dependency in a client-facing or regulated-audit context without a fork-and-own decision.

Integration Approach

Stack fit

  • Assumes Laravel + Filament + Livewire + Tailwind, with an activity log backing store. This is the default stack for Filament admin panels, so no new runtime, no new service, no new infrastructure.
  • No database migration of its own is expected (it reads existing activity rows); confirm during the spike, because a package-supplied migration would touch a shared, high-write table.
  • i18n via Laravel translation files aligns with existing localization practice — the incremental cost is authoring the catalog, not building a translation mechanism.
  • v1.0.2 adds one config key we should own explicitly: pagination.max_per_page (default 500). If we publish the config file, we must add it deliberately — absence falls back to 500, which is the correct default, so the risk is only in an over-eager local override.

Migration path

  • Nothing to migrate in the data sense. This is additive view-layer configuration; existing activity rows render immediately once mapping exists.
  • Upgrading an existing install to 1.0.2 is a no-op for a standard setup, but treat it as a security patch with a verification step rather than a routine bump: confirm the lockfile resolves >=1.0.2, grep for $wire.set against widget properties, and check any hardcoded filterByEvent URLs against the exposed filter tabs.
  • Historical rows will render degraded (raw keys or IDs) wherever a mapping is missing or a related record has since been deleted. Plan for a deliberate "unknown attribute" fallback so the timeline never looks broken.
  • If we run a bespoke audit table, do not force an adapter in phase one — either normalize to the supported schema or defer the package.

Compatibility checks (do these before any code)

  • Filament major/minor constraint and PHP constraint against our matrix.
  • Resolved version is >=1.0.2. Verify in composer.lock, not just in the manifest.
  • Any $wire.set(...) calls against ActivityTimelineWidget properties (these will now throw CannotUpdateLockedPropertyException).
  • Any custom source that throws AuthorizationException — expected 403 now, not the error panel.
  • Underlying activity-log package version and schema (properties JSON support on our DB — MySQL/Postgres vs SQLite differences).
  • Whether the package's Filament assets/views need publishing and whether that conflicts with our theme build.
  • Localization and config file publishing and override path, so our catalog and our max_per_page survive package upgrades.

Sequencing

  1. Inventory (0.5d): list models with activity logging, their attributes, and who should see them. This is product work, not dev work, and it is the actual cost driver.
  2. Version/security gate (0.25d): confirm >=1.0.2, confirm no $wire.set usage, add the package to the dependency watch list. Do this first — it is cheap and it is the only step where being wrong has a disclosure consequence.
  3. Spike (1–2d, non-prod): install on a scratch panel, wire one resource, evaluate render performance on a realistically sized activity table.
  4. Label catalog design (1–2d, PM + dev): agree event naming and translation keys per model. This is where business value is created or lost.
  5. Authorization design (0.5d): scope the widget per role; decide whether sensitive attributes need suppression via config hidden_attributes; decide the max_per_page policy; test the 403 path with a restricted role and confirm it does not take down the surrounding page.
  6. Pilot (1 resource, 1 team): measure whether support/ops actually use it.
  7. Rollout + hardening: indexes, pagination limits, pruning policy, optional fork registered in our dependency register.

Hedging

  • Keep all mapping and translation customizations in our own config/translation files, never in the package's published views. Keep the plugin registration behind a single provider line so removal is a one-line revert.
  • Keep the version floor in our dependency policy rather than in tribal knowledge, and subscribe to the repo's releases — the 1.0.2 disclosure is the kind of thing we want to hear from the source, not from a scanner alert three weeks later.

Operational Impact

Maintenance

  • Small but non-zero and permanent. Expect an annual compatibility bump (Filament major, Laravel major) plus incidental fixes. At 0 dependents, we will not benefit from someone else finding those breaks first — though v1.0.2 shows the maintainer does find and fix some of them themselves.
  • New standing obligation: security-release tracking. Add composer audit / Dependabot coverage and a named owner for this package's advisories. The 1.0.2 fix was for a flaw our own review had flagged as a category risk (widget-level attribute disclosure), which means the class of issue is live, not hypothetical.
  • Mitigation: pin exact versions with a floor at >=1.0.2, add the package to the dependency review list, and pre-authorize a fork under our org if maintenance lapses. MIT makes this legally trivial; the cost is developer familiarity, not licensing.
  • The translation catalog is a living asset: every new logged model or renamed attribute needs a catalog entry, or the timeline silently regresses to raw keys.

Support

  • Community-only, no SLA, no vendor to escalate to. Internal ownership is mandatory — name a DBA/backend owner for schema questions, a product owner for event naming, and now a security owner for advisory triage.
  • Knowledge concentration risk is low (small surface) but real (one or two developers will understand the mapping conventions).
  • Security response capability: we can patch a Livewire property-lock issue ourselves in an afternoon if we have forked. That is a genuine mitigation — but only if the fork is current, which is the argument for forking at adoption rather than at incident.

Scaling

  • The bottleneck is the activity table, not the plugin. Enforce composite indexes on (subject_type, subject_id, created_at) and (causer_type, causer_id); paginate aggressively; cap the timeline depth shown in the UI.
  • pagination.max_per_page (500 default) now hard-bounds the window, which removes one unbounded-query path. It is a ceiling, not a tuning parameter — set per_page well below it for the default experience and let "load more" be the escape hatch.
  • Define a retention/archival policy up front. Append-only audit tables are the most common source of admin-panel latency at 12–24 months.
  • Widgets on dashboards multiply query cost by viewer count — avoid placing a timeline on high-traffic overview pages.

Failure modes to plan for

  • Unmapped attribute or model → raw key rendered. Fallback copy must be intentional, not accidental.
  • Missing translation → key leaks into the UI. Add a CI check that catalog keys resolve.
  • Deleted subject/causer → null morph, broken or blank label. Decide between "deleted record" placeholder and soft-delete retention.
  • Renamed/recycled IDs → labels resolve against current data and can misrepresent history. Relevant if the timeline is read as an audit record.
  • Timezone and locale drift → timestamps and number/date formatting diverge per viewer; confirm viewer-locale rendering is intended.
  • Authorization gap → sensitive field changes visible to under-privileged roles. Still the highest-severity failure mode. v1.0.2 removed the mount-time override bypass and turned silent denials into 403s, which materially narrows this — but the config-level hidden_attributes and role scoping are still ours to get right. Test with a restricted role before rollout.
  • Widget exception blast radius (new) → a propagated 403 from one widget may break the containing dashboard rather than degrading gracefully. Verify page-level behavior for partial-privilege roles.
  • Locked-property exceptions (new) → any of our own JS writing to widget state now fails loudly at runtime. Low severity, easy to miss in review.
  • Upstream abandonment → package stops tracking Filament. Detectable early via the compatibility matrix and the release feed; recoverable via fork.

Ramp-up

  • Developer ramp-up: hours, not days — Filament conventions plus a config file. A new contributor can ship a mapped timeline in under a day. Add an hour for the security-hardening concepts (locked properties, hidden attributes, max page size) so the next developer does not re-open them.
  • Product/ops ramp-up: the real cost. Event naming, translation authoring, and role scoping require product decisions; budget 3–5 days of combined effort for a first wave of 5–10 resources.
  • Total realistic pilot cost: ~1 week for one resource end-to-end including the authorization review and the >=1.0.2 verification step.

Recommendation: proceed with a time-boxed, single-resource pilot in an internal admin panel, contingent on the compatibility check, the existing-activity-log check, and a hard version floor of >=1.0.2. The security release improves the package's standing — it demonstrates a maintainer who finds and discloses — but it also confirms that this dependency needs active tracking rather than passive pinning. Do not adopt broadly, and do not position the output as an audit trail, until the maintainer's release cadence is observed over at least one Filament major cycle.

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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel