chore: install laravel telescope as dev-only debugging dashboard

Installs laravel/telescope ^5.0 (v5.12.5) as a dev-dependency.
Three-layer production safety adapted to Laravel 11 layout (no
Kernel.php; routing/schedule in bootstrap/app.php +
routes/console.php):

  1. composer.json `extra.laravel.dont-discover` lists
     laravel/telescope. After editing, `php artisan package:discover`
     regenerates bootstrap/cache/packages.php — without this step
     the auto-discovery cache still registers the vendor provider.
  2. AppServiceProvider::register() gates registration to local +
     testing environments. Registers BOTH the vendor
     Laravel\Telescope\TelescopeServiceProvider (routes, migrations,
     publishing) AND the project's App\Providers\TelescopeService
     Provider (gate + filter) — they're sibling classes that extend
     ServiceProvider independently, not parent/child, so both must
     register for the dashboard to work. bootstrap/providers.php
     deliberately does NOT list either Telescope provider.
  3. .env TELESCOPE_ENABLED flag (false in .env.example). Runtime
     toggle that disables Telescope even when the providers are
     registered.

Production safety verified via simulated APP_ENV=production check:
confirms no Telescope-* providers are loaded.

Authorization: viewTelescope gate restricts dashboard to users
with the super_admin Spatie Permission role. Even in local
environments, only super_admin can view. Default was an email
allow-list stub — replaced with `$user->hasRole('super_admin')`.

Pruning: Schedule::command('telescope:prune --hours=48') added in
routes/console.php (Laravel 11's schedule location), environment-
gated to local + testing only.

Documentation: /dev-docs/TELESCOPE.md added; CLAUDE.md gets a
Development-tooling section. The doc explicitly calls out the
dual-provider registration (vendor + app) which differs from the
single-provider pattern in older Laravel versions.

Migrations applied: telescope_entries, telescope_entries_tags,
telescope_monitoring tables. Route registration verified in local
(42 telescope.* routes).

Tests: 1208/1208 passing — Telescope loads in the testing
environment as well, so the suite exercised it without issues.

Deployment note (flag for separate docs): a production operator
who runs `php artisan migrate` manually will still apply the
Telescope migrations — but because the providers never register
in production, the tables stay empty.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-25 04:03:31 +02:00
parent 5771a678ef
commit f38c7ece97
11 changed files with 612 additions and 2 deletions

101
dev-docs/TELESCOPE.md Normal file
View File

@@ -0,0 +1,101 @@
# Laravel Telescope (dev-only)
Local debugging dashboard for queries, jobs, mails, redis, events,
exceptions. **Never registered in production.**
## Access
- Local URL: <http://localhost:8000/telescope>
- Auth requirement: authenticated user with `super_admin` role
(Spatie Permission). The `viewTelescope` gate is the access
control; even in `local`, only super_admin can view.
## Production safety — three-layer defense
Crewli runs Laravel 11 (no `Kernel.php`; routing/schedule live in
`bootstrap/app.php` + `routes/console.php`). The defense layers
adapted to that layout are:
1. **`composer.json` `extra.laravel.dont-discover`** lists
`laravel/telescope`, so Laravel's auto-discovery never registers
the vendor `Laravel\Telescope\TelescopeServiceProvider`. After
editing this list, run `php artisan package:discover` once to
refresh `bootstrap/cache/packages.php`.
2. **`AppServiceProvider::register()`** gates manual registration
to `local` + `testing` only, and registers BOTH the vendor
provider (routes/migrations/publishing) and the project's
`App\Providers\TelescopeServiceProvider` (gate + filter):
```php
if ($this->app->environment('local', 'testing')) {
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
$this->app->register(\App\Providers\TelescopeServiceProvider::class);
}
```
`bootstrap/providers.php` deliberately does NOT list either
Telescope provider — both registrations live behind this
environment gate.
3. **`.env` `TELESCOPE_ENABLED` flag.** `false` in `.env.example`.
Runtime toggle that disables Telescope even when the providers
are registered (e.g. to silence Telescope locally during a
profiling session).
If you ever see Telescope on production: revert immediately,
audit the three layers above, and treat as a security incident.
The dashboard exposes every query, payload, and job — including
secrets in payloads.
### Verifying production safety
```bash
APP_ENV=production php -r "
require 'vendor/autoload.php';
\$app = require 'bootstrap/app.php';
\$app->make('Illuminate\Contracts\Console\Kernel')->bootstrap();
foreach (array_keys(\$app->getLoadedProviders()) as \$p) {
if (stripos(\$p, 'telescope') !== false) echo 'LOADED: ' . \$p . PHP_EOL;
}
"
```
Expected output: nothing. Any line means a layer is breached.
## Pruning
Scheduled in `routes/console.php` (Laravel 11 layout):
```php
Schedule::command('telescope:prune --hours=48')
->daily()
->environments(['local', 'testing']);
```
48-hour retention. Adjust if dev DB is filling up.
## What Telescope captures
- All Eloquent queries with bindings + execution time (N+1 detection)
- Background jobs with payload + status + retry history
- Sent mails with rendered HTML + plaintext + recipient
- Redis commands
- Cache reads/writes
- Events fired with listeners
- Exceptions with stack traces
- HTTP requests with headers + payload
## What NOT to use Telescope for
- Production debugging — never enabled there
- Long-term audit trails — Spatie ActivityLog handles that
(see ARCH-FORM-BUILDER §17.1)
- Performance benchmarking — use APM tools designed for it
## Disabling temporarily
If Telescope is slowing down a specific dev workflow:
```bash
TELESCOPE_ENABLED=false php artisan serve
```
Or set globally in `.env` and restart your server. Re-enable when
done.