Skyline

Security & access control

Who can reach the dashboard — and who can reach the buttons that delete things.

Access to the Laravel Skyline dashboard is controlled by exactly what controls access to Horizon's: the viewHorizon gate defined in your HorizonServiceProvider, and the middleware listed under horizon.middleware. Skyline changes neither. What does change is what a person who passes that gate is able to do — so it is worth re-reading a gate that was written when the dashboard could only look.

Read this before deploying Skyline

Skyline's destructive actions — deleting a job, emptying a queue, pausing a queue, running a delayed job immediately — sit behind the same viewHorizon gate as read-only access. There is no separate permission. Anyone who could previously watch your queues can now also act on them.

The viewHorizon gate#

Horizon's installer publishes a HorizonServiceProvider with a gate that, by default, allows nobody in production:

protected function gate(): void
{
    Gate::define('viewHorizon', function ($user) {
        return in_array($user->email, [
            'taylor@laravel.com',
        ]);
    });
}

If that list is still the stub, or if the gate was widened to "any authenticated user" back when the dashboard was read-only, now is the moment to narrow it. A reasonable gate for Skyline is the set of people you would trust to run redis-cli DEL against a production queue — because that is materially what Empty queue does.

Gate::define('viewHorizon', fn ($user) => $user->isAdmin() && $user->hasTeam('platform'));

The local environment#

Horizon's Horizon::auth() callback allows access in the local environment without consulting the gate at all, which is what lets you open /horizon on your laptop without logging in. Skyline inherits that behaviour unchanged. It means the gate is the only thing standing between a request and a queue-emptying button everywhere else — so a misconfigured APP_ENV on a deployed box is worth treating as a security issue, not a cosmetic one.

Middleware#

Everything the dashboard serves, including every API endpoint Skyline adds, runs through the middleware stack in config/horizon.php:

'middleware' => ['web'],

Adding your own middleware here — an IP allow-list, an SSO guard, a second factor — protects the Skyline endpoints exactly as it protects Horizon's, because they are the same stack. There is no separate route group to remember.

CSRF, and calling the API from a script#

Because the endpoints sit inside the web group, they are session-authenticated and CSRF-protected. This is the part that surprises people writing a runbook: a bare curl at the pause endpoint does not pause a queue, it gets a redirect to your login page or a 419.

A script that genuinely needs to drive the API has to carry a session cookie and a CSRF token, like a browser would. If what you actually want is a deploy-time or incident-time hook, prefer the Artisan commands — they need no HTTP session and are the intended surface for automation:

php artisan horizon:pause
php artisan horizon:pause-supervisor supervisor-1
php artisan horizon:continue

See the HTTP API reference for the endpoints themselves.

What the destructive actions can and cannot do#

The guards are in the backend, not only in the UI, so they hold even for a request that skips the confirmation modal:

  • Only pending and delayed jobs can be deleted. A job a worker has already reserved is rejected with a 422.
  • Completed, silenced and failed jobs cannot be deleted at all — they are historical records, not queue entries.
  • Per-job deletion requires a Redis connection; other drivers are rejected with a 422.
  • A job picked up by a worker between the click and the delete landing fails with a 409 rather than silently doing nothing.

None of that is an access control. It bounds the blast radius of a mistake; it does not bound the blast radius of the wrong person having the gate. Set the gate.

The license key is a credential#

Finally, and separately from the dashboard: your Skyline license key authenticates Composer against a private registry, so it is a secret in the ordinary sense. Keep it in a secret manager or in CI secrets, supply it through COMPOSER_AUTH rather than a committed auth.json, and never put it in a public repository. See Installation.

Common questions

Who can access the Laravel Skyline dashboard?

Exactly who could access Horizon's. Access is controlled by the viewHorizon gate defined in your HorizonServiceProvider and by the middleware listed under horizon.middleware, and Skyline changes neither. In the local environment Horizon's auth callback allows access by default; everywhere else the gate decides.

Does Skyline need a separate permission for its destructive actions?

No — and that is the thing to know before deploying it. Deleting a job, emptying a queue and pausing a queue all sit behind the same viewHorizon gate as read-only access. Anyone who could previously watch your queues can now also act on them, so revisit a gate that was written when the dashboard could only look.

Queue control, not just queue monitoring.

Skyline is a drop-in replacement for Laravel Horizon that lets you act on what you see — pause a queue, jump a job to the front, drain a backlog.

Sign up for early access