License & Entitlement Contracts
Contracts only. Integrates services by reference — no cross-service ownership.
ADR: ADR-023 · ADR-021
Contract version: license-entitlement.v1
1. Layering (aligned with Payment L1/L2/L3)
| Layer |
Name |
Owner |
Example |
| L1 |
Service / module / feature entitlement |
Core Platform |
payment.module.enabled |
| L2 |
Service capability pack |
Owning service |
payment.byo_psp.basic |
| L3 |
Feature toggle / capability flag |
Owning service |
refunds_enabled |
Commercial License grants L1 (and may request L2 activation via events). Services remain authoritative for L2/L3.
2. Contracts
2.1 ServiceEntitlement
| Field |
Type |
Meaning |
tenant_id |
uuid |
|
service_identifier |
string |
Manifest id |
enabled |
bool |
L1 gate |
license_id |
uuid? |
Source license |
visible_to_tenant |
bool |
Tenant Visibility |
source |
enum |
subscription | trial | admin_grant | legacy |
2.2 ModuleEntitlement
| Field |
Type |
Meaning |
tenant_id |
uuid |
|
module_key |
string |
|
service_identifier |
string |
|
enabled |
bool |
|
license_id |
uuid? |
|
2.3 FeatureEntitlement
| Field |
Type |
Meaning |
tenant_id |
uuid |
|
feature_key |
string |
|
enabled |
bool |
|
license_id |
uuid? |
|
2.4 Quota
| Field |
Type |
Meaning |
quota_key |
string |
e.g. sms.monthly, seats |
tenant_id |
uuid |
|
limit |
int | unlimited |
|
period |
enum |
monthly | yearly | lifetime | rolling_30d |
license_id |
uuid? |
|
2.5 UsageLimit
| Field |
Type |
Meaning |
limit_key |
string |
|
tenant_id |
uuid |
|
max_value |
int |
|
enforcement |
enum |
hard | soft | observe |
2.6 CapabilityFlag
| Field |
Type |
Meaning |
capability_key |
string |
Discovery / UI gate |
tenant_id |
uuid |
|
value |
bool | string | number |
|
owner_service |
string |
Who evaluates |
2.7 TenantVisibility
| Field |
Type |
Meaning |
tenant_id |
uuid |
|
resource_kind |
enum |
service | module | bundle | admin_nav | capability |
resource_key |
string |
|
visible |
bool |
Hide inactive commercial surfaces |
reason |
string? |
|
3. Integration matrix (contracts only)
| Service |
How commercial foundation integrates |
| Identity |
AuthN/session; role checks use permissions exposed when L1+visibility allow; Identity does not own licenses |
| Payment |
L1 payment.module.enabled from Core; L2/L3 remain Payment; activation may emit payment.activation.requested |
| Accounting |
Opaque accounting.contract.requested; Accounting owns books |
| Experience |
Workspace/dashboard shells after entitlement; Experience does not own subscription |
| CRM |
Optional L1 crm.module.enabled; CRM owns CRM data |
| Communication |
Quotas (e.g. SMS) via Quota contracts; Communication owns send engines |
| Delivery / Hospitality / Healthcare / Sports / Loyalty |
Same pattern: L1 from Core license; service owns domain |
Events that invalidate caches: reuse / extend Core feature_access.changed semantics (existing platform pattern).
4. Invariants
- No service writes another service’s L2/L3 rows.
- Hidden / disabled entitlements MUST not expose admin nav, APIs, or capabilities (same isolation rule as ADR-021).
- Quotas are commercial limits; usage metering engines remain future/service-owned.