Clearing Entitlements
The capability plane — what it gates, its report / strict / off enforcement postures, unprovisioned grace, and tenant-scoped evaluation.
RBAC answers "may this caller do this?"; entitlements answer a different question — "is this tenant provisioned for this capability?" They run as a separate plane alongside authorization, and a request must satisfy both.
Two gates, not one
A caller can be a tenant admin (passes RBAC) and still be refused a workflow
publish because the tenant is not entitled to that workflow type. RBAC gates
the identity; entitlements gate the tenant's plan. This page covers the auth
behavior of the entitlement plane; for the plan/quota model see
Concepts → Entitlements.
What the plane gates
Entitlement checks are wired at specific enforcement points where a tenant's plan should constrain behavior:
| Surface | Gated operations | Requirement prefix |
|---|---|---|
| Workflow lifecycle | Start, publish, and schedule-activation of a workflow definition | workflow.start / workflow.publish / workflow.schedule |
| Routing strategy | Selecting a routing/pipeline strategy | strategy.* / strategy.pipeline.* |
| Queue admission | Queue-admission governance operations | routing.admission.<operation> |
| Connector execution | Provider use; action, trigger, resolver, proxy, sync, and function execution | connector.action.* / connector.trigger.* … |
Each check builds a subject (e.g. {Kind: "workflow.definition", Key: <type>}) and
a requirement key, then evaluates it against the tenant's entitlement snapshot.
Enforcement postures
The behavior when a tenant's required entitlement is missing, stale, or the evaluator is unavailable is controlled by one setting:
export DUCTOR_ENTITLEMENT_ENFORCEMENT=report # report | strict | off| Mode | Behavior | When to use |
|---|---|---|
report (default) | Evaluate and admit, but emit a loud grace-admit event for every request that would be denied under strict. | Safe migration default — no outage, but not a silent fail-open. |
strict | Fail closed on missing/stale/unavailable facts for the enforced surfaces. Explicit denies always deny. | Production, once snapshots are populated. |
off | Legacy fail-open (deny-list only): explicit denies still deny, everything else admits. Announced once at startup. | Escape hatch / legacy compatibility. |
Read the table with one rule in mind: the posture only decides what happens when
a fact is missing, stale, or unavailable. An explicit deny denies in every
mode, including off:
`report` is a migration ramp, not a destination
Report mode lets you turn entitlements on and watch exactly which requests
would be denied — via the grace-admit events — before flipping to strict.
An invalid enforcement value is treated as a visible misconfiguration: it is
logged and falls back to the safe report posture rather than crashing boot or
silently fail-opening.
Unprovisioned grace
strict mode alone would instantly deny tenants that have never been onboarded to
any entitlement source. The unprovisioned grace softens exactly that case:
export DUCTOR_ENTITLEMENT_GRACE_UNPROVISIONED=true # defaultWhen true (the default), a tenant that has no entitlement source and no
snapshot at all is admitted under a loud, observable bootstrap grace — so
enabling strict does not instantly lock out un-onboarded tenants. Genuine gaps
still fail closed: a stale snapshot, a present-but-incomplete snapshot, or a
tenant that has sources but no snapshot are all denied. Set it to false to
remove the grace entirely.
Tenant-scoped evaluation
Every entitlement check is evaluated in the tenant's context — there is no global
entitlement decision. A check that cannot resolve a tenant is refused rather than
admitted: the enforced surfaces require tenant-scoped evaluation, and a missing
tenant context yields ErrEntitlementDenied. Environment-qualified checks resolve
the environment first; an archived or locked environment cannot satisfy an
entitlement check.
Startup posture logging
On boot the entitlement service logs its resolved enforcement posture, so an
operator can confirm from the logs whether the deployment is running in report,
strict, or off — and whether unprovisioned grace is on.
Where the plan itself lives
Entitlements gate against a tenant's snapshot; how that snapshot maps to plans and quotas — including hot-path quota enforcement — is covered in Concepts → Entitlements, while the Stripe billing integration that populates plan tiers is documented in Billing & Usage → Plans.
Members
The workspace member roster and its lifecycle — invite, assign roles, disable, reenable, and remove — where a member is modeled as a tenant API key plus its RBAC role bundle.
Tenancy Isolation
How every request is pinned to a tenant, how the token–tenant relationship is resolved, and how the cross-tenant boundary is enforced.