withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) // Registers the `/broadcasting/auth` endpoint (under the `web` group, so PanelScheme // guards it) and the private-channel authorization callbacks in routes/channels.php. // Without this, private/presence channels can never be subscribed to. See R-note in // routes/channels.php: every broadcast channel is PRIVATE (authenticated) — metrics are // fleet infrastructure data and must not be reachable on a stale Caddy host via Reverb. ->withBroadcasting(__DIR__.'/../routes/channels.php') ->withMiddleware(function (Middleware $middleware): void { // Trust Caddy's X-Forwarded-* ONLY in production, where the app has no host port // (docker-compose.prod.yml exposes app:80 on the internal net only) so Caddy is // the sole peer. In dev the app port is published on the host, so trusting forged // proxy headers would let a LAN peer fake HTTPS (forcing a Secure cookie over // plain HTTP and breaking the bare-IP recovery login). if (($_SERVER['APP_ENV'] ?? getenv('APP_ENV') ?: 'local') === 'production') { $middleware->trustProxies(at: '*'); } // PanelScheme first (scheme/host enforcement before auth), so an HTTP request to // the active domain is sent straight to HTTPS and a wrong host is refused before // any auth redirect. $middleware->web( prepend: [PanelScheme::class], append: [SetLocale::class, SecurityHeaders::class], ); }) ->withExceptions(function (Exceptions $exceptions): void { $exceptions->shouldRenderJsonWhen( fn (Request $request) => $request->is('api/*'), ); // Errors thrown BEFORE the web-group SetLocale middleware (CSRF 419, an // unmatched-route 404, maintenance 503) would render the custom error page in // the default locale. Resolve the user's language first so those pages honour // it too (R16); returning null falls through to the normal renderer (the // resources/views/errors/* views). $exceptions->render(function (Throwable $e, Request $request) { SetLocale::apply($request); return null; }); })->create();