akyos/native-push
akyos/native-push is a Laravel package for sending native push notifications to mobile apps. It simplifies device token management and message delivery, making it easier to add iOS and Android push support to Laravel projects.
Architecture fit. akyos/native-push is a Laravel package built on a Symfony Notifier transport (native-push://default) that talks directly to APNs (HTTP/2, .p8 JWT) and FCM HTTP v1 (service-account OAuth2). Architecturally that means it replaces a vendor (OneSignal, FCM legacy wrapper, SNS, Pusher Beams) with in-house delivery. The value is clear: no third-party relay, no per-subscriber vendor fee, no token hand-off to a third party. The cost is equally clear: everything the vendor abstracted — credential rotation, connection reuse, retry/backoff, feedback-channel processing, payload shaping — becomes our responsibility or an unstated assumption about the package.
The coupling profile is the main architectural concern. The package is Doctrine-native (PushSubjectInterface + PushSubjectTrait on any Doctrine entity, PushDevice as a Doctrine entity) and frontend-native to Symfony UX (Stimulus controller, companion akyos/ux-native-cli). A conventional Laravel application is Eloquent-first and its front-end is typically Blade/Livewire/Inertia with Vite. Adopting this package therefore implies, at minimum, evaluating doctrine/orm + a Laravel-Doctrine bridge in the same app — a substantial and non-reversible dependency — or accepting a parallel persistence model for push devices alongside Eloquent users. Similarly, the Symfony Notifier transport sits beside, not inside, Laravel's Notification/NotificationChannel system; the integration seam between "Laravel notification" and "Symfony Notifier message" is the single most important thing to validate before commitment.
Integration feasibility. Installation is a single Composer require, and the surface area is small and legible: one transport, one entity, one trait, one interface, two HTTP endpoints, one Stimulus controller, four Artisan commands. That is a good sign for a v1.0.0 — it is opinionated rather than sprawling. The commands (check, test, send, devices) give us a credible path to a same-day technical spike: configure credentials, native-push:check, native-push:test, observe on a real device. check in particular suggests the author anticipated the most common failure (misconfigured .p8 / service account) — worth confirming what it actually validates.
The risk concentrates in three places: (1) the Notifier-in-Laravel seam, (2) Doctrine in an Eloquent app, (3) credential lifecycle for two providers with different auth models. None is a blocker, but each is a decision we should make deliberately rather than discover in staging.
Technical risk. Rated medium-high, driven less by code quality (unknown) than by blast radius and immaturity. Specific risks: direct-to-provider delivery means APNs 410/BadDeviceToken and FCM UNREGISTERED handling is our feedback loop — the notes claim "suppression des tokens morts," but the detection mechanism, timing, and whether it runs on send-failure only or via a scheduled sweep are unstated. The /native-push/devices endpoints are an authenticated-write surface for device tokens and, per the notes, phone numbers; default middleware, authorization policy, and rate limiting must be verified before exposure. "Réattribution automatique" of phone numbers implies a device can migrate between users — correct for shared/recycled handsets, but it is a privacy and account-takeover consideration that needs an explicit policy. Credential storage (.p8 files, service-account JSON) is high-sensitivity material that must come from a secrets manager, not .env in the repo.
Key questions for the maintainer / spike:
Notification facade, or must we call the Symfony Notifier directly? Is there a custom NotificationChannel?PushDevice and recipients?native-push:check and devices do, and how are dead tokens detected and purged (on-send vs. scheduled)?POST/DELETE /native-push/devices: default middleware, configurability, idempotency of re-registration.url() validation, per-platform payload overrides, batch/fan-out sends, APNs topic/bundle ID handling.Disposition: conditional go for a two-week spike; not an approved production dependency until the Notifier seam, the Doctrine requirement, and dead-token handling are demonstrated.
Stack fit. Good on the backend (PHP/Laravel is native ground for a Composer package), mixed at the persistence layer (Doctrine-first in an Eloquent world), and weakest at the front end (Stimulus assumes a Symfony UX asset pipeline; workable under Vite but not the path of least resistance for a Livewire/Inertia team). Treat the front-end controller as optional — the two HTTP endpoints are the actual contract, and we can call them from whatever client we already have.
Migration path. There is no incumbent to migrate from, so this is a greenfield capability rather than a replacement — which lowers switching risk but also means no fallback exists on day one. Recommended path:
.p8 + key ID + team ID + bundle ID, and an FCM service account, in the secrets manager. Wire config, run native-push:check, resolve only to non-production devices.PushMessage with url() and delivers to one iOS and one Android test device. This answers the Laravel/Symfony seam question definitively.POST/DELETE /native-push/devices before any client ships.Compatibility. Pin exact versions and verify the PHP/Laravel matrix against our current runtime before the spike — a Notifier-transport package will track Symfony Notifier versions, which can move independently of Laravel's supported range. Prefer explicit version constraints over ^ for the first adoption. akyos/ux-native-cli --notification is only useful if we standardize on that app-generation tooling; otherwise use url() as a plain deep link.
Sequencing. Backend send-path first, device registration second, client deep links third. Do not ship the registration endpoint before the authorization and retention policy is settled — a device table populated under the wrong ownership model is expensive to unwind.
Maintenance. We take on the maintenance of direct provider integrations: APNs certificate/key rotation, FCM service-account and OAuth token refresh, and API version tracking (APNs HTTP/2 and FCM v1 are stable but not frozen). We also inherit an immature dependency — a v1.0.0 from a single vendor, with a small ecosystem surface and no prior assessment baseline in our records. Realistically, plan to vendor-fork or wrap the transport if the maintainer's cadence diverges from ours; the Notifier transport is small enough that this is a viable hedge.
Support. Support burden shifts from "open a ticket with the push vendor" to "diagnose provider responses ourselves." That is a real capability investment: on-call needs to recognize APNs 403/ExpiredProviderToken versus BadDeviceToken, and FCM SENDER_ID_MISMATCH versus UNREGISTERED. Budget a runbook and one named owner. Vendor responsiveness to issues is unknown and should be judged on the spike, not the README.
Scaling. Two dimensions. Delivery volume: direct APNs/FCM has no per-message vendor cost, which is attractive at high fan-out, but throughput depends on HTTP/2 connection reuse and JWT/token caching inside the transport — if sends are synchronous and connection-per-message, latency and provider throttling will appear quickly. Move all sends to queues with concurrency limits and per-provider rate control. Recipient scale: device lookup, dedup, and reassignment queries on a growing PushDevice table need indexes on the token and the subject reference; the dedup guarantee is only as good as its unique constraints. "No third-party service" also means no vendor-side analytics — engagement metrics must be instrumented by us or forgone.
Failure modes. (1) Silent misconfiguration — .p8/service-account errors surfacing only as zero deliveries; mitigated by native-push:check in deploy smoke tests. (2) Token leakage from reuse after number reassignment — mitigated by strict ownership checks and revocation on logout. (3) Duplicate or orphaned device rows if registration is not idempotent. (4) Dropped notifications with no retry if sends are fire-and-forget — need provider response capture and a bounded retry/backoff. (5) Credential exposure through committed config; enforce secrets-manager sourcing and rotation runbook. (6) Provider policy changes (payload limits, priority/apns-push-type requirements) breaking delivery without a code change on our side.
Ramp-up. Low-to-moderate for a PHP engineer on the send path — the API is a handful of classes and four commands, and a working device notification is a day of work. Higher for the Doctrine-in-Laravel boundary and for the operational side (credential management, feedback processing, on-call runbook), which are not package-specific problems but are newly on our plate. Front-end effort is minimal if we skip Stimulus and call the endpoints ourselves. Expect roughly two weeks to a validated pilot and four to six to a hardened production rollout, contingent on the Doctrine decision.
Net: worth adopting if the Notifier seam and persistence model check out; the operational ownership of two provider integrations is the price of removing the middleman, and it should be accepted explicitly rather than absorbed as an afterthought.
How can I help you explore Laravel packages today?