Core Concepts

Entitlement Model

How Ductor scopes tenants and enforces plan and quota limits across the API and hot path.

Ductor is multi-tenant from the ground up. Every pool, rule, connection, and workflow run is scoped to a tenant, and entitlements decide what each tenant is allowed to do and how much of it. Entitlements sit on top of tenant isolation — isolation is what makes a limit meaningful, entitlements are the limits themselves.

Plans and quotas

Entitlements combine two ideas:

  • Plan — the set of features and ceilings a tenant is provisioned for (integrates with Stripe billing where enabled). For how plan tiers are populated from Stripe billing and enforced, see Billing & Usage → Plans & entitlement enforcement and the Stripe integration.
  • Quota — the runtime budget a tenant may consume: request volume, capacity, concurrency, and similar counters.

When a tenant exceeds what it is entitled to, Ductor fails visibly rather than silently degrading. Quota exhaustion surfaces as a domain error (ErrQuotaExceeded, wrapped by QuotaError) that the transport layer maps to a precise HTTP / Connect status — so clients get a 429-style signal they can act on, not a mysterious empty result.

Fail visibly, never silently

This mirrors Ductor's core principle: every path either works correctly or fails observably. An over-quota tenant gets a clear, typed error — never a dropped request.

Enforcement on the hot path

Entitlement checks run where the load is. Capacity is tracked with atomic Redis Lua increments and decrements, so concurrency and rate ceilings are enforced consistently across every pod without a central lock. Because the check is cheap and colocated with routing, entitlements gate the routing pipeline without adding a round-trip.

The same atomic-counter approach appears in the tiered queue, where per-tenant and per-pool concurrency limits are enforced at lease time. Whether work arrives through a synchronous routing call or an asynchronous workflow step, the ceiling is the same and it's enforced the same way.

Errors you'll see

ErrorMeaning
ErrQuotaExceeded / QuotaErrorThe tenant is over its runtime budget.
ErrExhausted / ExhaustedErrorA capacity-limited target has no headroom.
ErrConflict / StateErrorThe requested change conflicts with current state.

All of these are declared in domain/errors/ and mapped to protocol codes in the transport layer, so behavior is identical whether a client speaks REST or Connect-RPC. Because the errors are typed domain sentinels rather than ad-hoc strings, a client can branch on them programmatically — an over-quota condition is distinguishable from a state conflict without string matching.

Enforcement postures

This page is the what and why of entitlements. The runtime enforcement posture — how a missing, stale, or unavailable entitlement is handled (report vs strict vs off), the unprovisioned bootstrap grace, and exactly which surfaces are gated (workflow publish/schedule, strategy selection, queue admission, connector execution) — is documented in Auth & Security → Entitlements.

Where to go next