# Entitlements Model ## Entities **Feature** (DB) Named, typed unit of functionality. The catalog of what can be entitled. - `key` — fully qualified, underscores, e.g. `iam_members`, `iam_sso` - `kind` — `bool` or `numeric` - `name` — display name **Plan** (DB) A named set of feature→entitlement mappings. - `kind` — `base` or `addon` - Has many plan entitlements (feature + configured value) **Subscription** (DB) Links a tenant to a plan for a time window. Multiple per tenant allowed. - `(tenant_id, plan_id, starts_at?, ends_at?)` - Active when now is between starts_at and ends_at, or ends_at is null **tenant_usage** (DB) Atomic counters per tenant per feature. Incremented/decremented with discipline on every write/delete. - `(tenant_id, feature_key, count)` ## Aggregation Given a tenant, find effective entitlement per feature: 1. Find all active subscriptions 2. Separate into base and addon subscriptions 3. **Base resolution** — per feature, take max across all base plan entitlement values - Numeric: max value; nil (unlimited) beats any number - Bool: OR 4. **Addon resolution** — per feature across all addon plan entitlements - Numeric: sum - Bool: OR 5. **Effective** — numeric: base + addon_sum; bool: base OR addons 6. "Not defined" in any subscription = 0 (numeric) or false (bool) Add-ons can grant features independently — a base plan is not a prerequisite for an addon to apply. ## Enforcement In the capability layer, before Create: 1. Resolve effective numeric entitlement for the resource's feature 2. Get current counter value from tenant_usage 3. If counter >= limit (and limit is not nil/unlimited), return error 4. After successful write: increment counter 5. After Delete: decrement counter ## Tenant Visibility A `Tenants_GetEntitlements` endpoint returns effective entitlements + current usage. Shape TBD: flat `{feature, limit, usage}` list vs. grouped by subscription. ## Open Questions 1. **Counter bootstrap** — when introduced, existing tenants have real resources but counters at zero. One-time migration to seed from current row counts? 2. **Counter consistency** — counter increment/decrement in the same DB transaction as the write? Assumes single DB. 3. **Feature registry** — Go constants only, or also a DB table so operators can see available features without reading code? 4. **Tenant visibility shape** — flat list or grouped by subscription? 5. **Multiple active base plans** — soft expectation of one active base plan at a time; how to surface violations to operators? 6. **Plan versioning** — when a plan's entitlements change, do existing subscribers see the new values immediately or are they pinned to the version at subscription time? 7. **Grandfathering** — tenants on an old plan that no longer exists or has been downgraded; how do subscriptions referencing a deleted/changed plan behave? 8. **Plan migrations** — moving a tenant from one plan to another; how to handle mid-period transitions (proration, overlap, gap)? 9. **Trials** — time-limited access to a higher-tier plan; modeled as a subscription with an end date, or as a distinct subscription kind? 10. **Downgrades** — when a tenant's effective entitlements decrease (plan change or subscription expiry), resources that exceed the new limit are already created; enforcement strategy (block new creates only, warn, or force-cleanup)?