Bootstrap repo: State Hub registration, agent docs, TEN-WP-0001/0002
statehub register + repo-seed template scaffold (CLAUDE.md, .claude/rules/,
registry/). INTENT.md and SCOPE.md rewritten from the generated stub to
match net-kingdom's ratified tenant-engine-boundary-contract_v0.1.md
(Purpose, Responsibility Boundary, Non-Goals). topic_slug corrected from
the auto-assigned custodian default to netkingdom, matching key-cape and
user-engine.
TEN-WP-0001 (bootstrap) complete: files reviewed/refined, stack decided
(Python 3.12 + FastAPI, matching qonto-assistant's convention), first real
workplan seeded.
TEN-WP-0002 drafted: service skeleton, domain model (tenant/grouping/
capability-role/plan-grant), storage layer, and the three boundary-contract
API surfaces (cache-read for key-cape, live-lookup for flex-auth with an
explicit fail-closed requirement, write API behind a WriteAuthorizer seam
since real flex-auth integration is a declared non-goal for this pass).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 21:56:07 +02:00
|
|
|
---
|
|
|
|
|
repo: tenant-engine
|
|
|
|
|
updated: "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.
|
|
|
|
|
|
|
|
|
|
## Related
|
|
|
|
|
|
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
|
|
|
- `docs/flex-auth-integration.md` — how the write API's `WriteAuthorizer`
|
|
|
|
|
seam is implemented against a real `flex-auth`
|
Bootstrap repo: State Hub registration, agent docs, TEN-WP-0001/0002
statehub register + repo-seed template scaffold (CLAUDE.md, .claude/rules/,
registry/). INTENT.md and SCOPE.md rewritten from the generated stub to
match net-kingdom's ratified tenant-engine-boundary-contract_v0.1.md
(Purpose, Responsibility Boundary, Non-Goals). topic_slug corrected from
the auto-assigned custodian default to netkingdom, matching key-cape and
user-engine.
TEN-WP-0001 (bootstrap) complete: files reviewed/refined, stack decided
(Python 3.12 + FastAPI, matching qonto-assistant's convention), first real
workplan seeded.
TEN-WP-0002 drafted: service skeleton, domain model (tenant/grouping/
capability-role/plan-grant), storage layer, and the three boundary-contract
API surfaces (cache-read for key-cape, live-lookup for flex-auth with an
explicit fail-closed requirement, write API behind a WriteAuthorizer seam
since real flex-auth integration is a declared non-goal for this pass).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 21:56:07 +02:00
|
|
|
- `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
|