tenant-engine/INTENT.md
tegwick 5d57c7d488 TEN-WP-0003: FlexAuthWriteAuthorizer -- gate writes through flex-auth
flex_auth.py: CheckRequest + FlexAuthCheckClient against flex-auth's real
POST /v1/check contract (schemas/check_request.schema.json,
decision_envelope.schema.json, read directly from the flex-auth repo, not
guessed). Fail-closed by construction: only effect=="allow" authorizes;
every other effect, non-200, malformed body, or transport failure resolves
to deny, nothing raises past is_allowed().

authz.FlexAuthWriteAuthorizer implements the existing WriteAuthorizer
Protocol. Action -> resource-type mapping coordinated with FLEX-WP-0008's
planned vocabulary (both repos reference the same table).
DefaultDenyWriteAuthorizer stays the fallback when no flex-auth URL is
configured.

config.py: Settings.from_env(), mirroring qonto-assistant's pattern.
docs/flex-auth-integration.md documents the contract, fail-closed rule,
and current real state (denies everything until FLEX-WP-0008 lands).

60 tests passing. Verified live twice over real HTTP between separate
processes (not just MockTransport): a deny-returning flex-auth double
produces 403 from POST /tenants, an allow-returning one produces 201.

Also registered (not implemented) the two workplans this depends on for a
complete picture: flex-auth/FLEX-WP-0008 (protected-system registration --
what makes allow reachable) and key-cape/KEY-WP-0005 (discovered key-cape
emits none of iam-profile_v0.3.md's core claims yet, not just missing
tenant_roles -- a bigger, security-sensitive gap flagged rather than
quietly worked around).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 22:54:44 +02:00

6.3 KiB

repo updated
tenant-engine 2026-07-23

INTENT

Purpose

tenant-engine exists to be the single, canonical source of truth for what a tenant is on the NetKingdom platform — its existence, its onboarding grouping, its capability roles, and its plan/subscription assignment — separate from who its users are (user-engine), who authenticates it (key-cape/Keycloak), who authorizes actions against it (flex-auth), and what it pays for a given plan (adaptive-pricing).

Full ownership boundary, formally: net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md.

Why This Exists

No service in the fleet currently owns tenant-as-an-entity facts as a queryable resource. user-engine's own boundary contract explicitly scopes it to consuming tenant identifiers and storing records scoped by a tenant — not owning the tenant record itself. The IAM Profile defines the shape of the tenant claim, not a live directory of which tenants exist, what they're allowed to do, or what plan they're on.

That gap became concrete during Binky Hedgehog GmbH's onboarding as the platform's first tenant outside tenant:coulomb (key-cape/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md): a tenant's capability roles (PLTF/IAM/VEN/CUS, non-exclusive — a tenant can hold several at once, and its roles can change independently of how it was onboarded) needed a real owner, a real carrying mechanism, and a real governance model (role grants are usually a consequence of a paid plan). See net-kingdom/docs/adr/ADR-0013-tenant-onboarding-grouping-taxonomy.md and ADR-0014-tenant-capability-roles-and-tenant-engine-ownership.md.

Primary Utility

tenant-engine provides:

  • Tenant records: existence, identifier, and onboarding grouping (trial/friendly/single/small/medium/large/enterprise/ consumer/family/community/association/agentic, per ADR-0013).
  • Capability role grants: PLTF/IAM/VEN/CUS, with an audited grant/revoke trail linking most grants to a plan-assignment change.
  • Plan/subscription assignment: which adaptive-pricing plan a tenant currently holds, referenced by id — never a duplicated copy of plan terms.
  • A cache-read API key-cape calls at token-issuance time to source the optional tenant_roles token claim (IAM Profile v0.3).
  • A live-lookup API flex-auth calls synchronously for privileged, high-stakes decisions — the case where a cached claim isn't trustworthy enough (a stale VEN grant surviving a plan cancellation is not an acceptable risk for money-movement or credential-vending actions).
  • Reserved, not yet implemented: guardrail/quota policy (spend limits, entity/action counts) — trial-grouped tenants default to a zero spend budget; design is future work.

Intended Users

  • key-cape / Keycloak, at token issuance time (cache-read API).
  • flex-auth, at authorization-decision time (live-lookup API, and the target of write-authorization for grant/revoke/plan mutations).
  • Operators and tenant administrators, indirectly, through whatever admin surface eventually consumes tenant-engine's APIs (not yet built).
  • adaptive-pricing, as the source tenant-engine references for plan definitions (not the other way around).

Strategic Role

tenant-engine sits beside user-engine, not inside it — a deliberate bounded-context split, not an oversight. user-engine's aggregate root is the person; tenant-engine's is the tenant. Consumers that only need a fast, security-critical tenant-role lookup (flex-auth on every privileged decision, key-cape on every token issuance) should never need to depend on user-engine's much larger surface (registration flows, factor models, family dataspace onboarding) to get it. This matches the fleet's existing convention of many small, single-purpose -engine/-core services rather than one large one.

Responsibility Boundary

tenant-engine owns tenant-as-an-entity facts. It never re-derives or duplicates facts another system already owns.

tenant-engine Owns

  • Tenant existence, identifier, and onboarding grouping.
  • Capability role grants and their audit trail.
  • Plan/subscription assignment (by reference, not by copying plan terms).
  • Reserved: guardrail/quota policy, once designed.

Other Systems Own

Concern Owner
OIDC discovery, login, token issuance key-cape, Keycloak
Tenant identifier format and role vocabulary NetKingdom (IAM Profile, ADR-0013, ADR-0014)
Users, profiles, memberships user-engine
Authorization decisions flex-auth
Pricing-model / plan definitions adaptive-pricing
Runtime secrets OpenBao / secrets-engine
Payment processing Not yet identified — tenant-engine records plan assignment, not payment execution

Non-Goals

  • Not an identity provider. Never issues, verifies, or refreshes tokens.
  • Not a policy decision point. Never makes an authorization decision itself — it answers queries flex-auth issues.
  • Not a billing processor. Records plan assignment, not payment execution.
  • Not a pricing-model engine. References adaptive-pricing plan ids.
  • Not the user/membership domain. Never stores user accounts or profiles.

Governing Principle

This repository should stay focused on the purpose above. Work that changes its authority, ownership boundaries, or operational promises should be captured in a workplan before implementation, and should stay consistent with net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md — if the two drift, the canon contract wins and this file should be corrected.

  • docs/flex-auth-integration.md — how the write API's WriteAuthorizer seam is implemented against a real flex-auth
  • net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md — the formal ownership contract
  • net-kingdom/canon/standards/iam-profile_v0.3.md — the tenant_roles claim and carrying mechanism this service implements
  • net-kingdom/docs/adr/ADR-0013-tenant-onboarding-grouping-taxonomy.md
  • net-kingdom/docs/adr/ADR-0014-tenant-capability-roles-and-tenant-engine-ownership.md
  • key-cape/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md — the first tenant this service will record