tomatophp/filament-fcm
TomatoPHP's Filament FCM brings Firebase Cloud Messaging into your Filament admin panel. Send push notifications to devices, manage FCM credentials and notification templates, and monitor delivery, all from a clean, native Laravel interface.
Architecture fit. tomatophp/filament-fcm is a thin adapter, not a messaging platform. It binds Firebase Cloud Messaging into two places a TPM should care about: the Laravel notification channel layer (so push becomes another via() channel alongside mail and database) and the Filament admin panel (so operators get a manual send/test surface without a deploy). For a product already on Filament + Laravel notifications, that is the correct shape of dependency: small, substitutable, and co-located with where alert logic already lives. The strategic value is not "we have push" — it is "we have one alert model with multiple delivery surfaces." That is the argument that justifies the dependency in a design review, and it should be stated explicitly rather than assumed.
Integration feasibility. Feasible for a small team with an existing Filament panel and an authenticated API for device registration. The work decomposes into four bounded pieces: (1) credential setup in Firebase console — service-account JSON, APNs auth key for iOS, VAPID certificate for web; (2) a token registration endpoint that upserts against the authenticated user; (3) one notification class wired to the push channel and dispatched via a queued job; (4) optional Filament table action for manual sends. Pieces 1 and 2 are where the calendar time goes; the package itself is hours. The package does not solve credential provisioning, token lifecycle, or delivery analytics — those are ours regardless of vendor choice.
Technical risk. Ranked, with the TPM's mitigation:
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Legacy server-key auth instead of FCM HTTP v1 | Medium | Blocking — legacy API is shut down, failures are opaque 401s | Read the config and HTTP client code path before committing to the spike. Go/no-go gate. |
| Filament major-version coupling | High (over time) | Panel breaks or silently renders blank on upgrade | Pin exact version; add "send one push end-to-end" to the Filament upgrade checklist |
| Single-maintainer abandonment (20 stars, 0 dependents) | Medium | No upstream fixes; forced fork or replacement | Keep a fork with upstream remote; absorb behind our own PushChannel interface |
| Stale/rotated tokens silently degrading delivery | High | Alerts stop arriving with no error | Prune on UNREGISTERED, nightly sweep by last_seen_at, alert on send-error rate per platform |
Transitive sibling tomatophp/filament-* packages colliding |
Low–Medium | Duplicate service providers, non-deterministic channel resolution | composer why / composer show --tree review before merge |
| Secret handling — service-account JSON in repo or image | Low | Credential compromise | File path via secret manager, never inline JSON in env |
Key questions for the engineering owner before approval:
filament/filament constraint on the tag we intend to install, versus our current major?user_id, polymorphic notifiable, or neither — and does it carry a tenant column we need for multi-panel?kreait/firebase-php / laravel-notification-channels/fcm? A known wrapper means we can swap the adapter without touching our notification classes.PushChannel interface on day one? (Non-negotiable from my side — it is the cheapest insurance in the whole plan.)Verdict. Approve as a bounded pilot, not as platform infrastructure. The decision this package actually informs is "buy (MIT, free) a thin FCM adapter" versus "build a thin FCM adapter" — not "buy an engagement suite." Reframing it that way is the most useful thing a TPM can do here: license cost is zero, ownership and lifecycle risk are the real line item.
Stack fit. Requires Laravel with a running queue worker, Filament (matching major), and PHP at or above the package floor. The only genuinely non-negotiable external dependency is FCM itself plus platform credentials: APNs auth key for iOS, VAPID/web-push certificate for web. Neither is optional and neither can be provisioned by the package. If the target workflow is "approver gets pinged on their phone," iOS credential setup is on the critical path and should be scheduled as engineering work, not as a footnote.
Migration path. Greenfield for most teams — there is no incremental migration from an incumbent push vendor here. If we already use laravel-notification-channels/fcm or kreait/firebase-php, the sequencing is inverted: adopt the PushChannel seam first and treat this package as a second adapter, so the Filament UI comes as an add-on rather than a replacement. If we have no push at all, the path is: credentials → token endpoint → one notification class → Filament action, in that order, each shippable independently.
Compatibility. Three surfaces to verify: the Filament major (plugin breakage is the top cause of failure and often presents as a blank widget rather than an error), the illuminate/* constraints on our Laravel version, and sibling tomatophp/filament-* packages auto-discovered by Composer. Pin exactly — no caret on a single-maintainer plugin — and register the plugin in a non-default panel during validation, since single-panel assumptions are a common plugin defect and most production installs run at least two panels.
Sequencing (four gates, roughly two to three weeks elapsed, mostly credential debugging):
composer.json require block, the published config, and the channel class. Kill the initiative here if it is legacy server-key based or Filament majors mismatch. This gate is cheap and prevents the expensive mistake.curl before touching package code — this isolates FCM from the adapter and is the single highest-value debugging investment.POST /devices endpoint, updateOrCreate on (user_id, token) with a unique index, platform and last_seen_at. User identity derives from the bearer token, never from the request body — a token registration endpoint without user binding is an account-takeover primitive. Ship with the pruning job, not after it.Scope discipline. Use the Filament UI as a manual override — test sends, maintenance announcements, resends — not as a campaign authoring tool. It is weak-to-useless for segmentation, A/B testing, drip sequences, open-rate analytics, and multi-channel orchestration, and it will not fail loudly on any of those; it will simply not do them and someone will ask later. Flag that boundary in the PRD now so it is a stated limitation rather than a discovered gap. Keep payloads thin (~4KB limit, data values must be strings — an integer ID fails serialization) and keep PII off lock screens: send "You have a pending approval" plus a deep link, let the app fetch details after auth.
Product framing to carry into design review. This package's real product contribution is roadmap sequencing. It converts "notify the on-call engineer on their phone" from a platform initiative into a bounded feature that can plausibly land this quarter. It is also the cheapest possible experiment to test whether push alerting changes operator behavior at all — a two-week pilot on one workflow yields evidence for or against a broader mobile companion app at near-zero cost. That experiment, not the push plumbing, is the deliverable I would fund.
Maintenance. The code surface is small; the operational surface is not. Ongoing obligations: Filament upgrade regression (one end-to-end push per major bump), token table hygiene, credential rotation (APNs keys expire — an expired key silently zeroes iOS delivery while Android keeps working), and dependency-tree review before any Composer change. Assign a named owner and put "push delivery rate per platform" on an existing ops dashboard. Ownership cost, not license cost, is the honest line item.
Support. Expect the ticket queue to be dominated by credentials, not by the package. Establish a fixed diagnostic order and publish it so support does not improvise: (1) does the token exist and belong to the right user; (2) is the service-account JSON loading with correct permissions; (3) does a raw HTTP v1 send succeed outside the package; (4) only then inspect adapter code. Two recurring self-inflicted failures belong in the runbook: config:cache retaining old credentials after an .env change (always config:clear), and "push is broken" that is actually no queue worker running — check failed_jobs first.
Scaling. Fine at back-office volume: per-user, per-device sends to staff. Three scaling behaviors matter as volume grows: batch per user (five stale devices should be one job, not five inline HTTP calls in a Livewire request), use topics for cohort broadcasts and tokens for individuals — mixing them is how the wrong group gets notified — and feature-flag the send path so push can be disabled globally in thirty seconds when error rates spike, without a revert deploy. Beyond that, this package stops scaling: no segmentation, no throttling policy, no delivery analytics. When volume or targeting complexity crosses that line, the correct move is to keep the PushChannel seam and swap the adapter, which is why the seam is non-negotiable up front.
Failure modes and observability. The dangerous ones are silent, not loud:
UNREGISTERED plus a nightly sweep of last_seen_at, the table grows monotonically and every send wastes work on dead rows. Client must re-register on token refresh.tenant_id the package does not ship, added in our own migration and applied to the routeNotificationForFcm query.Ramp-up. Low for a Laravel-fluent engineer: hours to first send once credentials exist, one to two days to a production-shaped queued notification with pruning. The schedule risk is concentrated in Firebase console and APNs provisioning, not in PHP. Budget half a day for the first device-to-device loop and expect most of it to be credential debugging — that expectation should be set with stakeholders in advance so an afternoon of 401s does not read as a slipping milestone.
Exit strategy and ownership. Keep a fork with upstream as a Git remote and point Composer at our VCS repo. Combined with the PushChannel interface and a FakePushChannel in tests (tests must never hit FCM), that converts "upstream went quiet" from an incident into a composer update. Given the small surface and 20-star, single-maintainer, zero-dependent risk profile, that insurance is the difference between adopting a package and inheriting a liability.
Recommendation. Fund as a two-to-three-week pilot against one operational workflow, with Gate 0 — the auth-model and Filament-version audit — as an explicit stop point. Success criteria: one real alert class delivering to a held device with the app backgrounded, token pruning running on schedule, a per-platform error-rate alert live, and a PushChannel seam in place. If those four land, push alerting becomes a reusable capability; if the pilot shows operators do not change behavior, we have spent two weeks to learn that before funding a mobile app. Either outcome is a good trade.
How can I help you explore Laravel packages today?