Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Permissions Laravel Package

cartalyst/permissions

Cartalyst Permissions is a PHP package for role-based access control, letting you define roles and permissions and check user access in Laravel applications. It provides a simple API for assigning, inheriting, and validating permissions.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What the package actually is. cartalyst/permissions is a small, framework-agnostic permission-string resolution container. It stores permission definitions, groups them into roles, and answers one question: given a set of granted permission slugs, does ability X pass? Wildcard matching (users.*, *.view, *) and inheritance are the genuinely fiddly logic worth borrowing.

Architecture fit — and the expectation gap. The description promises "Laravel integration and configuration" and role-based access control for Laravel apps. The v2.1.0 evaluation found no service provider, no facade, no publishable config, no migrations, no middleware, no Gate bridge. Treat the package as a library you compose around, not an RBAC feature you install. For a TPM this is the single most important framing correction: the delivery is not "turn on permissions," it is "adopt a resolution primitive and build the authorization layer."

Integration feasibility. Composer install is trivial and unopinionated — which cuts both ways. Zero Laravel coupling means it will never conflict with your framework version, but it also means it will never tell you it's incompatible. All compatibility signal must come from your own test suite. Realistic build-out, all owned by us:

  • A gateway class so the package type never leaks into call sites
  • An Ability registry so abilities are constants, not string literals
  • Schema: roles, permissions, permission_role, role_user (+ tenant_id if scoped)
  • A repository that hydrates and caches a user's granted slugs
  • Cache invalidation via observers, fired after commit
  • A Gate::before bridge so @can, authorize(), and can: middleware work unchanged
  • A resolution test matrix and an explain artisan command

That is a spike plus roughly one to two engineer-weeks of glue and tests — not a sprint drop-in.

Technical risk.

  • License (gate, not formality). composer.json reports NOASSERTION, and Cartalyst's historical model has been commercial/proprietary. Legal sign-off or a defensible vendoring decision must precede any code. This is the most common reason this dependency dies mid-project.
  • Staleness. One star, zero dependents, one release after a multi-year gap. No CVE feed, no upstream patch path, no Stack Overflow surface. A future PHP or Laravel minor that breaks it becomes our incident.
  • Undocumented wildcard semantics. Precedence rules are security-relevant and the README is the only API reference. A permissive default on an unknown ability is a vulnerability, not a bug.
  • Cache correctness is entirely ours. Nothing in the package invalidates anything. Stale permissions is the failure mode we will ship if we don't centralize invalidation.
  • Scope creep into product architecture. Multi-tenancy scoping is cheap now and expensive to retrofit; the package surfaces that question but answers none of it.

Key questions to put in front of engineering and the sponsor.

  1. Has legal cleared the license, and under what terms do we vendor or fork?
  2. Do we have authorization complexity that justifies a custom RBAC store, or does native Gate/Policy plus a thin spatie/laravel-permission install cover us?
  3. Which engineer owns this fork in six months, and is that named in the runbook?
  4. Are roles global or tenant-scoped — and is that decision recorded before schema work starts?
  5. What is our exit trigger (a blocked PHP/Laravel upgrade, a CVE posture requirement, an enterprise questionnaire) and does the gateway abstraction make that a one-class change?
  6. Is entitlement data (plan tiers, add-ons) in scope now, or is this purely internal admin roles? The answers have different scaling and audit profiles.

Integration Approach

Stack fit. PHP 8.x (v2.1.0 is the first release declaring a PHP 8 constraint) with any Laravel version, precisely because it binds to none. It belongs in app/Authorization/ behind a gateway — never called from a controller, view, or job directly. That single discipline is what makes the eventual swap to native policies or spatie/laravel-permission a one-class change rather than a grep across 200 files.

Migration path.

  • Brownfield (scattered if ($user->isAdmin()) checks): wrap first, move data second. Stand up the gateway and the Gate::before bridge so existing checks keep their current behavior, then migrate role/permission records behind it. Every call site should end up going through Gate / @can / the gateway. Never app(Permissions::class) inline.
  • Greenfield: start at the boundary, not the database. Gateway → ability registry → config-owned definitions → schema → cache → Gate bridge. This ordering means the design conversation happens before the migration does.

Compatibility.

  • Pin ^2.1 with a committed composer.lock; vendor the source into packages/ within a quarter and document ownership.
  • Install and run the full suite on every PHP minor actually shipped (8.1/8.2/8.3) with error_reporting(E_ALL) so deprecations surface in CI, not production.
  • Treat the README as unverified; read the source before trusting any signature from a blog post.
  • Confirm early whether the package ships Role/Permission value objects or only the evaluator — the answer changes how much schema and domain modeling we own.

Sequencing (proposed milestones).

  1. License/legal gate. Blocks everything. No spike spend before sign-off.
  2. Source-read spike + resolution test. A table-driven PermissionResolutionTest of [granted[], asked, expected] covering *, users.*, *.view, nested wildcards, and unknown abilities. This test is the spec — write it before a single role exists.
  3. Gateway, Ability constants, config/permissions.php definitions. Bind the singleton; make abilities reviewable in one file.
  4. Schema, seeding, cache, observers. Seed permissions from config on deploy; validate in a test that every Ability::* constant exists in the table. Decide tenant scoping in the pivot before writing migrations.
  5. Gate bridge. Gate::before returning true for super-admins, null for policy-backed or undefined abilities, and the gateway verdict otherwise. Avoid naive Gate::define('*', ...) — it interacts poorly with policies and authorize().
  6. php artisan permissions:explain [email protected] export.reports — prints roles, raw slugs, resolved array, and the container's verdict. Ship this at rollout, not after the first escalation.
  7. Rollout with denial logging at debug level, so a typo'd ability string is distinguishable from a correct denial.
  8. Product leverage: model plan tiers and add-on entitlements as rows (plan:pro -> export.reports, users.create). A pricing change becomes a migration + seeder, not a release.

Operational Impact

Maintenance — we own the lifecycle. Assume no upstream. Budget a fork inside the monorepo, a named owner in the runbook, and a quarterly review tied to PHP/Laravel upgrade planning. The package is small enough to read in an afternoon; that readability is the main thing we're buying.

Support surface. No vendor, no CVE feed, no community. Internal documentation and the source are the support channel. Every new engineer learns this from us, not from the internet — so the explain command and the resolution test matrix are onboarding artifacts, not just debugging aids.

Scaling. Evaluation itself is cheap: in-memory array matching against a hydrated permission set, no query per check. The cost sits in hydration and cache. Cache the resolved array per user (keyed by tenant where scoped), not query results, and invalidate on write only — on Role/Permission save, pivot attach/detach, and user role changes, via observers with Cache::forget after commit (DB::afterCommit()). Centralize in a PermissionCache::flushFor() so tinker, seeders, and queued imports can't bypass it.

Failure modes, ranked by likelihood and blast radius.

  1. Stale cache — permissions revoked but still granted, or vice versa. The number-one bug with this package, entirely self-inflicted.
  2. Silent deny from a typo'd ability string — indistinguishable from correct denial in tests and from "the app is broken" in production.
  3. Wildcard over-grant — a permissive default on an unknown ability, or an inheritance direction we assumed rather than tested. Security-relevant.
  4. Upgrade breakage — a PHP or Laravel minor that the unmaintained package can't absorb, discovered at the worst possible time.
  5. License audit finding — NOASSERTION surfacing in a procurement or diligence review after the code has shipped.
  6. Transaction/cache ordering — invalidation fired before commit, leaving the cache repopulated with pre-change data.

Ramp-up. The source is a fast read; the surrounding design is what takes time. Allocate: a spike to read the source and write the resolution spec, an afternoon for any engineer on the gateway pattern, and a half-day for whoever owns the Gate bridge — that's the piece with the subtlest interaction with policies. Expect a two-to-three-week calendar window from license clearance to a trustworthy first rollout, mostly test-writing.

TPM leverage beyond the code. Even in the scenario where we don't install it, this package is a cheap forcing function: it gives product and engineering a shared vocabulary (roles vs. permissions vs. entitlements vs. plan features), it draws a deliberate line between policy storage and policy enforcement, and it surfaces the multi-tenancy and audit-trail questions that SOC 2 questionnaires will ask later. Run that conversation in the spike. If the outcome is "native Gate/Policy plus a documented entitlement table," that is a successful use of this evaluation — not a failed adoption.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style