sereny/nova-permissions
A Laravel Nova tool that adds a user-friendly interface for managing roles and permissions. Create roles, define permissions, and assign them to users directly from your Nova admin panel, with seamless integration with spatie/laravel-permission and no code required.
Architecture fit
sereny/nova-permissions is a presentation layer, not a permissions engine. It contributes a Nova tool plus two Nova resources (Role, Permission) that read and write the tables owned by spatie/laravel-permission. That distinction is the single most decision-relevant fact about the package, and it should drive how the org scores it.
Consequences for a TPM:
Sereny\ class. Authorization binds to Spatie's contract (HasRoles, Gate, policies, can: middleware). If that boundary holds, a stalled package costs us an admin screen, not production access control. If it slips, the exit becomes a refactor.Integration feasibility
Technically straightforward; procedurally full of decisions that must be made before install, not after.
| Check | Status if unmet |
|---|---|
| Nova 4.x/5.x licensed and working | Hard blocker — package will not install cleanly |
| Spatie installed, config published, migrations run | Hard blocker |
Nova-authenticated model uses HasRoles |
Silent no-op |
| Teams enabled/disabled decided in writing | Retrofitting changes what the UI means |
| Guard(s) enumerated | Silent "permission denied" with no error |
The 20-minute first win is real: expose the tool to super-admins only, create a billing-manager role, tick grouped permissions, assign to a user. That is a sprint of UI work reclaimed. The reclaimable time should be redirected into domain authorization policies — the part that is actually product-specific — not treated as slack.
Technical risk
laravel/nova, so the reported 0 dependents is a measurement artifact, not evidence of no adoption. Score this as unknown adoption, and weight the maintainer-risk and Nova-coupling findings directly instead of via a composite.Auth::id()), but it is work we own.model_has_roles rows. Needs a policy blocking deletion of canonical seeder-defined roles plus reviewed confirmation copy.view invoices, Invoices-View, and invoice_view in the same afternoon. This is the most likely ongoing source of defects, and it is a governance problem the package cannot solve.Key questions to resolve before committing
guard_name?Sereny\ imports in app or policy code" in code review, and who is accountable for that boundary?Stack fit
Fits cleanly into a Laravel + Nova + Spatie stack (PHP 8.x, Nova 4/5, spatie/laravel-permission 6.x). It is additive — no schema of its own, no middleware, no route rewriting beyond registering a tool. The mismatch case is any environment where Nova is not already the admin surface; there, the cost baseline is Nova licensing plus a UI rewrite, and this package's 20-minute install figure is misleading.
Migration path
Three distinct starting states, with different sequencing and different levels of effort:
$user->getAllPermissions()->pluck('guard_name')->unique() before building anything on top.vyuldashev/nova-permission, a hand-rolled screen, or nothing is a composer change plus re-registering the tool. Role and permission data is untouched because it lives in Spatie tables. This is the cheapest migration of the three and the reason the exit is cheap.Compatibility
Pin exactly three things in composer.json — the package, Nova, and Spatie — and commit the pin with a comment explaining why. A floating constraint on any of the three is how we get a broken admin screen on a Tuesday with no obvious cause. Verify the package's declared Nova constraint in its own composer manifest at each upgrade rather than trusting the README. Confirm vendor namespace and class paths against the autoload block: small Nova packages rename resources between releases more often than expected.
Sequencing
| Phase | Work | Exit criterion |
|---|---|---|
| 0 — Preconditions | Nova license confirmed; teams and guards decided in writing; audit requirement triaged | Decisions recorded in the ADR |
| 1 — Foundation | Spatie installed and configured; PermissionSeeder with canonical roles/permissions; resource.action naming convention published and owned |
Seeder reproduces identical state in a fresh environment |
| 2 — Surface | Package installed and pinned; tool + resources registered; viewNovaPermission gate enforced server-side and asserted with a 403 test |
Non-admin hitting the tool route gets 403 |
| 3 — Hardening | Authorization flow tests (not UI tests) run against both seeder-populated and UI-mutated databases; Redis cache; delete-protection policy for canonical roles; manual fallback runbook written for non-engineers | Runbook findable by ops without engineering |
| 4 — Steady state | Boundary enforced in code review; re-evaluate at each Nova major | Swap path documented in the decision record |
Phase 2 must not start before Phase 1. Exposing the matrix first guarantees taxonomy drift and a UI that becomes the de facto source of truth.
The gate is the one non-negotiable in Phase 2: hiding the tool from navigation is cosmetic. Without a server-side gate, any user who can reach the screen can grant themselves permissions — that is a privilege-escalation path, not a UI nicety.
Maintenance
Low, bounded, and concentrated at a predictable trigger: Nova majors. Ongoing maintenance is a pin review, a quarterly check that the package still declares a compatible Nova constraint, and ownership of the taxonomy. Budget one spike per Nova upgrade — run it on a branch against the authorization suite, never as a manual click-through. Everything else (Spatie, migrations, guards) is maintenance we would carry with or without this package.
Support
No SLA and a single-agency maintainer mean this cannot become a strategic dependency; treat it as an accelerator. The mitigations are organizational, not technical, and they are cheap:
User::where('email', ...)->first()->assignRole(...) in tinker), placed where a non-engineer can find it. An admin outage that is an inconvenience is acceptable; one that becomes an incident is not.Sereny\ import" rule enforced in review, converting an 87-star dependency into a replaceable-UI decision.Scaling
Two axes matter:
php artisan permission:cache-reset.Failure modes
| Failure | Blast radius | Detection | Mitigation |
|---|---|---|---|
| Nova major upgrade breaks the tool | Admin screen only; production authz intact | Upgrade branch | Authorization suite + pinned versions + fallback runbook |
Guard mismatch (web role, api actor) |
Silent false from can() |
Tinker guard check | Verify guards before Phase 1 exit |
| Stale permission cache across nodes | Divergent authz per node, intermittent | Hard to reproduce | Shared Redis cache; forgetCachedPermissions() in seeders/commands/bulk queries |
| Role deleted while in use | Cascade drops model_has_roles |
Support ticket | Policy blocking deletion of canonical roles |
| Taxonomy drift in the matrix | Broken policies months later | Authorization test suite | Naming convention, named owner, review of new permissions |
| Bulk query-builder edits skip model events | Cache never invalidated | Mystery bug | Explicit cache flush in any script touching role tables |
Ramp-up
Engineer onboarding is negligible — one config file, two registered resources, one gate. The real ramp-up is operational: a 20-minute walkthrough for the ops lead (create role → tick grouped permissions → assign to a user) is sufficient for day-to-day use. The training that actually matters is governance — publishing the resource.action convention and making clear that the UI assigns users to roles, not defines the permission universe. Direct user→permission grants bypass the role model and produce an access graph nobody can reason about six months later; reserve them for exceptions or disallow them.
Success metrics worth tracking
Sereny\ imports in app/ or policy code (grep-able; the leading indicator of exit cost).How can I help you explore Laravel packages today?