bbs-lab/nova-permission
bbs-lab/nova-permission adds a Laravel Nova UI for spatie/laravel-permission, letting you create roles and permissions, assign them to users, and manage access control directly from Nova without custom admin screens.
Feature: per-record (granular) permission control inside Nova
admin/writer/reader, Post vs Service) is a usable reference model for scoping a first feature.unique(name, guard_name) was never replaced) and filtered on a hardcoded chambers.id column from another project. Any roadmap item or customer commitment resting on granular permissions in 1.x is effectively unmet and must be re-baselined against 2.x.Build vs. buy
HasRoles) and contract (CanOverridePermission) on the authenticatable model, and its policies call hasPermissionToOnModel(). That is a one-way door on the user model and on every policy that touches these resources — worth deciding deliberately, not as a side effect of a sprint.Roadmap and platform timing
^8.2, Nova ^4.0 || ^5.0, spatie/laravel-permission ^6. On Nova 3, PHP 8.1, or Spatie 5, this is not adoptable without a platform upgrade first — sequence it behind (or bundle it with) the Nova 4/5 migration, not in parallel with it.config('nova-permission.cache.enabled'), NOVA_PERMISSION_CACHE / NOVA_PERMISSION_CACHE_TTL). Defaults changed from caching to not caching — expect a latency regression on authorization-heavy screens and budget a follow-up ticket to enable the cache with a tuned TTL, rather than discovering it in production..php files that auto-run on php artisan migrate, and only Spatie's migration is published under --tag="permission-migrations". This is a deployment-governance decision: schema changes to the authorization table (including the new composite unique index) will arrive with ordinary deploys, so review them the way you review a first-party migration.Upgrade decision (1.x → 2.x)
HasRoles trait, implement CanOverridePermission, re-point the gate_cache config key to the new cache section, and stop narrowing the base Policy signatures (?bool, keep the Model $model parameter). Allocating a short, explicit spike here is cheaper than debugging authorization gaps later.Adopt when:
Look elsewhere or defer when:
HasRoles/CanOverridePermission contract entirely.php artisan migrate, or need a schema you control end-to-end on the authorization table.Adoption guardrails worth writing into the ticket: pin the major version explicitly, run the upgrade guide against a branch with the full permission matrix exercised, verify the composite unique index (name, guard_name, authorizable_id, authorizable_type) applies cleanly on MySQL (the index name is explicit for this reason), and add a regression test that a same-name permission can exist on two different model instances — that is precisely the failure mode 1.x had.
For executives (one paragraph): We are evaluating a Nova package that lets administrators grant permissions on individual records rather than only across the whole application — the difference between "writers can edit posts" and "this writer can edit this post." The previous version advertised that capability but it did not work; the new v2.0.0 makes it work, adds a real test suite and CI, and modernizes the stack. The cost is a mandatory platform floor (PHP 8.2+, Nova 4/5, Spatie 6) and a non-trivial upgrade if we are already on 1.x. Recommendation: adopt only as part of the Nova 4/5 upgrade, not as a standalone project, and treat it as a buy decision that saves us building an admin permissions UI and a caching layer ourselves — with the residual risk that it is a small-vendor dependency in a security-sensitive part of the system.
For engineering (the short version):
This is a correctness release, not a feature release. The headline is that granular permissions were dead in 1.x — same-name scoped permissions could not be inserted (Spatie's unique(name, guard_name) was never replaced) and scoping filtered on a hardcoded chambers.id. If we ever shipped against that, our assumptions were wrong.
Breaking changes to plan for: PHP ^8.2, Nova ^4.0 || ^5.0, Spatie ^6; the authenticatable model must use the package's HasRoles trait and implement CanOverridePermission (policies call hasPermissionToOnModel()); package migrations now auto-run on php artisan migrate and are no longer publishable except Spatie's; gate_cache is replaced by an opt-in cache section defaulting to disabled; base Policy methods return ?bool and keep the Model $model parameter, so don't narrow them in overrides.
Also fixed and worth knowing: Nova 4 support restored (the Nova-5-only devtool dependencies were removed), the permission builder search now actually filters, and per-model authorization-cache invalidation is corrected. The quality bar is now Pest with 100% line coverage plus mutation and architecture tests, PHPStan level 8, Pint, and a CI matrix across Nova 4/5, Laravel 11–13, PHP 8.3–8.5 — that is the reason to trust it more than 1.x, and also the reason a 1.x→2.x jump deserves its own branch and a permission-matrix regression test rather than a same-day merge.
How can I help you explore Laravel packages today?