Files
member-console/design/entitlements/model.md

44 KiB

Entitlements -- Model Reference

Module: entitlements Projection of: data-model.md entitlements section

Entitlements answers: "What capabilities are available, who provisioned them, and how are they distributed?"

The resource pool is the bridge between the financial world (billing accounts, subscriptions, purchases) and the resource world (workspaces, boolean entitlements, numeric entitlements, credits). It decouples "who pays" from "who uses" while maintaining a clear audit trail between them.


resource_pools

A resource pool aggregates boolean entitlements, numeric entitlements (limits and quotas), and credits from one or more provision sources. Workspaces draw capabilities from pools via pool assignments. Pools are the unit of resource sharing: multiple workspaces can share a pool (shared mode), or a pool can be dedicated to a single workspace.

Pools are auto-created for simple cases and explicitly managed for complex ones:

  • When a billing account is created, a default pool is auto-created.
  • When a workspace is created, it is auto-assigned to its organization's default billing account's default pool.
  • Administrators can create additional pools for departmental isolation, client-funded projects, or cross-organization sponsorship.
Field Type Purpose
pool_id UUID Primary key
org_id UUID FK -> organization.organizations. The organization this pool belongs to (governance scope).
name VARCHAR(255) Display name. Auto-generated for default pools.
slug VARCHAR(100) URL-safe identifier. Unique within the organization.
pool_type VARCHAR(20) default (auto-created with billing account), shared (explicit multi-workspace), dedicated (explicit single-workspace).
is_auto_managed BOOLEAN If true, system manages this pool automatically. Default pools are auto-managed.
description TEXT What this pool is for.
status VARCHAR(20) active, suspended, archived.
suspended_at TIMESTAMPTZ When most recently suspended.
archived_at TIMESTAMPTZ When archived.
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

Constraints:

  • slug is unique within an organization.

Status lifecycle governed by the Soft-Delete and Terminal State Policy.

Relationships:

  • resource_pools (0..*) -> (1) organizations: Pools belong to an organization (governance scope). FK -> organization.organizations
  • resource_pools (1) -> (0..*) pool_provisions: Pools receive provisions from billing sources.
  • resource_pools (1) -> (0..*) pool_assignments: Pools are assigned to workspaces.
  • resource_pools (1) -> (0..*) boolean_entitlements: Pools hold binary capabilities.
  • resource_pools (1) -> (0..*) numeric_entitlements: Pools hold numeric limits and quotas.
  • resource_pools (1) -> (0..*) pool_ondemand_config: Pools may have on-demand usage configuration.

pool_provisions

A pool_provision records that a resource pool has been provisioned with capabilities from a specific source. The source may be a subscription (recurring agreement), a purchase (one-time transaction), or a grant (administrative/promotional access).

Pool provisions are the uniform interface through which all access mechanisms connect to resource pools. The entitlement system queries provisions to determine what capabilities a pool should have. It does not need to know whether the provision came from a subscription, purchase, or grant.

Field Type Purpose
provision_id UUID Primary key
pool_id UUID FK -> resource_pools. The pool being provisioned.
billing_account_id UUID FK -> billing.billing_accounts. The financial source. NULL for grants that target an org or person directly.
subscription_id UUID FK -> billing.subscriptions. If this provision comes from a subscription. NULL otherwise.
purchase_id UUID FK -> billing.purchases. If this provision comes from a purchase. NULL otherwise.
grant_id UUID FK -> grants. If this provision comes from a grant. NULL otherwise. (Intra-module, Decision 106.)
entitlement_set_id UUID FK -> entitlement_sets. Which entitlement set's rules apply. Resolved at provision-creation time: from products.entitlement_set_id for subscription/purchase sources, or directly from the grant for grant sources. Always populated.
quantity INTEGER For per-unit sources: how many units. Determines resource value multipliers via entitlement_set_rules.
status VARCHAR(20) active, suspended, ended. Derived from source status.
activated_at TIMESTAMPTZ When this provision became active.
suspended_at TIMESTAMPTZ When suspended, if applicable.
ended_at TIMESTAMPTZ When ended, if applicable.
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

Constraints (exclusive arc -- source):

CHECK (
  (subscription_id IS NOT NULL AND purchase_id IS NULL AND grant_id IS NULL)
  OR (subscription_id IS NULL AND purchase_id IS NOT NULL AND grant_id IS NULL)
  OR (subscription_id IS NULL AND purchase_id IS NULL AND grant_id IS NOT NULL)
)

Exactly one of subscription_id, purchase_id, or grant_id must be set.

Note: billing_account_id is not constrained to the same org_id as the pool. This enables cross-organization provisioning (Decision 53).

How each source type creates provisions:

Source Trigger Pool Provision Status
Subscription (active/trialing) Subscription item activated active
Subscription (past_due) Payment failed, grace period active (configurable)
Subscription (unpaid/paused) Retries exhausted or paused suspended
Subscription (canceled) Agreement terminated ended
Purchase (completed) Transaction completed active (indefinitely)
Purchase (refunded) Full refund issued ended
Grant (active) Grant valid_from reached active
Grant (expired) Grant valid_until reached ended
Grant (revoked) Admin revocation ended

Multi-item subscriptions: A subscription with three items creates three pool_provision records -- one per item. Each references a different entitlement set (resolved from each item's product) and activates that set's rules on the pool.

Relationships:

  • pool_provisions (0..*) -> (1) resource_pools: Provisions target a pool.
  • pool_provisions (0..*) -> (0..1) billing_accounts: Provisions may have a financial source. FK -> billing.billing_accounts
  • Exclusive arc -- source (subscription_id / purchase_id / grant_id):
    • pool_provisions (0..*) -> (0..1) subscriptions: Provisions may come from a subscription. FK -> billing.subscriptions
    • pool_provisions (0..*) -> (0..1) purchases: Provisions may come from a purchase. FK -> billing.purchases
    • pool_provisions (0..*) -> (0..1) grants: Provisions may come from a grant (intra-module).
  • pool_provisions (0..*) -> (1) entitlement_sets: Provisions always resolve to an entitlement set for materialization. (intra-module)

pool_provision_ladders

A pool_provision_ladder row records that a given pool provision occupies a specific rung on a plan ladder. The junction separates two orthogonal facts that Amendment #1 to Doc 31 identified as improperly conflated in the original "Option A" design: the commercial fact ("this billing source activated this pool at time T") belongs to pool_provisions, while the catalog-shape fact ("this activation occupies this ladder rung") belongs here. That separation honors doc-32's four-orthogonal-concerns framing and enables a pool provision arising from a multi-ladder bundle to register on several ladders with a single pool_provisions row rather than the redundant proliferation of multiple provision rows per activation.

The exclusion constraint is the table's raison d'être. Because PostgreSQL's GiST exclusion operator cannot express cross-table predicates, the columns pool_id, status, activated_at, and ended_at are denormalized from the parent provision and kept in sync by an AFTER UPDATE trigger on pool_provisions (see below). The constraint then enforces "at most one active provision per (pool, ladder)" across overlapping temporal windows: two rows with non-overlapping [activated_at, ended_at) windows on the same (pool_id, plan_ladder_id) pair are permitted, enabling Lago-style dual-row scheduled transitions. The WHERE (status = 'active') partial predicate ensures the GiST index is empty for ended provisions and entirely absent for cooperatives with no ladder enrollment whatsoever, honoring the self-sufficiency constraint of Decision 87.

Field Type Purpose
provision_id UUID PK component. FK -> pool_provisions. The provision that occupies this ladder rung.
plan_ladder_id UUID PK component. FK -> billing.plan_ladders. The ladder on which the provision is registered.
pool_id UUID Denormalized from pool_provisions.pool_id; required for the GiST exclusion predicate. Kept in sync by trigger.
status VARCHAR(20) Denormalized from pool_provisions.status. Binary: 'active' or 'ended'. Trial state is carried on the source subscription, not here (see note below). Kept in sync by trigger.
activated_at TIMESTAMPTZ Denormalized from pool_provisions.activated_at. Lower bound of the exclusion window. Kept in sync by trigger.
ended_at TIMESTAMPTZ Denormalized from pool_provisions.ended_at. Upper bound of the exclusion window; NULL means unbounded. Kept in sync by trigger.

Primary key: (provision_id, plan_ladder_id)

DDL:

CREATE TABLE entitlements.pool_provision_ladders (
  provision_id    UUID NOT NULL REFERENCES entitlements.pool_provisions(provision_id) ON DELETE CASCADE,
  plan_ladder_id  UUID NOT NULL REFERENCES billing.plan_ladders(plan_ladder_id),
  -- Denormalized from pool_provisions; kept in sync by trigger:
  pool_id         UUID NOT NULL,
  status          VARCHAR(20) NOT NULL,
  activated_at    TIMESTAMPTZ NOT NULL,
  ended_at        TIMESTAMPTZ,
  PRIMARY KEY (provision_id, plan_ladder_id),
  CONSTRAINT one_active_provision_per_pool_per_ladder
    EXCLUDE USING gist (
      pool_id WITH =,
      plan_ladder_id WITH =,
      tstzrange(activated_at, ended_at, '[)') WITH &&
    )
    WHERE (status = 'active')
);

CREATE INDEX pool_provision_ladders_pool_idx ON entitlements.pool_provision_ladders (pool_id);

The pool_provision_ladders_pool_idx index supports the canonical "what plan is this pool on?" query, which filters by pool alone without specifying a ladder — the primary-key index covering (provision_id, plan_ladder_id) does not serve this access pattern.

Note on trial state (status field). The junction's status vocabulary is intentionally binary: 'active' or 'ended'. Trial state is carried on the source subscription (billing.subscriptions.status = 'trialing'), not on this junction. Adding 'trialing' here would either require constraint participation — making trial and active mutually exclusive and breaking seamless conversion — or would be non-participating, in which case it redundantly projects information already present on the subscription. The vehicle-vs-position distinction (modules/GLOSSARY.md §5.1) makes the reasoning precise: trial is a vehicle state (how the subscription is currently running), not a position state (which catalog slot the pool currently holds). To answer "is this pool trialing tier T on ladder L?", join the junction against pool_provisions and billing.subscriptions:

SELECT s.status, s.trial_start, s.trial_end
FROM entitlements.pool_provision_ladders pl
JOIN entitlements.pool_provisions p     ON p.provision_id = pl.provision_id
JOIN billing.subscriptions s            ON s.subscription_id = p.subscription_id
WHERE pl.pool_id = :pool_id
  AND pl.plan_ladder_id = :ladder_id
  AND pl.status = 'active'
  AND s.status = 'trialing';

Relationships:

  • pool_provision_ladders (0..*) -> (1) pool_provisions: Each junction row belongs to exactly one provision. Cascade-deleted when the provision is deleted.
  • pool_provision_ladders (0..*) -> (1) plan_ladders: Each junction row references a ladder rung. FK -> billing.plan_ladders

pool_provision_transitions

A pool_provision_transition is the audit record of a ladder-position change affecting a pool provision — an initiation, upgrade, downgrade, or end. The table answers "what tier did this pool hold at time T, who put it there, and why?" — a question that cannot be reconstructed from pool_provisions and pool_provision_ladders timestamps alone once actor attribution and reason-for-change are required. It complements, rather than replaces, billing.subscription_changes: that table remains canonical for commercial mutations of a subscription (status transitions, period boundaries, amount changes); this table is canonical for plan-position history of a pool. Subscription-driven ladder attachments are recorded in both — intentionally — because they answer different questions (see GLOSSARY §3, "pool provision transition").

The table is scoped narrowly to ladder-position changes. Suspension and resumption lifecycle are not recorded here — those are carried by pool_provisions.status and, for subscription-driven suspensions, subscription_changes. This scope discipline is deliberate: it keeps transition_type semantically clean and makes the table's eventual absorption into a generic audit.log module mechanical rather than requiring semantic rewriting (see "View-shape contract" below).

Field Type Purpose
transition_id UUID Primary key
pool_id UUID FK -> resource_pools. The pool whose ladder position changed. Denormalized for query convenience; the same value is reachable via provision_id → pool_provisions.pool_id.
provision_id UUID FK -> pool_provisions. The provision whose ladder attachment this transition records.
plan_ladder_id UUID FK -> billing.plan_ladders. The ladder on which the transition occurred. NULL for off-ladder transitions (e.g., an add-on grant's lifecycle).
from_rank INTEGER The rank the pool held immediately prior to the transition. NULL on initiate (no prior attachment).
to_rank INTEGER The rank the pool holds immediately after the transition. NULL on end (no subsequent attachment).
transition_type VARCHAR(50) initiate, upgrade, downgrade, or end. Strictly scoped to ladder-position changes.
actor_type VARCHAR(50) operator, system, or webhook. Identifies the agent responsible for the transition.
actor_id UUID FK -> identity.persons when actor_type = 'operator'. NULL-permitted for system and webhook actors today, with room to populate it if webhook authentication later admits actor attribution.
reason TEXT Human-readable explanation. For operator actions, typically a reason the operator entered; for system transitions, a code indicating the trigger (e.g., "subscription_activated", "reapply_defaults").
effective_at TIMESTAMPTZ When the transition took effect in the business sense.
created_at TIMESTAMPTZ When the row was recorded. Distinct from effective_at to accommodate scheduled and backdated transitions.

DDL:

CREATE TABLE entitlements.pool_provision_transitions (
  transition_id    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  pool_id          UUID NOT NULL REFERENCES entitlements.resource_pools(pool_id),
  provision_id     UUID NOT NULL REFERENCES entitlements.pool_provisions(provision_id),
  plan_ladder_id   UUID REFERENCES billing.plan_ladders(plan_ladder_id),
  from_rank        INTEGER,
  to_rank          INTEGER,
  transition_type  VARCHAR(50) NOT NULL
                   CHECK (transition_type IN ('initiate', 'upgrade', 'downgrade', 'end')),
  actor_type       VARCHAR(50) NOT NULL
                   CHECK (actor_type IN ('operator', 'system', 'webhook')),
  actor_id         UUID REFERENCES identity.persons(person_id),
  reason           TEXT,
  effective_at     TIMESTAMPTZ NOT NULL,
  created_at       TIMESTAMPTZ NOT NULL DEFAULT now(),
  CONSTRAINT operator_actor_requires_id
    CHECK (actor_type != 'operator' OR actor_id IS NOT NULL)
);

CREATE INDEX pool_provision_transitions_pool_idx
  ON entitlements.pool_provision_transitions (pool_id, effective_at DESC);

CREATE INDEX pool_provision_transitions_provision_idx
  ON entitlements.pool_provision_transitions (provision_id);

Rank conventions.

  • initiate: from_rank IS NULL, to_rank is the rank of the initial attachment.
  • upgrade: both ranks populated, to_rank higher than from_rank by the ladder's ordering.
  • downgrade: both ranks populated, to_rank lower than from_rank, or to_rank IS NULL when the downgrade leaves the ladder empty and no re-application follows.
  • end: to_rank IS NULL. Recorded when a provision's ladder attachment ends without a replacement. When a supersession transition ends the outgoing provision alongside activating a superseding one, both events are recorded as separate rows (one end, one initiate/upgrade) rather than conflated.

View-shape contract. The columns are chosen to map directly onto a generic audit.log projection shaped as {resource_type, resource_id, actor_type, actor_id, action, occurred_at, recorded_at, payload}. The correspondence is: resource_type='pool_provision', resource_id=provision_id, action=transition_type, occurred_at=effective_at, recorded_at=created_at, payload={pool_id, plan_ladder_id, from_rank, to_rank, reason}. When the generic audit module graduates from the backlog, absorption of this table is mechanical rather than semantic: a UNION ALL view across pool_provision_transitions (and any peer specialized logs) yields the generic log without requiring rewrites. Should that absorption occur, this table may be deprecated into a view over audit.log without disturbing callers.

Relationships:

  • pool_provision_transitions (0..*) -> (1) resource_pools: Every transition is scoped to a pool.
  • pool_provision_transitions (0..*) -> (1) pool_provisions: Every transition is attributed to a specific provision.
  • pool_provision_transitions (0..*) -> (0..1) plan_ladders: Transitions are typically ladder-scoped; NULL permitted for off-ladder lifecycle. FK -> billing.plan_ladders
  • pool_provision_transitions (0..*) -> (0..1) persons: Operator transitions reference the responsible person. FK -> identity.persons

Sync trigger: pool_provisionspool_provision_ladders

Because the exclusion constraint on pool_provision_ladders operates against denormalized columns, those columns must remain identical to their counterparts on pool_provisions. An AFTER UPDATE trigger on pool_provisions provides this guarantee:

CREATE OR REPLACE FUNCTION entitlements.sync_pool_provision_ladders()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN
  UPDATE entitlements.pool_provision_ladders
  SET pool_id      = NEW.pool_id,
      status       = NEW.status,
      activated_at = NEW.activated_at,
      ended_at     = NEW.ended_at
  WHERE provision_id = NEW.provision_id;
  RETURN NEW;
END;
$$;

CREATE TRIGGER pool_provisions_sync_ladders
  AFTER UPDATE OF pool_id, status, activated_at, ended_at
  ON entitlements.pool_provisions
  FOR EACH ROW EXECUTE FUNCTION entitlements.sync_pool_provision_ladders();

Contract. The trigger fires after any UPDATE that touches pool_id, status, activated_at, or ended_at on pool_provisions. It propagates the new values to all matching rows in pool_provision_ladders (there may be more than one if the provision spans multiple ladders in a bundle). It does not fire on INSERT — the application code that creates a provision is also responsible for creating the corresponding junction row(s). It fires on DELETE implicitly: pool_provision_ladders carries ON DELETE CASCADE, so no trigger is required for deletions.

The trigger is required for correctness of the exclusion constraint: if a provision's status transitions to 'ended' or its temporal window shifts (e.g., in a scheduled replacement), the junction rows must reflect that change immediately, before any subsequent insertion that would otherwise violate the constraint.


grants

A grant represents administrative, promotional, or exceptional access that is not backed by a commercial transaction. No money changes hands. Access is conferred by an authorized person. Grants are the non-financial provisioning source in the resource pool system -- they answer "what does this entity have?" rather than "who pays?" (Decision 106).

Field Type Purpose
grant_id UUID Primary key
entitlement_set_id UUID FK -> entitlement_sets. The entitlement set this grant confers directly. NULL if product_id is set (resolved from the product's set).
product_id UUID FK -> billing.products. Optional. When a grant confers a specific commercial product (e.g., "granted the Pro plan by board decision"), the product is recorded for audit clarity. The entitlement set is resolved from products.entitlement_set_id at provision-creation time. NULL if entitlement_set_id is set directly.
granted_to_billing_account_id UUID FK -> billing.billing_accounts. If the recipient is a billing account. NULL otherwise.
granted_to_org_id UUID FK -> organization.organizations. If the recipient is an organization. NULL otherwise.
granted_to_person_id UUID FK -> identity.persons. If the recipient is a person. NULL otherwise.
granted_by_person_id UUID FK -> identity.persons. Who authorized this grant.
grant_reason VARCHAR(50) Classification: promotional, complimentary, legacy, sponsored, trial_extension, board_decision, other.
description TEXT Human-readable explanation.
valid_from TIMESTAMPTZ When the grant becomes effective.
valid_until TIMESTAMPTZ When the grant expires. NULL for indefinite.
status VARCHAR(20) active, expired, revoked.
revoked_at TIMESTAMPTZ When the grant was revoked, if applicable.
revoked_by_person_id UUID FK -> identity.persons. Who revoked it.
revocation_reason TEXT Why it was revoked.
metadata JSONB Additional grant attributes.
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

Constraints (exclusive arc -- recipient):

CHECK (
  (granted_to_billing_account_id IS NOT NULL AND granted_to_org_id IS NULL AND granted_to_person_id IS NULL)
  OR (granted_to_billing_account_id IS NULL AND granted_to_org_id IS NOT NULL AND granted_to_person_id IS NULL)
  OR (granted_to_billing_account_id IS NULL AND granted_to_org_id IS NULL AND granted_to_person_id IS NOT NULL)
)

Exactly one of granted_to_billing_account_id, granted_to_org_id, or granted_to_person_id must be set.

Constraints (entitlement source -- at least one):

CHECK (
  entitlement_set_id IS NOT NULL OR product_id IS NOT NULL
)

At least one of entitlement_set_id or product_id must be set. When product_id is set, the entitlement set is resolved from products.entitlement_set_id at provision-creation time. When entitlement_set_id is set directly, the grant confers capabilities without a commercial product intermediary. Both may be set if a grant confers a product and the set is denormalized for query convenience, though the typical pattern is one or the other.

Relationships:

  • grants (0..*) -> (0..1) entitlement_sets: Grants may directly reference an entitlement set. (intra-module)
  • grants (0..*) -> (0..1) products: FK -> billing.products. Grants may reference a product for audit clarity.
  • Exclusive arc -- recipient (granted_to_billing_account_id / granted_to_org_id / granted_to_person_id):
    • grants (0..*) -> (0..1) billing_accounts: FK -> billing.billing_accounts. Grants may target a billing account.
    • grants (0..*) -> (0..1) organizations: FK -> organization.organizations. Grants may target an organization.
    • grants (0..*) -> (0..1) persons (via granted_to_person_id): FK -> identity.persons. Grants may target a person.
  • grants (0..*) -> (1) persons (via granted_by_person_id): FK -> identity.persons. Grants are authorized by a person.
  • grants (1) -> (0..*) pool_provisions: Grants create pool provisions (intra-module).

pool_assignments

A pool_assignment links a workspace to a resource pool. Workspaces draw their capabilities -- boolean entitlements, numeric entitlements, and on-demand access -- from their assigned pools.

Each workspace has exactly one primary pool assignment. Secondary assignments are optional and provide additional capabilities.

Field Type Purpose
assignment_id UUID Primary key
pool_id UUID FK -> resource_pools.
workspace_id UUID FK -> organization.workspaces.
is_primary BOOLEAN Whether this is the workspace's primary pool.
status VARCHAR(20) active, suspended.
suspended_at TIMESTAMPTZ When most recently suspended.
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

Constraints:

  • Each workspace has exactly one primary pool assignment (is_primary = true).
  • The combination of pool_id and workspace_id is unique.

Entitlement resolution across multiple pools:

  • Boolean entitlements: Union semantics. If any assigned pool grants a capability, the workspace has it.
  • Numeric entitlements (limits and quotas): Primary pool is consumed first. If exhausted, secondary pools are checked in assignment order.
  • On-demand usage: Routed through the primary pool's on-demand configuration, with fallback to secondary pools.

Relationships:

  • pool_assignments (1..*) -> (1) workspaces: Every workspace has at least one pool assignment. FK -> organization.workspaces
  • pool_assignments (0..*) -> (1) resource_pools: Assignments reference a pool.

pool_ondemand_config

A pool_ondemand_config defines on-demand resource pricing available through a pool. Unlike pre-set quotas, on-demand resources are consumed without pre-commitment and billed after the fact. This is billing-module configuration, not an entitlement — it defines what happens at the boundary when entitlements are exhausted, or serves as the sole access gate for purely post-paid resources (Decision 104).

Field Type Purpose
config_id UUID Primary key
pool_id UUID FK -> resource_pools.
resource_key VARCHAR(100) FK -> resource_keys. Resource identifier (e.g., api_calls, compute_minutes, bandwidth_bytes). Shared namespace with entitlement resource keys (Decision 103).
provision_id UUID FK -> pool_provisions. The provision that triggered this config's creation. NULL for manually configured entries. Provides lifecycle coupling (Decision 102).
billing_account_id UUID FK -> billing.billing_accounts. Who pays for on-demand consumption.
rate_plan_id UUID FK -> billing.prices. What rate applies to this on-demand resource.
soft_limit BIGINT Optional spending alert threshold (smallest currency unit). NULL = no alert.
hard_limit BIGINT Optional spending cap (smallest currency unit). NULL = unlimited.
status VARCHAR(20) active, suspended.
suspended_at TIMESTAMPTZ When most recently suspended.
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

Relationships:

  • pool_ondemand_config (0..*) -> (1) resource_pools: Config belongs to a pool.
  • pool_ondemand_config (0..*) -> (0..1) pool_provisions (via provision_id): Lifecycle coupling for provision-triggered config.
  • pool_ondemand_config (0..*) -> (1) resource_keys (via resource_key): Shared resource namespace.
  • pool_ondemand_config (0..*) -> (1) billing_accounts: Config designates a payer. FK -> billing.billing_accounts
  • pool_ondemand_config (0..*) -> (1) prices: Config references a rate plan. FK -> billing.prices

boolean_entitlements

A boolean entitlement represents a binary capability that a resource pool has. Boolean entitlements are materialized from rule_type = 'boolean' entitlement_set_rules when pool provisions are activated. Toggle history (when is_enabled changes) is tracked exclusively through the audit log (Decision 43).

Field Type Purpose
entitlement_id UUID Primary key
pool_id UUID FK -> resource_pools. The pool that has this entitlement.
resource_key VARCHAR(100) FK -> resource_keys. Machine-readable feature identifier (e.g., sites, custom_domains, api_access).
is_enabled BOOLEAN Whether this entitlement is currently active.
source_provision_id UUID FK -> pool_provisions. Which provision granted this entitlement.
valid_from TIMESTAMPTZ When this entitlement became active.
valid_until TIMESTAMPTZ When this entitlement expires. NULL means valid until provision ends.
metadata JSONB Additional entitlement attributes. JSONB Governance Policy applies.
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

"Last funder standing" logic: When a provision ends, the system checks whether any other active provision on the same pool grants the same entitlement. If yes, the entitlement remains active. If no, it is deactivated.

Relationships:

  • boolean_entitlements (0..*) -> (1) resource_pools: Entitlements belong to a pool.
  • boolean_entitlements (0..*) -> (1) pool_provisions: Entitlements are granted by a provision.
  • boolean_entitlements (0..*) -> (1) resource_keys (via resource_key): Shared resource namespace.

numeric_entitlements

A numeric entitlement represents a numeric limit or quota on resource usage within a resource pool. The numeric_entitlements table stores the definition (what the limit is and what type); mutable usage state is separated into numeric_entitlement_usage (Decision 49). Provenance -- which provisions contribute to the effective limit -- is tracked in numeric_entitlement_contributions (Decision 50).

The entitlement_type discriminator distinguishes two species within the numeric genus:

  • limit: Static allocation without a reset period (e.g., 5 workspaces). The application enforces a count; no usage tracking or reset.
  • quota: Renewable consumption budget with a reset period (e.g., 1000 API calls/month). Usage is tracked and periodically reset.
Field Type Purpose
entitlement_id UUID Primary key
pool_id UUID FK -> resource_pools. The pool this numeric entitlement belongs to.
resource_key VARCHAR(100) FK -> resource_keys. Machine-readable identifier (e.g., sites_limit, storage_bytes, api_calls).
entitlement_type VARCHAR(20) limit or quota. Discriminator for the numeric genus.
resource_limit BIGINT Effective limit. -1 means unlimited. Denormalized; recomputed from numeric_entitlement_contributions by the materialization pipeline.
reset_period VARCHAR(20) For quotas: daily, monthly, yearly. NULL for limits.
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

Relationships:

  • numeric_entitlements (0..*) -> (1) resource_pools: Numeric entitlements belong to a pool.
  • numeric_entitlements (1) -> (0..*) numeric_entitlement_contributions: Numeric entitlements receive contributions from provisions.
  • numeric_entitlements (1) -> (1) numeric_entitlement_usage: Each numeric entitlement has a corresponding usage state record.
  • numeric_entitlements (0..*) -> (1) resource_keys (via resource_key): Shared resource namespace.

numeric_entitlement_contributions

A numeric entitlement contribution records a single provision's contribution to a numeric entitlement's effective limit. When a pool has multiple active provisions that grant the same resource, each provision creates a separate contribution. The entitlement's resource_limit is derived from its contributions using the applicable stacking policy (Decision 48).

Field Type Purpose
contribution_id UUID Primary key
entitlement_id UUID FK -> numeric_entitlements. The numeric entitlement receiving this contribution.
provision_id UUID FK -> pool_provisions. The provision contributing.
contributed_value BIGINT The value this provision contributes. For resource_per_unit rules, this is resource_value x provision.quantity.
stacking_policy VARCHAR(20) The stacking policy that applies to this contribution. Denormalized from the rule at materialization time.
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

Constraints:

  • The combination of entitlement_id and provision_id is unique.
  • Index on entitlement_id for the aggregation query.

Effective limit computation: For additive stacking, the effective limit is SUM(contributed_value). For maximum, it is MAX(contributed_value). For replace, it is the contribution from the most recently activated provision.

Relationships:

  • numeric_entitlement_contributions (0..*) -> (1) numeric_entitlements: Contributions target a numeric entitlement.
  • numeric_entitlement_contributions (0..*) -> (1) pool_provisions: Contributions originate from a provision.

numeric_entitlement_usage

A numeric entitlement usage record holds the mutable consumption state for a numeric entitlement, separated from the entitlement definition to eliminate write contention between the materialization pipeline (which updates definitions) and the usage increment path (which updates consumption).

Field Type Purpose
usage_id UUID Primary key
entitlement_id UUID FK -> numeric_entitlements. The entitlement definition this usage tracks.
pool_id UUID FK -> resource_pools. Denormalized from entitlement for indexed lookups.
resource_key VARCHAR(100) Denormalized from entitlement for indexed lookups without join.
current_usage BIGINT Current usage against this entitlement in the current period.
current_period_start TIMESTAMPTZ Start of current reset period. NULL for limits (non-resetting).
current_period_end TIMESTAMPTZ End of current reset period. NULL for limits (non-resetting).
last_reset_at TIMESTAMPTZ When usage was last reset.
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

Constraints:

  • entitlement_id is unique (1:1 with numeric_entitlements, in a separate table for write isolation).
  • Composite index on (pool_id, resource_key) for the hot-path lookup.

Usage increment pattern:

UPDATE numeric_entitlement_usage
SET current_usage = current_usage + $increment
WHERE entitlement_id = $id
  AND current_usage + $increment <= (SELECT resource_limit FROM numeric_entitlements WHERE entitlement_id = $id)

If no rows are returned, the limit would be exceeded.

Relationships:

  • numeric_entitlement_usage (1) -> (1) numeric_entitlements: Each usage record tracks one numeric entitlement.

entitlement_sets

An entitlement set is a named, reusable collection of entitlement rules -- the canonical unit of capability specification. Entitlement sets decouple "what capabilities are conferred" from "how they are sold" (products) and "why they were given" (grants). Products reference a set to declare what a purchase or subscription confers; grants may reference a set directly for ad hoc capability conferral without requiring a commercial artifact.

This factoring ensures that the materialization pipeline has a single, uniform input regardless of provisioning source: every pool provision resolves to exactly one entitlement set.

Field Type Purpose
set_id UUID Primary key
name VARCHAR(255) Internal name (e.g., "Pro Capabilities", "API Access", "Board Grant — Extended Storage").
description TEXT What this set represents and when it should be used.
is_active BOOLEAN Whether this set can be referenced by new products or grants. Existing references remain valid when deactivated.
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

Relationships:

  • entitlement_sets (1) -> (0..*) entitlement_set_rules: A set contains rules that define its capabilities.
  • entitlement_sets (1) -> (0..*) products: Products reference a set to declare what they confer. FK <- billing.products.entitlement_set_id
  • entitlement_sets (1) -> (0..*) grants: Grants may reference a set directly for ad hoc conferral.
  • entitlement_sets (1) -> (0..*) pool_provisions: Provisions always resolve to a set for materialization.

entitlement_set_rules

An entitlement set rule defines a single capability within an entitlement set. This table uses single-table inheritance: the rule_type discriminator determines which set of fields is populated, with a CHECK constraint enforcing mutual exclusivity. Four rule types exist: boolean (binary capabilities), limit (static numeric allocations), quota (renewable consumption budgets), and credit (included prepaid credits).

Formerly product_entitlement_rules (Decision 107, Document 29). Re-parented from product_id to set_id to support the entitlement set abstraction, enabling grants to confer capabilities without requiring a product intermediary.

Field Type Purpose
rule_id UUID Primary key
set_id UUID FK -> entitlement_sets. The set this rule belongs to.
rule_type VARCHAR(20) boolean, limit, quota, or credit.
resource_key VARCHAR(100) FK -> resource_keys. For boolean, limit, and quota rules: the resource identifier. NULL for credit rules.
resource_value BIGINT For limit and quota rules: the amount granted. -1 for unlimited. NULL for boolean and credit rules.
resource_per_unit BOOLEAN For limit and quota rules: whether the value is multiplied by the provision's quantity. NULL for boolean and credit rules.
stacking_policy VARCHAR(20) For limit and quota rules: how this entitlement combines with contributions from other provisions. additive (sum all contributions), maximum (take the highest), replace (most recent provision wins). NULL for boolean and credit rules. Default: additive.
reset_period VARCHAR(20) For quota rules: daily, monthly, or yearly. Required for quota rules, NULL for all other rule types.
credit_amount INTEGER For credit rules: the credit amount in smallest currency unit. NULL for all other rule types.
credit_currency VARCHAR(3) For credit rules: the currency (ISO 4217). NULL for all other rule types.
description TEXT Human-readable explanation of what this rule grants.
is_active BOOLEAN Whether this rule is currently in effect.
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

Constraints:

CHECK (
  (rule_type = 'boolean' AND resource_key IS NOT NULL
    AND resource_value IS NULL AND stacking_policy IS NULL
    AND reset_period IS NULL AND resource_per_unit IS NULL
    AND credit_amount IS NULL AND credit_currency IS NULL)
  OR (rule_type = 'limit' AND resource_key IS NOT NULL AND resource_value IS NOT NULL
    AND reset_period IS NULL
    AND credit_amount IS NULL AND credit_currency IS NULL)
  OR (rule_type = 'quota' AND resource_key IS NOT NULL AND resource_value IS NOT NULL
    AND reset_period IS NOT NULL
    AND credit_amount IS NULL AND credit_currency IS NULL)
  OR (rule_type = 'credit' AND credit_amount IS NOT NULL AND credit_currency IS NOT NULL
    AND resource_key IS NULL AND resource_value IS NULL
    AND stacking_policy IS NULL AND reset_period IS NULL AND resource_per_unit IS NULL)
)

For rule_type = 'boolean': resource_key is required; all numeric, quota, and credit fields are NULL. For rule_type = 'limit': resource_key and resource_value are required; reset_period is NULL. For rule_type = 'quota': resource_key, resource_value, and reset_period are required. For rule_type = 'credit': credit_amount and credit_currency are required; all resource fields are NULL. stacking_policy defaults to additive when NULL (applies to limit and quota rules).

Relationships:

  • entitlement_set_rules (0..*) -> (1) entitlement_sets: Rules belong to a set. (intra-module)
  • entitlement_set_rules (0..*) -> (0..1) resource_keys: Boolean, limit, and quota rules reference a resource key. (intra-module)
  • Consumed by the materialization pipeline when pool provisions activate or deactivate, producing boolean_entitlements, numeric_entitlements, numeric_entitlement_contributions, and credit_grants.

resource_keys

A resource key is a canonical identifier in the shared namespace that bridges the entitlement system and the on-demand billing system (Decision 103). Both sides of the boundary — entitlement_set_rules.resource_key, boolean_entitlements.resource_key, numeric_entitlements.resource_key, pool_ondemand_config.resource_key, usage_events.resource_key — FK into this table. A typo (api-calls vs. api_calls) produces an FK violation rather than a silent namespace mismatch.

Field Type Purpose
resource_key VARCHAR(100) Primary key. Machine-readable identifier (e.g., api_calls, storage_bytes, workspaces).
display_name VARCHAR(255) Human-readable name (e.g., "API Calls", "Storage").
description TEXT What this resource represents.
unit VARCHAR(50) Unit of measurement (e.g., call, byte, seat).
created_at TIMESTAMPTZ

usage_events

A usage_event records consumption of a resource by a workspace. Usage events are append-only observational data — they record what happened, not what should be charged. All consumption paths — quota decrements, credit deductions, and on-demand charges — generate usage events. The resolution_path discriminator identifies which path resolved each event.

Field Type Purpose
event_id UUID Primary key
workspace_id UUID FK -> organization.workspaces. Where consumption occurred.
pool_id UUID FK -> resource_pools. Which pool this was routed through.
resource_key VARCHAR(100) FK -> resource_keys. What was consumed (e.g., api_calls, compute_minutes).
quantity BIGINT How much was consumed (in the resource's unit).
unit VARCHAR(50) Unit of measurement (e.g., call, second, byte).
event_timestamp TIMESTAMPTZ When consumption occurred.
resolution_path VARCHAR(20) How this consumption was resolved: quota, credit, or on_demand.
billing_account_id UUID FK -> billing.billing_accounts. Resolved payer at time of event. NULL for quota-resolved events with no direct billing association.
metadata JSONB Additional event attributes. JSONB Governance Policy applies.
created_at TIMESTAMPTZ

Relationships:

  • usage_events (0..*) -> (1) workspaces: Events occur in a workspace. FK -> organization.workspaces
  • usage_events (0..*) -> (1) resource_pools: Events are routed through a pool.
  • usage_events (0..*) -> (0..1) billing_accounts: Events may have a resolved payer (NULL for quota-resolved events). FK -> billing.billing_accounts
  • usage_events (0..) -> (0..) pending_charge_usage_events: Events may be linked to the pending charges they contributed to. FK -> billing.pending_charge_usage_events
  • usage_events (0..) -> (0..) credit_transactions: Events may be linked to real-time credit deductions (prepaid topology). FK -> billing.credit_transactions