Configuration

Publish the config, then drive it with environment variables:

php artisan vendor:publish --tag=laravel-iam-client-config

All keys

Key Env Default Meaning
mode IAM_CLIENT_MODE local Transport: local = in-process PDP; http = remote Admin API. Any value ≠ http selects local.
http.base_url IAM_CLIENT_BASE_URL Versioned API root, e.g. https://iam.example.com/api/iam/v1. The client appends /decisions/check.
http.token IAM_CLIENT_TOKEN Bearer token for the Admin API. Omitted from the request when null.
http.timeout 5 Guzzle request timeout (seconds).
subject_type user Subject type sent in every decision query.
default_application IAM_CLIENT_APP Default application when a call doesn’t pass one.
default_organization IAM_CLIENT_ORG Default organization (tenant) when a call doesn’t pass one.
cache.enabled true Wrap the transport in CachingDecider.
cache.ttl 30 Decision cache TTL in seconds. <= 0 disables caching even when enabled.
cache.store null Laravel cache store name. null = default store.
gate.enabled true Register the Gate::before adapter.
gate.intercept namespaced namespaced = only abilities with :; all = every ability.

The published file

return [
    'mode' => env('IAM_CLIENT_MODE', 'local'),

    'http' => [
        'base_url' => env('IAM_CLIENT_BASE_URL'),   // e.g. https://iam.example.com/api/iam/v1
        'token'    => env('IAM_CLIENT_TOKEN'),       // Bearer for the Admin API
        'timeout'  => 5,
    ],

    'subject_type'         => 'user',
    'default_application'  => env('IAM_CLIENT_APP'),
    'default_organization' => env('IAM_CLIENT_ORG'),

    'cache' => [
        'enabled' => true,
        'ttl'     => 30,     // seconds
        'store'   => null,   // null = default store
    ],

    'gate' => [
        'enabled'   => true,
        'intercept' => 'namespaced',  // or 'all'
    ],
];

Example .env blocks

Remote (http)
Same app (local)
IAM_CLIENT_MODE=http
IAM_CLIENT_BASE_URL=https://iam.example.com/api/iam/v1
IAM_CLIENT_TOKEN=${IAM_SERVICE_TOKEN}
IAM_CLIENT_APP=billing
IAM_CLIENT_ORG=org_acme
IAM_CLIENT_MODE=local
IAM_CLIENT_APP=billing
IAM_CLIENT_ORG=org_acme

Notes

There is no fail_open key

The transport is always fail-closed: an unreachable PDP denies. Tolerating an outage is a conscious
application choice, not a config setting.

Cache TTL is your revocation latency

A short TTL (default 30s) bounds how long a revoked grant keeps being honored on each node. explain queries
are never cached regardless. See Cache decisions.

Defaults keep call sites terse

Set default_application / default_organization once (via IAM_CLIENT_APP / IAM_CLIENT_ORG) and most
calls won’t need to pass them — they’re inherited unless overridden per call. See
ABAC context & ReBAC resources.

Turn the Gate off for shadow mode

Set gate.enabled = false while the spatie bridge runs in shadow
mode, so the adapter’s enforcement doesn’t pollute decision diffing.

Cache after changing config

In production with config:cache, run php artisan config:clear (or re-cache) after editing .env so the
new transport/cache/gate settings take effect.

See also