dynamik-dev/laravel-policy-engine

A Laravel-native scoped permissions package. Composable, contract-driven authorization that integrates with Gates, Policies, middleware, and Blade.

Maintainers

Package info

github.com/dynamik-dev/marque

pkg:composer/dynamik-dev/laravel-policy-engine

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 11

Open Issues: 0

v0.2.4 2026-04-14 14:34 UTC

README

Pint Larastan Pest PHPStan Level 9

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

Quick look

Marque::createRole('admin', 'Admin')
    ->grant(['members.*', 'posts.*'])
    ->assignTo($user, scope: $acmeTeam);

Marque::createRole('viewer', 'Viewer')
    ->grant(['posts.read'])
    ->assignTo($user, scope: $widgetTeam);

$user->can('members.remove', $acmeTeam);  // true
$user->can('members.remove', $widgetTeam); // false

Roles, 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

Features

Wired into the Gate

$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>
@endcan

Deny rules

Prefix any permission with !. The denial overrides every other role that grants it.

Marque::createRole('editor', 'Editor')
    ->grant(['posts.*', 'comments.*'])
    ->deny(['posts.delete']);

$editor->can('posts.create');  // true
$editor->can('posts.delete');  // false -- deny wins

Permission boundaries

Boundaries 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. Pass a scope string or any Scopeable model.

Marque::boundary($freeOrg)->permits(['posts.read', 'comments.read']);
Marque::boundary($proOrg)->permits(['posts.*', 'comments.*', 'analytics.*']);

$user->assignRole('admin', $freeOrg);
$user->can('analytics.view', $freeOrg);  // false -- boundary blocks it
$user->can('analytics.view', $proOrg);   // true

Wildcards

'posts.*'           // all post actions
'*.read'            // read anything
'*.*'               // superadmin
'posts.update.own'  // fine-grained qualifiers

Resource policies

Attach authorization rules directly to a resource type. The when() closure receives the user and the resource instance.

Marque::resource(Post::class)
    ->allow('update')
    ->when(fn ($user, $post) => $post->author_id === $user->id);

Marque::resource(Post::class)
    ->deny('delete');

Contract-driven

Every component implements a PHP interface. You can swap any implementation through the service container. See Swapping implementations.

Why not Spatie?

Spatie laravel-permission works well for flat RBAC. Marque adds scoped roles, deny rules, permission boundaries, and declarative policy documents. See the full comparison.

Requirements

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.

Documentation

Getting StartedHow authorization works | Installation | Permission naming | Seeding permissions and roles

AuthorizationChecking permissions | Roles | Scoped permissions | Deny rules | Boundaries | Resource policies

IntegrationsMiddleware | Blade | Model policies | Sanctum

Policy DocumentsDocument format | Import / Export

ExtendingSwapping implementations | Events | Cache

TestingTesting authorization

ReferenceConfiguration | Contracts | Facade | Events | Artisan commands | Comparison with Spatie