funnelchat20/platform-gate
A lightweight Laravel package for platform-level gating, letting you restrict access to routes, features, or resources based on the requesting platform. Simple to configure and drop into existing apps, it suits multi-platform projects needing clear access boundaries.
funnelchat20/platform-gate is the package that lets your Laravel app trust requests coming through the Funnelchat edge: it reads the X-Funnelchat-Edge header, validates the shared edge secret and host, and exposes the result to the rest of the app.
Minimal setup:
composer require funnelchat20/platform-gate:^0.7
php artisan vendor:publish --tag=platform-gate-config
Then, in .env, set the secret and the expected host you were given for your domain. Without both, the edge is not trusted and the package logs a configuration error instead of silently accepting traffic.
Where to look first:
config/platform-gate.php) — secret, hosts, and any toggles.TrustEdge class — this is where the header is read, the secret is compared, and the secret is scrubbed from the request/globals.First use case: put the gate in front of an API route group that is only ever reached through the edge, and verify locally with a request that carries a correct X-Funnelchat-Edge header plus one that carries a wrong one — the second must be rejected and must not leak the value anywhere you can see it (storage/logs, Sentry, ray, etc.).
Upgrading: from 0.7.0 this is a drop-in — composer update funnelchat20/platform-gate with ^0.7 pinned is enough, no config or code changes. From 0.6, follow the 0.6 → 0.7 guide but land on 0.7.1 directly rather than 0.7.0. Domains can be rolled out in any order.
Register the gate as early as possible. The scrubbing of the secret only helps if it happens before anything else can serialise the request. Put TrustEdge at the top of your global middleware stack (or at least ahead of anything that reports to Sentry, Telescope, query loggers, or request-logging middleware).
Treat the config as an assertion, not a default. A short secret or a host that isn't a host is a deployment error. Since 0.7.1 those errors are emitted on the first request per process, with the same message text as before — so wire them into the same alerting you already use for ERROR-level logs. Expect exactly one occurrence per PHP process, not one per request; use that as your health-check signal after a deploy or a secret rotation.
Sentry needs a second half. With sentry in LOG_STACK, scrubbing $_SERVER/$_ENV is what stops the secret reaching Sentry from PHP-side logs — but on PHP-FPM the header itself is still visible to getallheaders() and cannot be unset from PHP. Add a before_send in your Sentry config that removes X-Funnelchat-Edge from the request headers:
// config/sentry.php
'before_send' => function (\Sentry\Event $event): ?\Sentry\Event {
$request = $event->getRequest();
unset($request['headers']['X-Funnelchat-Edge'], $request['headers']['x-funnelchat-edge']);
$event->setRequest($request);
return $event;
},
Without this, 0.7.1 alone is not sufficient — the release notes are explicit about it. If you run under a non-FPM SAPI, still add it: it costs nothing and protects you if the SAPI changes.
Audit your log stack. Any channel that captures the whole request (Sentry, an HTTP request logger, an APM integration) is a place the header can surface. Grep your LOG_STACK / config/logging.php for request-capturing channels and confirm each one either doesn't record headers or has its own scrubbing.
Make the invariant testable. Write a feature test that sends a request with a known-bad secret and asserts that the string appears nowhere in the captured log output. The 0.7.1 release added exactly this kind of test inside TrustEdge; mirroring it in your app protects you against your own future logging additions (e.g. someone dumping $request->headers->all() in a debug handler).
Rotation workflow. Rotate the secret on the edge first or on the app first — the notes only guarantee that domains can roll out in any order. Within a single domain, expect a window of failed validation. If the Worker/edge process starts before the secret exists, you get a configuration error, once, and traffic is not trusted until the secret is in place; sequence the deploy so the secret is set before the process serving traffic boots.
server bag but not from $_SERVER/$_ENV, and TrustEdge's own configuration-error logs ran before any scrubbing. If you pinned 0.7.0, upgrade — this is a security fix, not a feature bump.getallheaders() limit is real. There is no PHP-side workaround on FPM; the before_send callback is the supported answer. Don't spend time trying to unset() the header.TrustEdge; now it fails on the first request. If you had code catching an exception at container-resolution time, or a boot-time smoke test asserting the error, it will no longer see it. Assert on the log instead.sentry channel active and confirm nothing lands in Sentry. This is the one regression that would be silent in production.^0.7 is fine and picks up 0.7.1 transparently; pin exactly only if you have a reason, and if you do, make sure it's 0.7.1 and not 0.7.0.How can I help you explore Laravel packages today?