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

11 KiB

Entitlements -- Interfaces

Module: entitlements

This document records the cross-module interface surface of the entitlements module: what other modules depend on (provided interfaces), what this module depends on (consumed interfaces), and the behavioral contracts that govern these boundaries.


Provided Interfaces (Other Modules Depend on Us)

pool_id (entitlements.resource_pools.pool_id)

Consumed by: organization module FK column: organization.role_assignments.scope_pool_id Cardinality: 0..* role assignments per pool Semantics: Pool-scoped role assignments allow administrators to grant permissions scoped to a specific resource pool. This is the mechanism for pool administration delegation. The organization module references pool_id to scope role assignments; it does not interpret or depend on pool contents (entitlements, quotas, provisions).

Stability guarantee: pool_id is a UUIDv7 primary key. Pools are never physically deleted (ON DELETE RESTRICT as universal default, Decision 33). A pool may transition to archived status, but the row and its pool_id persist indefinitely.

entitlement_set_id (entitlements.entitlement_sets.set_id)

Consumed by: billing module FK column: billing.products.entitlement_set_id Cardinality: 0..* products per entitlement set Semantics: Products reference an entitlement set to declare what capabilities a purchase or subscription confers. The billing module does not interpret set contents (rules, resource keys); it carries the entitlement_set_id so that the provision-creation path can resolve and denormalize it onto pool_provisions.

Stability guarantee: set_id is a UUIDv7 primary key. Entitlement sets are never physically deleted. A set may be deactivated (is_active = false), but the row and its set_id persist indefinitely. Existing products and grants referencing a deactivated set remain valid.

usage_events (entitlements.usage_events table)

Consumed by: billing module FK references:

  • billing.credit_transactions.usage_event_id -> entitlements.usage_events.event_id -- real-time credit deductions (prepaid topology)
  • billing.pending_charge_usage_events.event_id -> entitlements.usage_events.event_id -- billing attribution linkage

Cardinality: 0..* credit transactions per usage event; 0..* pending charge links per usage event Semantics: Usage events are append-only universal consumption records. The billing module references individual events to link them to the financial transactions they produce -- either real-time credit deductions (Topology B, prepaid) or pending charges that roll up into invoice line items (Topology A, postpaid). The billing module never modifies usage events; it creates its own records that reference them.

Stability guarantee: Usage event rows are append-only; no field is updated after creation. event_id is a UUIDv7 primary key that persists indefinitely. The billing_account_id on the event is denormalized at creation time and never changes, even if the pool's on-demand configuration subsequently changes. For quota-resolved events, billing_account_id may be NULL.


Consumed Interfaces (We Depend on Other Modules)

From organization module (schema: organization)

FK Column References Purpose
resource_pools.org_id organization.organizations.org_id Governance scope: which organization governs this pool
pool_assignments.workspace_id organization.workspaces.workspace_id Capability delivery: which workspace receives capabilities from this pool
usage_events.workspace_id organization.workspaces.workspace_id Usage attribution: which workspace generated this consumption

Why the dependency exists: Resource pools exist to provide capabilities to workspaces within organizations. The organization provides the governance container (which org controls this pool) and the resource container (which workspace receives capabilities). Without these references, the pool abstraction has no consumers and no governance scope.

From billing module (schema: billing)

FK Column References Purpose
pool_provisions.billing_account_id billing.billing_accounts.billing_account_id Financial source identification
pool_provisions.subscription_id billing.subscriptions.subscription_id Subscription as provision source (exclusive arc)
pool_provisions.purchase_id billing.purchases.purchase_id Purchase as provision source (exclusive arc)
grants.product_id billing.products.product_id Optional product reference for audit clarity on grants
grants.granted_to_billing_account_id billing.billing_accounts.billing_account_id Billing account as grant recipient
pool_ondemand_config.billing_account_id billing.billing_accounts.billing_account_id On-demand charge routing (payer designation)
pool_ondemand_config.rate_plan_id billing.prices.price_id On-demand pricing rate
pool_provision_ladders.plan_ladder_id billing.plan_ladders.plan_ladder_id Catalog-shape registration: records which plan ladder rung a provision occupies; carries the exclusion constraint enforcing tier exclusivity per (pool, ladder)
usage_events.billing_account_id billing.billing_accounts.billing_account_id Resolved payer at event time

Why the dependency exists: Resource pools bridge the financial world and the resource world. The billing module provides the financial sources (billing accounts, subscriptions, purchases) and the product catalog (products, prices) that the pool system consumes. Pool provisions normalize these diverse access sources into a uniform interface for materialization. Grants (Decision 106), entitlement sets, and entitlement set rules now live within the entitlements module. The pool_provisions.entitlement_set_id and entitlement_set_rules.set_id references are intra-module. The billing module's products.entitlement_set_id is a consumed interface from entitlements (reverse direction from the prior product_entitlement_rules.product_id pattern). The pool_provision_ladders junction is the sole point at which entitlements directly references billing's plan ladder catalog: it crosses the module boundary to record catalog-shape facts (which ladder rung a provision occupies) while keeping the commercial facts (the provision itself) within the entitlements module.

entitlement_sets and entitlement_set_rules (intra-module)

entitlement_sets and entitlement_set_rules (formerly product_entitlement_rules, Decision 107) are intra-module tables. The entitlement set abstraction decouples capability specification from commercial packaging: products reference a set via products.entitlement_set_id (a consumed interface from entitlements into billing), and grants may reference a set directly via grants.entitlement_set_id for ad hoc capability conferral.

entitlement_set_rules.resource_key is an intra-module FK referencing resource_keys. entitlement_set_rules.set_id is an intra-module FK referencing entitlement_sets.

The materialization pipeline reads rules keyed by set_id (resolved from pool_provisions.entitlement_set_id):

  • For rule_type = 'boolean': creates or updates boolean_entitlements rows.
  • For rule_type = 'limit' or 'quota': creates or updates numeric_entitlements and numeric_entitlement_contributions rows.
  • For rule_type = 'credit': creates credit_grants in the billing module with source_provision_id provenance (cross-module write, Decision 100).
  • The pipeline never writes to entitlement_set_rules.

resource_keys as Shared Namespace

Consumed from: shared (Decision 103) Table: resource_keys Access pattern: FK reference from both entitlement and on-demand systems

The resource_keys table enforces a shared namespace between the entitlement system and the on-demand billing system. Both entitlement_set_rules.resource_key and pool_ondemand_config.resource_key FK into resource_keys.resource_key. This ensures that a typo (e.g., api-calls vs. api_calls) produces an FK violation rather than a silent mismatch between a quota and its corresponding on-demand config.


Interface Contracts

Organization Module Guarantees

  • organizations.org_id remains stable through the organization's lifecycle. Organizations are never physically deleted.
  • workspaces.workspace_id remains stable through the workspace's lifecycle. Workspaces are never physically deleted. A workspace may be suspended or archived, but the row persists.

Billing Module Guarantees

  • products.product_id remains stable when a product is deactivated (is_active = false). Products are catalog entries that persist indefinitely for historical reference.
  • billing_accounts.billing_account_id remains stable when a billing account is suspended or closed. The row persists for financial audit purposes.
  • subscriptions.subscription_id and purchases.purchase_id remain stable through their lifecycle transitions. These are referenced by pool provisions and must persist for provision provenance.
  • prices.price_id remains stable when a price is deactivated. Referenced by pool_ondemand_config.rate_plan_id for on-demand pricing.

Entitlements Module Guarantees (to Consumers)

  • resource_pools.pool_id remains stable indefinitely. Pools transition through status states (active, suspended, archived) but are never physically deleted.
  • usage_events rows are append-only. No field is updated after creation. event_id persists indefinitely.
  • The materialization pipeline is idempotent: calling materialize_pool_entitlements(pool_id) multiple times with the same pool state produces the same result.
  • Entitlement state (boolean_entitlements.is_enabled) is a pure function of active provisions and their entitlement set rules. No administrative override mechanism exists (Decision 51).

Behavioral Expectations

  • Provision lifecycle: When a billing source (subscription or purchase) changes status, the billing module or application code is responsible for updating the corresponding pool_provisions status and invoking the materialization pipeline. When a grant changes status, the entitlements module manages this internally. The entitlements module does not poll billing sources.
  • On-demand config payer resolution: When a usage event is recorded, the payer is resolved from pool_ondemand_config.billing_account_id at event creation time. This is a point-in-time snapshot; subsequent changes to the on-demand config do not retroactively change event attribution. For quota-resolved events, billing_account_id may be NULL.
  • Cross-organization provisioning: pool_provisions.billing_account_id is unconstrained relative to the pool's org_id (Decision 53). The entitlements module does not enforce cross-org governance; that is deferred application logic.