Skip to content

Getting Started

Welcome to Arbiter, a framework-agnostic policy evaluation engine for hierarchical path-based access control. This guide will help you install, configure, and start using Arbiter in your application.

Arbiter provides a powerful system for answering the question: “Can service X perform action Y on resource path Z?”

Think of it as a flexible authorization layer that works with hierarchical paths like:

  • /customers/cust-123/carriers/fedex/api-key
  • /platform/carriers/*/credentials
  • /internal/services/order-service/config

Install Arbiter via Composer:

Terminal window
composer require cline/arbiter

If using Laravel, register the service provider in config/app.php:

'providers' => [
// ...
Cline\Arbiter\ArbiterServiceProvider::class,
],
'aliases' => [
// ...
'Arbiter' => Cline\Arbiter\Facades\Arbiter::class,
],

Or add to bootstrap/providers.php in Laravel 11+:

return [
// ...
Cline\Arbiter\ArbiterServiceProvider::class,
];

Hierarchical resource identifiers separated by /:

/platform/carriers/fedex/api-key
/customers/cust-123/carriers/ups/credentials
/internal/services/order-service/config

Named collections of rules that define access:

use Cline\Arbiter\Policy;
use Cline\Arbiter\Rule;
use Cline\Arbiter\Capability;
$policy = Policy::create('shipping-service')
->addRule(
Rule::allow('/platform/carriers/*')
->capabilities(Capability::Read, Capability::List)
)
->addRule(
Rule::allow('/customers/*/carriers/*')
->capabilities(Capability::Read)
)
->addRule(
Rule::deny('/customers/*/payments/*')
);

Actions that can be performed:

  • read - View/fetch resource
  • list - List children at path
  • create - Create new resource
  • update - Modify existing resource
  • delete - Remove resource
  • admin - Full control (implies all others)
  • deny - Explicit denial (overrides allows)
  • Exact: /carriers/fedex matches only /carriers/fedex
  • Single wildcard: /carriers/* matches /carriers/fedex, /carriers/ups
  • Glob wildcard: /customers/** matches any depth under /customers/
  • Variables: /customers/${customer_id}/* with runtime substitution

Arbiter provides two fluent API styles:

Start with the policy and check specific paths:

Arbiter::for('policy-name')
->can('/path', Capability::Read)
->allowed();

Start with the path and check which capabilities exist:

Arbiter::path('/some/path')
->against('policy-name')
->allows(Capability::Read);
// Or get all available capabilities
$caps = Arbiter::path('/some/path')
->against('policy-name')
->capabilities();
use Cline\Arbiter\Facades\Arbiter;
use Cline\Arbiter\Policy;
use Cline\Arbiter\Rule;
use Cline\Arbiter\Capability;
// Define a policy
$policy = Policy::create('shipping-service')
->addRule(
Rule::allow('/platform/carriers/*')
->capabilities(Capability::Read, Capability::List)
)
->addRule(
Rule::allow('/customers/*/carriers/*')
->capabilities(Capability::Read)
)
->addRule(
Rule::deny('/customers/*/payments/*')
);
// Register policy
Arbiter::register($policy);
// Check access using fluent API
Arbiter::for('shipping-service')
->can('/platform/carriers/fedex', Capability::Read)
->allowed();
// => true
Arbiter::for('shipping-service')
->can('/customers/cust-123/carriers/ups', Capability::Read)
->allowed();
// => true
Arbiter::for('shipping-service')
->can('/customers/cust-123/payments/stripe', Capability::Read)
->allowed();
// => false (explicit deny)
Arbiter::for('shipping-service')
->can('/platform/carriers/new', Capability::Create)
->allowed();
// => false (no create capability)
$apiPolicy = Policy::create('api-client')
->addRule(
Rule::allow('/api/v1/users/*')
->capabilities(Capability::Read, Capability::List)
)
->addRule(
Rule::allow('/api/v1/posts/**')
->capabilities(Capability::Read, Capability::Create, Capability::Update)
)
->addRule(
Rule::deny('/api/v1/admin/**')
);
Arbiter::register($apiPolicy);
Arbiter::for('api-client')
->can('/api/v1/users/123', Capability::Read)
->allowed();
// => true
Arbiter::for('api-client')
->can('/api/v1/posts/new', Capability::Create)
->allowed();
// => true
Arbiter::for('api-client')
->can('/api/v1/admin/settings', Capability::Read)
->allowed();
// => false (explicit deny)
$tenantPolicy = Policy::create('tenant-app')
->addRule(
Rule::allow('/tenants/${tenant_id}/**')
->capabilities(Capability::Read, Capability::Update, Capability::Create)
);
Arbiter::register($tenantPolicy);
// Context provides variable values
$context = ['tenant_id' => 'tenant-123'];
Arbiter::for('tenant-app')
->with($context)
->can('/tenants/tenant-123/settings', Capability::Read)
->allowed();
// => true (tenant_id matches)
Arbiter::for('tenant-app')
->with($context)
->can('/tenants/tenant-456/settings', Capability::Read)
->allowed();
// => false (tenant_id mismatch)
$envPolicy = Policy::create('production-only')
->addRule(
Rule::allow('/platform/**')
->capabilities(Capability::Read)
->when('environment', 'production')
)
->addRule(
Rule::allow('/platform/**')
->capabilities(Capability::Read, Capability::Update)
->when('environment', ['staging', 'development'])
->when('role', fn($v) => in_array($v, ['admin', 'developer']))
);
Arbiter::register($envPolicy);
$context = ['environment' => 'production', 'role' => 'viewer'];
Arbiter::for('production-only')
->with($context)
->can('/platform/config', Capability::Read)
->allowed();
// => true
$context = ['environment' => 'staging', 'role' => 'admin'];
Arbiter::for('production-only')
->with($context)
->can('/platform/config', Capability::Update)
->allowed();
// => true

Arbiter is perfect for:

  1. Credential Vaults - Control access to /customers/*/carriers/* paths
  2. File Systems - Permission checks on hierarchical file paths
  3. API Authorization - Route-based access control with wildcards
  4. Multi-Tenant Apps - Tenant-scoped resource access
  5. Feature Flags - Path-based feature toggles
  6. CMS/Content - Hierarchical content permissions