Marque
Laravel authorization with scoped roles, deny rules, permission boundaries, and JSON policy documents
A letter of marque was a government document that granted pirates scoped permission to plunder specific waters. Marque does roughly the same thing for Laravel authorization, minus the plundering. A user is admin in one team and viewer in another, and every permission check accepts a scope.
Scoped roles
Spatie’s laravel-permission covers flat RBAC well. Once you need “admin in Acme, viewer in Widget Co,” you either glue team scoping on top or outgrow the package. Marque takes scope as a first-class argument:
Marque::createRole('admin', 'Admin')
->grant(['members.*', 'posts.*'])
->assignTo($user, scope: $acmeTeam);
$user->can('members.remove', $acmeTeam); // true
$user->can('members.remove', $widgetTeam); // false
A scope can be any model that implements Scopeable: teams, orgs, projects, plan tiers.
Deny rules
Prefix a permission with ! and that denial overrides every role that grants it. An editor with posts.* and !posts.delete has full post access except delete:
Marque::createRole('editor', 'Editor')
->grant(['posts.*'])
->deny(['posts.delete']);
Boundaries
A boundary caps what any role can do inside a scope, admins included. Plan tiers live in the authorization layer instead of scattered feature flag checks:
Marque::boundary($freeOrg)->permits(['posts.read']);
Marque::boundary($proOrg)->permits(['posts.*', 'analytics.*']);
$user->assignRole('admin', $freeOrg);
$user->can('analytics.view', $freeOrg); // false, boundary blocks it
$user->can('analytics.view', $proOrg); // true
JSON policy documents
Roles, boundaries, and deny rules can live in version-controlled JSON and import at deploy time:
{
"roles": [{ "id": "editor", "permissions": ["posts.*", "!posts.delete"] }],
"boundaries": [{ "scope": "plan::free", "max_permissions": ["posts.read"] }]
}
php artisan marque:import policies/production.json
Wired into the Gate
$user->can(), @can, $this->authorize(), and the can: route middleware work with no extra wiring. Existing code that already uses Laravel’s Gate keeps working; Marque is the driver underneath.
Install
composer require dynamik-dev/marque