The staff guard
The panel authenticates against Lunar core’s staff guard, backed byLunar\Core\Models\Staff. Core registers the guard and its provider automatically unless lunar.staff.register_guard is disabled, using lunar.staff.guard (default staff) and lunar.staff.model (default Lunar\Core\Models\Staff).
The panel’s own guard config key (lunar.panel.guard) overrides the guard the panel authenticates against, for a host application that wants the panel on a different guard than the rest of Lunar. Left null (the default), the panel follows lunar.staff.guard. See Configuration.
Two-factor authentication is mandatory
There is no password-only login path. Every staff login is a two-step challenge:- Email and password, at
/panel/login. - A second factor: a time-based one-time password (TOTP) from an authenticator app if the staff member has enrolled one, or an emailed six-digit code otherwise.
Enrolling an authenticator app
Staff enroll their own second factor under Account > Security (panel.account.security). Enrollment generates a TOTP secret, shown as a QR code for scanning into an authenticator app, and a set of recovery codes to store somewhere safe. Confirming the enrollment with a valid code from the app activates it; from that point, login prompts for a code from the app instead of an emailed one.
Recovery codes let a staff member sign in if they lose access to their authenticator app. Each code is single-use; staff can regenerate the full set from the same Security screen.
Password reset
Password reset follows the standard Laravel flow, themed to the panel, starting at/panel/forgot-password.
Permissions
Access within the panel is controlled by permission handles, defined in Lunar core’s access control manifest (Lunar\Core\Auth\Manifest):
settings itself is also registered as a base permission, acting as the parent grouping for the settings:* handles.
The admin bypass
A staff member withadmin set to true on their record passes every permission check, regardless of assigned roles. Everyone else is checked per-permission through hasPermissionTo() (from spatie/laravel-permission).
Both checks are wired through a single Gate::after hook, registered by the panel’s service provider:
lunarphp/admin, so the two panels apply identical rules to the same staff account — a permission granted in one panel behaves the same way in the other.
Navigation and routes stay in sync
Panel routes are gated with Laravel’scan:{handle} middleware, and navigation items carry the same permission handle. Because both read from the same handle, a staff member never sees a navigation entry they cannot reach, and never hits a route for a feature that is hidden from their navigation. Add-on sections follow the same convention when they register their own navigation items, routes, and actions — see Extending the panel.