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>
102 lines
3.3 KiB
Markdown
102 lines
3.3 KiB
Markdown
# 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.
|