State tenant-engine's tenant relation in the write-api package, v3.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Container Image / build-and-push (push) Successful in 1m24s

tenant-engine named the relation in TEN-DEC-2026-002: `tenant` is the target
tenant record and always equals `resource.id`; the write API is cross-tenant
by design and `tenant.guardrail.read` does not differ. v2 carried no tenant
rule and a constant fixture tenant, so the deliberate scope and an omitted
rule were indistinguishable.

tenant-engine.write-api.mutate v3 (FLEX-DEC-2026-016):
- allowed requires tenant_is_target; a mismatch or absent tenant is denied
  tenant_not_target (object.get, so an absent key names the right cause).
- the cross-tenant scope is stated in the package and quantified by
  test_tenant_never_changes_effect over every action, three subjects and
  four tenants, with guards against passing by denying everything.
- fixtures rotate tenant across four tenants; five cross-tenant allows and
  two tenant_not_target denies added (42 fixtures, 33 tests, all pass).
- user-engine's tenant:platform exclusion is named as a fixed-record rule,
  not a subject/tenant relation, and tested separately.

Closes FLEX-WP-0022 (T01, T02 done). Also records TEN-IN-0004 and
SECRETS-IN-0002 on FLEX-WP-0020 and acknowledges the GH-DEC-2026-017
replies on FLEX-WP-0030-T04.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 63291@bnt-lap001
Assistant-Session: 8bd77868-ca68-4f49-bb1e-d539ecc0d703
This commit is contained in:
tegwick 2026-09-21 07:39:57 +02:00
parent bd3d270531
commit 3081067325
17 changed files with 783 additions and 150 deletions

View file

@ -10,10 +10,10 @@ protected-system consumer, gating its own write API
| --- | --- |
| `protected_system_manifest.yaml` | Resource types (`tenant`, `role-grant`, `plan-assignment`, `guardrail`) and the nine actions: the original four, the `FLEX-WP-0010` lifecycle trio, and the `FLEX-WP-0014` guardrail pair `tenant.guardrail.read` / `tenant.guardrail.set` |
| `subject_manifest.yaml` | Two registered callers: `tenant-engine` (all nine actions) and `flex-auth` (read-only on `tenant.guardrail.read`) |
| `policy_package.md` | Rego rules + embedded tests gating the write API |
| `policy_fixtures.yaml` | Allow/deny request/decision pairs, referenced by `policy_package.md`'s frontmatter |
| `policy_package.md` | Rego rules + embedded tests gating the write API. v3 states the tenant relation (`TEN-DEC-2026-002`, `FLEX-DEC-2026-016`): `tenant` is the target record and must equal `resource.id` (`tenant_not_target` otherwise); the scope is cross-tenant by design |
| `policy_fixtures.yaml` | Allow/deny request/decision pairs, referenced by `policy_package.md`'s frontmatter. `tenant` varies across four tenants so the suite reports on the field |
| `registry_snapshot.json` | Merged `systems`/`subjects`/`groups` snapshot assembled from the two manifests above, loadable by `flex-auth serve`/`check`/`load-registry` |
| `check_request_allow_create.json`, `check_request_deny_unknown_subject.json`, `check_request_allow_retire.json`, `check_request_deny_misspelled_lifecycle.json`, `check_request_allow_guardrail_read.json`, `check_request_allow_guardrail_set.json`, `check_request_deny_guardrail_set_as_reader.json`, `check_request_deny_misspelled_guardrail.json` | Standalone example requests for `flex-auth check` |
| `check_request_allow_create.json`, `check_request_deny_unknown_subject.json`, `check_request_allow_retire.json`, `check_request_deny_misspelled_lifecycle.json`, `check_request_allow_guardrail_read.json`, `check_request_allow_guardrail_set.json`, `check_request_deny_guardrail_set_as_reader.json`, `check_request_deny_misspelled_guardrail.json`, `check_request_deny_tenant_not_target.json` | Standalone example requests for `flex-auth check` |
**No `resource_manifest.yaml`** — unlike ops-warden's fixed SSH-certificate
inventory, `tenant-engine`'s resources (tenants) are created dynamically.