flex-auth/workplans/FLEX-WP-0022-tenant-scope-coverage.md
tegwick 3f60797c0a
All checks were successful
CI Smoke / host-smoke (push) Successful in 2s
CI Smoke / container-smoke (push) Successful in 5s
Record the first returned rename handoff and re-ask the tenant-scope question.
FLEX-WP-0020-T04: reuse-surface is the first owner to return a live work-record
(REUSE-WP-0023, two tasks, both deliberately in wait). Recorded in the handoff
evidence and verified rather than taken on report — the hub record is active,
access-engine raw returns 404, flex-auth returns 303, so the rename has not
landed and their refusal to pre-rewrite the federation source URL is correct.

That refusal changed the plan rather than only their side: flipping an enabled,
publish-passing source before the rename drops a live capability from the
composed federated index, so the source rewrite is ordered after T06, and
flex-auth owes them a ping when access-engine serves the capabilities index.
Nine owners remain pending; T05 stays blocked.

FLEX-WP-0022-T01: re-asked tenant-engine what CheckRequest.tenant denotes on the
write API, stating that "none" is a complete answer. Their code copies tenant_id
onto both tenant and resource.id, but that is observation, not admission, and
encoding a rule from it would make flex-auth the author of their tenancy model.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 28468@bnt-lap001
Assistant-Session: c76569b2-6056-4dad-aea4-49cd7a018f5d
2026-09-20 23:52:31 +02:00

132 lines
4.9 KiB
Markdown

---
id: FLEX-WP-0022
type: workplan
title: "Tenant scoping is unstated in tenant-engine and untested in two more packages"
domain: infotech
repo: flex-auth
status: active
flavor: implementation
depends_on:
- FLEX-WP-0021
owner: claude
topic_slug: netkingdom
planning_priority: P1
planning_order: 220
depends_on_workplans:
- FLEX-WP-0021
related_workplans:
- FLEX-WP-0010
- FLEX-WP-0014
created: "2026-09-06"
updated: "2026-09-15"
state_hub_workstream_id: "804c588c-f47a-50c4-bdd7-51b24bbf9539"
---
# FLEX-WP-0022 — Tenant scoping is unstated in tenant-engine and untested in two more packages
Opened by the sweep in `FLEX-DEC-2026-008`, which found
`secrets-engine.catalog-lane.lifecycle` v1 allowing a foreign tenant and then
asked whether the other packages shared the defect.
## What the sweep found
| Package | Fixture tenants | Tenant rule | State |
| --- | --- | --- | --- |
| `secrets-engine` | 3 distinct | added at v2 | fixed, `FLEX-DEC-2026-008` |
| `qonto-assistant` | 2 distinct | `wrong_tenant` | fine |
| `user-engine` | 2 distinct | `cross_tenant` | fine |
| `ops-warden` | constant | `wrong_tenant` | rule real, fixtures do not vary it |
| `railiance-platform` | constant | `wrong_tenant` | same |
| `tenant-engine` | constant | **none** | deployed, unscoped |
Verified against `tenant-engine`: an allowed `tenant.create` re-sent under
`tenant: tenant:coulomb` returns `allow` / `write_api_policy_matched`.
## Why `tenant-engine` is not the same fix
`secrets-engine` sends its own calling identity's tenant, so a constant
`known_tenant` is the right rule. `tenant-engine` is different in kind: its
subjects all sit in `tenant:platform` while its fixtures send
`tenant:friendly:binky`, so the request tenant names the **target** of the
operation. A service whose purpose is creating and retiring tenants acts across
them by design, and a constant would break it on its first real call.
The defect today is therefore not "the rule is missing" but **"nobody can tell
whether it is missing"**: a deliberate cross-tenant scope and an omitted rule
look identical in the artifact. That is the same publishing-shape argument
`gate-house` made for §12's derived-artifact rule.
## 1. Get the intended tenant relation from tenant-engine
```task
id: FLEX-WP-0022-T01
status: progress
priority: high
state_hub_task_id: "a84dcee5-9426-5b30-8dd3-f4c076d00174"
```
Owner: `flex-auth` to ask; `tenant-engine` owns the answer.
- Ask what the CheckRequest `tenant` denotes on the write API: the caller's
tenant, the target tenant record, or the tenant a guardrail applies to.
- Ask whether any of the nine write actions must be refused cross-tenant, and
whether `tenant.guardrail.read` (which `flex-auth` itself calls) differs.
Gate: the relation is named by `tenant-engine`, not inferred here. This is the
`FLEX-WP-0021-T01` rule applied to a field rather than to an action list.
2026-09-15: asked `tenant-engine` to name the relation
(`e8ba6a53-0093-4dc0-ad70-01f6b8c8e76b`). Their live
`FlexAuthWriteAuthorizer.authorize` copies `tenant_id` onto both
`CheckRequest.tenant` and `resource.id`. That is observation, not admission.
2026-09-20: re-asked after five days of silence, restating the three questions
and saying plainly that "none" is a complete answer. The gate is unchanged: a
policy rule inferred from reading their code would make `flex-auth` the author
of their tenancy model, which is the boundary `FLEX-WP-0021-T01` exists to hold.
T02 stays `wait` rather than being guessed forward.
## 2. Encode the relation, or record that there is none
```task
id: FLEX-WP-0022-T02
status: wait
priority: high
state_hub_task_id: "712ac826-845f-54bd-8446-f11258d21eb4"
```
Owner: `flex-auth`.
If a relation exists, encode it in `tenant-engine.write-api.mutate` as a new
version — fail-open corrections are visible as version changes
(`FLEX-DEC-2026-008`) — with a fixture per side.
If the scope is genuinely unrestricted, say so **in the package**: a stated
"this package is deliberately cross-tenant, because the caller administers
tenants" is a rule a reviewer can check. Silence is not.
Either way the fixtures must vary `tenant`, so the suite reports on the field.
## 3. Vary tenant in the two suites that hold it constant
```task
id: FLEX-WP-0022-T03
status: done
priority: medium
state_hub_task_id: "3058f171-99d2-526b-a1bb-bd7aed87d10a"
```
Owner: `flex-auth`.
`ops-warden` and `railiance-platform` have working `wrong_tenant` rules
exercised only by Rego tests. Add a wrong-tenant deny fixture to each so the
fixture suite covers what the rule claims. No policy change and no version bump:
the behaviour is already correct, only the evidence is thin.
Gate: no package's fixture suite holds `tenant` constant.
2026-09-15: added `fixture:ops-warden-wrong-tenant-deny` and
`fixture:credential-grant-wrong-tenant-deny` plus matching
`check_request_deny_wrong_tenant.json` files. Both suites now vary `tenant`.
`test-policy` and `flex-auth check` return `deny` / `wrong_tenant`. No policy
or version change.