net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md
tegwick bee22db620
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
docs(canon): reconcile workload and tenant grouping semantics
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02929-244b-7391-b933-c04010e8eedb
2026-08-22 14:53:31 +02:00

9.9 KiB

id type title domain status version created updated scope adr related
netkingdom-tenant-engine-boundary-contract standard NetKingdom Tenant Engine Boundary Contract v0.1 netkingdom accepted 0.1 2026-07-23 2026-08-22 tenant-domain-boundaries
docs/adr/ADR-0006-recursive-multi-tenant-identity-authorization.md
docs/adr/ADR-0013-tenant-onboarding-grouping-taxonomy.md
docs/adr/ADR-0014-tenant-capability-roles-and-tenant-engine-ownership.md
canon/standards/iam-profile_v0.3.md
canon/standards/user-engine-boundary-contract_v0.1.md
canon/standards/credential-management_v0.2.md
docs/platform-identity-security-architecture.md

NetKingdom Tenant Engine Boundary Contract v0.1

Purpose

This contract defines how tenant-engine integrates into the NetKingdom landscape without duplicating identity, authorization, user/membership, pricing, deployment, or secret-custody responsibilities that other services already own.

tenant-engine owns tenant-as-an-entity facts: existence, identifier and grouping (ADR-0013), capability roles (ADR-0014), plan/subscription assignment, and — reserved, not yet implemented — guardrail/quota policy. It is not an identity provider, not a policy decision point, not a billing processor, not a pricing-model engine, and not the user/membership domain. NetKingdom owns this cross-repo boundary contract; tenant-engine and its adapters must preserve the ownership rules below even in local/standalone deployments.

Ownership Model

Concern Owner tenant-engine responsibility
OIDC discovery, login, token issuance key-cape, Keycloak Supply tenant_roles via a read API at issuance time; never issues tokens itself
Tenant identifier format and grouping vocabulary NetKingdom (iam-profile_v0.3.md, ADR-0013) Store and validate tenant records against the ratified vocabulary; does not redefine it
Tenant capability-role vocabulary NetKingdom (ADR-0014) Store role grants against the ratified vocabulary (PLTF/IAM/VEN/CUS); does not redefine it
Users, profiles, memberships user-engine References tenant_id as an external key only; never stores user or membership records
Fine-grained authorization decisions flex-auth Serves as an authoritative data source flex-auth queries live for high-stakes checks; makes no authorization decisions itself
Pricing-model / plan definitions adaptive-pricing References plan definitions by id; never forks or duplicates plan terms locally
Runtime secrets OpenBao / secrets-engine Tenant-scoped OpenBao paths (e.g. tenants/<name>/...) reference tenant-engine's tenant registry as the naming authority; tenant-engine never stores secret material
Deployment mechanics Railiance Publishes readiness signals only
Credential routing/discovery ops-warden tenant-engine's own service credentials (if any) are routed the same way as any other lane; tenant-engine is not a routing authority

Source-Of-Truth Matrix

Resource kind Source of truth tenant-engine relation Boundary rule
Tenant record (existence, current grouping, name/slug) tenant-engine Canonical owner Current grouping must be one of ADR-0013's taxonomy, or the reserved ungrouped platform/coulomb classifications; the identifier segment is historical and must not be used as current grouping
Tenant capability roles tenant-engine Canonical owner Non-exclusive; grant/revoke are audited mutations (see Grant Contract below), never free-form field updates
Plan/subscription assignment tenant-engine Canonical owner References an adaptive-pricing plan id; does not define plan terms
Pricing-model / plan definitions adaptive-pricing Consume by reference only tenant-engine must not cache plan terms beyond what's needed to resolve which roles a plan currently grants
Guardrail/quota policy (spend limits, entity/action counts) tenant-engine (reserved) Canonical owner once designed Not implemented by this contract version — reserved namespace only, see Guardrail Policy section
tenant_roles token claim tenant-engine (live) / key-cape (cached copy) tenant-engine is authoritative; the token claim is a point-in-time cache Privileged/high-stakes decisions MUST re-query tenant-engine live — see IAM Profile v0.3, "Tenant Roles" section — never trust the cached claim alone
Tenant identifier claim shape (tenant) NetKingdom / IAM Profile contract tenant-engine validates against it; does not mint the claim format The wire format stays owned by the profile; the identifier is immutable, including its historical grouping segment; tenant-engine owns which concrete tenant values currently exist
User/membership records scoped by a tenant user-engine No relation tenant_id is the only key shared between the two services; tenant-engine never stores or reads user data
Authorization decisions flex-auth Data source only tenant-engine never enforces access itself; it answers queries flex-auth's policy packages issue

The tenant identifier and current grouping are intentionally independent after creation. tenant-engine MAY change the record's grouping through an audited mutation without renaming tenant_id. Reads and domain events expose current grouping explicitly. No API, guardrail, pricing, or authorization consumer may recover current grouping by parsing tenant_id.

Tenant Role & Plan Grant Contract

Every role grant or revocation is an audited mutation, not a direct field write, and must carry:

Field Requirement
tenant_id Stable tenant identifier
role One of PLTF, IAM, VEN, CUS
grant_reason plan_assignment, manual_grant, or platform_default
plan_id adaptive-pricing plan id; required when grant_reason: plan_assignment, otherwise null
granted_by Subject or system that performed the grant
granted_at Timestamp
revoked_at Nullable timestamp
correlation_id Request/import correlation id

Rules:

  • Plan-linked roles (starting with IAM) are granted and revoked automatically as a consequence of plan-assignment changes in tenant-engine; manual grants are the exception path and must always record granted_by.
  • trial-grouped tenants may hold any role without a plan requirement — grant_reason: platform_default is valid for trial-eligible roles with no plan_id (ADR-0014).
  • Every grant or revocation emits a domain event. After a revocation, key-cape's previously-cached tenant_roles claim is considered stale until the token naturally expires — v0.1 relies on the profile's short token lifetimes for bounded staleness, not push invalidation.
  • Cross-tenant grants are rejected; a grant always targets exactly one tenant_id.

Authorization Contract

tenant-engine is a data source, not a policy enforcement point. flex-auth remains the policy decision point for authorization outcomes; key-cape remains the token issuer. tenant-engine exposes:

  • a read API key-cape calls at token-issuance time to source the cached tenant_roles claim;
  • a live-lookup API flex-auth calls for high-stakes decisions (assurance.level >= aal2-class actions, per IAM Profile v0.3), returning current roles, grouping, and plan state;
  • a write API, authorization-gated by flex-auth itself, for grant/ revoke/plan-assignment mutations — tenant-engine does not self-authorize its own writes.

Performance model:

  • the live-lookup API must be fast and highly available, since flex-auth is required to call it synchronously on the request path for privileged actions; slow or unavailable tenant-engine lookups fail closed (deny), never fail open. The adapter existing without a production caller does not satisfy this contract and does not permit either service to claim I3;
  • the cached-claim read API used by key-cape may be slower, since it only runs at token issuance, not per-request;
  • request-scoped memoization within a single flex-auth decision is allowed; cross-request caching of live-lookup results is not, since it would recreate the staleness risk the live path exists to avoid.

Guardrail Policy (Reserved, Not Implemented)

Named here so the shape exists before it's designed, matching ADR-0014:

  • Spend limits — trial-grouped tenants default to a zero spend budget.
  • Entity count limits — reserved, not yet specified.
  • Action count limits — reserved, not yet specified.
  • Enforcement point, override process, and how guardrails interact with capability roles (a trial tenant holding VEN is still bound by trial guardrails) are all open design questions for tenant-engine's own workplan, not resolved by this contract.

Audit Correlation Contract

Every tenant-engine mutation (tenant creation, role grant/revoke, plan assignment) must create or reference a correlation bundle:

Field Requirement
correlation_id Stable id propagated across request, decision, audit, and event records
actor IAM Profile actor envelope (who performed the mutation)
tenant_id Tenant affected
resource and action e.g. tenant-engine:role, grant / revoke
authorization_decision_id flex-auth decision id backing the write
outbox_event_id Event id emitted for the mutation
change_summary Redacted mutation summary

Non-Goals

  • tenant-engine does not process payments or hold financial instruments — it records which plan a tenant is assigned to, sourced from whatever system actually processes payment (not yet identified; out of scope for this contract).
  • tenant-engine does not define pricing models, tiers, or terms — adaptive-pricing does.
  • tenant-engine does not store user accounts, profiles, or memberships — user-engine does.
  • tenant-engine does not make authorization decisions — flex-auth does.