A letter of marque was a document issued by a government that turned pirates into privateers, granting them scoped permission to plunder in specific waters. This package does the same thing for Laravel (minus the plundering). A user can be an admin in one team and a viewer in another. Deny rules, permission boundaries, and JSON policy documents are built in. The whole thing plugs into Laravel's Gate.
composer require dynamik-dev/marque$user->assignRole('admin', scope: $acmeTeam);
$user->assignRole('viewer', scope: $widgetTeam);
$user->can('members.remove', $acmeTeam); // true
$user->can('members.remove', $widgetTeam); // falseRoles, boundaries, and deny rules can live in JSON files you import at deploy time:
{
"roles": [
{
"id": "editor",
"permissions": ["posts.*", "comments.create", "!posts.delete"]
}
],
"boundaries": [
{ "scope": "plan::free", "max_permissions": ["posts.read", "comments.read"] },
{ "scope": "plan::pro", "max_permissions": ["posts.*", "comments.*", "analytics.*"] }
]
}php artisan marque:import policies/production.json$user->can(), @can, $this->authorize(), and can: middleware all work without any extra wiring.
$user->assignRole('editor', $acmeOrg);
$user->can('posts.create', $acmeOrg); // true
Route::middleware('can:posts.create')->post('/posts', [PostController::class, 'store']);@can('posts.create', $team)
<button>New Post</button>
@endcanPrefix any permission with !. The denial overrides every other role that grants it.
Marque::role('editor', 'Editor')
->grant(['posts.*', 'comments.*'])
->deny(['posts.delete']);
$editor->can('posts.create'); // true
$editor->can('posts.delete'); // false -- deny winsBoundaries set a ceiling on what any role can do inside a scope. A user with admin in a free-tier org still can't access pro-tier features.
Marque::boundary('plan::free', ['posts.read', 'comments.read']);
Marque::boundary('plan::pro', ['posts.*', 'comments.*', 'analytics.*']);
$user->assignRole('admin', $freeOrg);
$user->can('analytics.view', $freeOrg); // false -- boundary blocks it
$user->can('analytics.view', $proOrg); // true'posts.*' // all post actions
'*.read' // read anything
'*.*' // superadmin
'posts.update.own' // fine-grained qualifiersEvery component implements a PHP interface. You can swap any implementation through the service container. See Swapping implementations.
Spatie laravel-permission works well for flat RBAC. Marque adds scoped roles, deny rules, permission boundaries, and declarative policy documents. See the full comparison.
| Dependency | Supported Versions |
|---|---|
| PHP | 8.4, 8.5 |
| Laravel | 12, 13 |
| PostgreSQL | 17+ |
| SQLite | 3.35+ |
| Valkey / Redis | 8+ |
SQLite works out of the box for development. PostgreSQL and Valkey are optional — the package tests against both in CI. MySQL is not officially supported but should work fine since Laravel's query builder abstracts the differences.
Getting Started — Installation | Seeding permissions and roles
Authorization — Checking permissions | Roles | Scoped permissions | Deny rules | Boundaries
Integrations — Middleware | Blade | Model policies | Sanctum
Policy Documents — Document format | Import / Export
Extending — Swapping implementations | Events | Cache
Reference — Configuration | Contracts | Events | Artisan commands | Comparison with Spatie
