--- id: FLEX-WP-0008 type: workplan title: "tenant-engine Consumer Integration" domain: infotech repo: flex-auth status: ready owner: codex topic_slug: flex-auth planning_priority: P1 planning_order: 80 depends_on_workplans: - FLEX-WP-0007 related_workplans: - TEN-WP-0003 - KEY-WP-0005 created: "2026-07-23" updated: "2026-07-23" --- # FLEX-WP-0008: tenant-engine Consumer Integration ## Purpose Make `tenant-engine` a registered protected-system consumer of flex-auth, in both directions its boundary contract requires (`net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md`): 1. **tenant-engine's writes need authorization.** `tenant-engine`'s `FlexAuthWriteAuthorizer` (`TEN-WP-0003`) calls `POST /v1/check` for every mutation (`tenant.create`, `tenant.role.grant`, `tenant.role.revoke`, `tenant.plan.assign`). Until a policy package exists here, every check resolves to `deny`/`not_applicable` — correct fail-closed behavior, but it means `tenant-engine` cannot actually perform any write yet. This workplan is what makes real `allow` decisions reachable. 2. **flex-auth's own decisions need tenant capability roles as input**, for any protected system whose policy depends on a tenant's `PLTF`/`IAM`/ `VEN`/`CUS` roles (ADR-0014) at `aal2`-class decision time. flex-auth must call `tenant-engine`'s live-lookup endpoint (`GET /tenants/{id}/roles/live`) to enrich decision context, the same role a `context` adapter plays for any other external fact source. Closer in shape to `FLEX-WP-0006`/`FLEX-WP-0007` (ops-warden's small, security-lane SSH-certificate integration) than `FLEX-WP-0003` (Markitect's document-heavy, CARING-benchmark integration) — `tenant-engine`'s resource and action vocabulary is small and doesn't need CARING descriptor mapping. ## Task: Define tenant-engine resource and action vocabulary ```task id: FLEX-WP-0008-T01 status: todo priority: high ``` Resource types: `tenant`, `role-grant`, `plan-assignment`. Actions: `tenant.create`, `tenant.role.grant`, `tenant.role.revoke`, `tenant.plan.assign` — matching `TEN-WP-0003-T02`'s action-name mapping exactly (coordinate values, don't let the two repos invent different strings for the same action). Model on `examples/ops-warden`'s fixture shape (`schemas/check_request.schema.json`'s `resource{id,type,system}`, `system: "tenant-engine"`), not Markitect's. Done when: resource/action vocabulary documented under `docs/` following the `markitect-resource-namespace.md` / `markitect-action-vocabulary.md` naming pattern (`tenant-engine-resource-namespace.md`, `tenant-engine-action-vocabulary.md`). ## Task: Author and register the tenant-engine policy package ```task id: FLEX-WP-0008-T02 status: todo priority: high ``` Policy: writes require an `aal2`+ assurance actor holding an appropriate capability role for the action (e.g. `tenant.plan.assign` might require `PLTF` or a platform-operator subject type; exact rules are a design decision for this task, not pre-specified here — coordinate with ADR-0014's role semantics, not invented fresh). Register in `registry/` per the existing pattern; add fixtures (`allow`/`deny` request/response pairs) mirroring `examples/ops-warden/check_request_allow_adm.json`. Done when: `POST /v1/check` against a request matching `TEN-WP-0003`'s `FlexAuthCheckClient` shape returns `allow` for an authorized actor/action and `deny` for an unauthorized one, exercised by `main_test.go`-style integration tests. ## Task: tenant-engine live-lookup context adapter ```task id: FLEX-WP-0008-T03 status: todo priority: medium ``` A context-enrichment adapter (mirrors `internal/adapters/{relationship,rule,topaz}`'s shape) that calls `tenant-engine`'s `GET /tenants/{id}/roles/live` and attaches the result to decision context for policies that need it — used by *other* protected systems' policies that key off tenant capability role, not only by tenant-engine's own policy from T02. Must fail closed: adapter error or `tenant-engine` unavailability must not silently produce an empty role list indistinguishable from "no roles granted" (same rule `tenant-engine`'s own read endpoints already enforce — don't weaken it on the consuming side). Done when: a policy package referencing tenant capability role context correctly denies when the adapter call fails, not just when it succeeds with an empty result. ## Task: Closure review ```task id: FLEX-WP-0008-T04 status: todo priority: low ``` Confirm T01–T03 done; run flex-auth's existing test suite plus the new fixtures. Note in closure: `TEN-WP-0003`'s `FlexAuthWriteAuthorizer` should be re-verified against a real `allow` decision once this workplan's policy package exists (it was only tested against `deny`/`not_applicable` responses when built, since this workplan didn't exist yet). Run `statehub fix-consistency`.