These workplans exist only in the retired local hub. Their random pre-ADR-007 identifiers are refused by C-06 as stale references, so they cannot be registered. Deriving from the canonical record id takes no identity from anything: central does not hold them and the old ids die with the cache. Records central already holds were deliberately left untouched. Refs CUST-WP-0068-T06 Assistant: claude-code Assistant-Model: opus Assistant-Process: 2583210@bnt-lap001 Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
8.3 KiB
| id | type | title | domain | repo | status | owner | topic_slug | planning_priority | planning_order | depends_on_workplans | related_workplans | created | updated | state_hub_workstream_id | |||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| FLEX-WP-0014 | workplan | Authorize tenant-engine guardrail actions | infotech | flex-auth | finished | grok | netkingdom | P1 | 140 |
|
|
2026-08-16 | 2026-08-16 | 7de17bd2-5e7c-5355-ba1d-40a051a67746 |
FLEX-WP-0014 - Authorize tenant-engine guardrail actions
Extend package tenant-engine.write-api.mutate from seven actions to nine
with the TEN-WP-0006 guardrail surface. Until they exist here, every
guardrail check resolves to deny unknown_action — correct fail-closed
behaviour, but the surface is inert.
Requested by: tenant-engine (TEN-WP-0006, message 5572d198).
Blocked on: FLEX-WP-0013 (four-action production pin). That restore is
done; this workplan starts on a current seven-action package, not a
rolled-back one.
Requirements from tenant-engine
Two new actions, both on resource type guardrail, both against
system: "tenant-engine":
| Action | Resource type | HTTP surface |
|---|---|---|
tenant.guardrail.read |
guardrail |
GET /tenants/{id}/guardrails?actor= |
tenant.guardrail.set |
guardrail |
PUT / DELETE /tenants/{id}/guardrails/{limit_key} |
Strings are live in tenant-engine/src/tenant_engine/authz.py
_RESOURCE_TYPES. Match them verbatim. CheckRequest shape is unchanged:
subject_type: "service", resource_id = tenant_id.
They are two actions so policy can grant a PDP the read without granting
the write. tenant-engine's own tests call the read as actor=flex-auth.
v1 stays the activated revision; the identifying fact is that v1 as of
this commit contains the two guardrail actions.
Do not deploy a new production image in this workplan until the package is verified locally. Image + pin is a follow-on, named in T04.
T01 - Extend the policy package with the guardrail actions
id: FLEX-WP-0014-T01
status: done
priority: high
state_hub_task_id: "34fb239a-7f06-5e96-9f10-a6bbb85bf4c3"
Add tenant.guardrail.read and tenant.guardrail.set to
valid_actions, the frontmatter actions: list, and
protected_system_manifest.yaml. Add resource type guardrail.
CARING: View for the read (already used by qonto-assistant), EditAny
for the set (already used by tenant.update). Extend
caring.capabilities with View.
Register flex-auth as a second service subject that is allowed
tenant.guardrail.read and nothing else. Update subject_manifest.yaml
and rebuild examples/tenant-engine/registry_snapshot.json.
Update docs/tenant-engine-action-vocabulary.md and
docs/tenant-engine-resource-namespace.md. Keep the existing note that
this policy governs who may call tenant-engine's admin API, not a tenant's
own capability roles.
Done when valid_actions carries all nine actions, the new resource type
is declared, and flex-auth is a registered read-only subject.
Done 2026-08-16: nine actions in frontmatter, valid_actions, and the
protected-system manifest. Resource type guardrail added. CARING
View on the read, EditAny on the set. flex-auth registered as a
read-only subject. Registry snapshot rebuilt (2 subjects, 2 groups).
T02 - Decide how the read/write split is used today
id: FLEX-WP-0014-T02
status: done
priority: medium
state_hub_task_id: "8ea87d5f-883a-5d63-89bb-33a3d0963472"
tenant-engine split the actions so a PDP can read ceilings without being able to change them. Decide whether that split is used now or only reserved.
Record the decision in policy_package.md. Using it now — flex-auth may
read, tenant-engine may read and set, nobody else — is the expected
outcome unless a concrete reason says otherwise. Inventing an ops write
subject is out of scope; writes stay on the existing service identity.
Done when the decision and its reasoning are recorded and implemented.
Done 2026-08-16: yes — use the split now. Recorded under "Decision:
use the read/write split now" in
examples/tenant-engine/policy_package.md. flex-auth may
tenant.guardrail.read and is denied action_not_granted on
tenant.guardrail.set. tenant-engine may do both. No ops write
subject.
T03 - Fixtures and verification
id: FLEX-WP-0014-T03
status: done
priority: high
state_hub_task_id: "1029d2ce-bb37-530e-91ef-ded86f6f5be8"
Add fixture pairs for: authorized tenant-engine read and set → allow;
authorized flex-auth read → allow; flex-auth set → deny
action_not_granted; unknown subject → deny unknown_subject; misspelled
guardrail action → deny unknown_action.
Verify against the real binary: test-policy, load-registry, check on
standalone request files. Then close the loop with a live flex-auth serve
and a real tenant-engine on TENANT_ENGINE_FLEX_AUTH_URL, through
unmodified FlexAuthWriteAuthorizer:
GET /tenants/{id}/guardrails?actor=flex-auth→ 200GET ...?actor=ops→ 403PUT ...asactor=tenant-engine→ 200PUT ...asactor=flex-auth→ 403PUT ...asactor=ops→ 403
Done when those five calls match, go test ./... is green, and
gofmt/go vet are clean.
Done 2026-08-16: 17/17 Rego tests, 23/23 fixtures. load-registry loads
clean. Standalone check files return the expected allow/deny.
Live flex-auth serve on 127.0.0.1:9098 + real tenant-engine on
TENANT_ENGINE_FLEX_AUTH_URL:
| Call | tenant-engine | flex-auth |
|---|---|---|
POST /tenants actor=tenant-engine |
201 | allow |
GET .../guardrails?actor=flex-auth |
200 spend.monthly=25000 grouping |
allow |
GET ...?actor=ops |
403 write_denied |
deny unknown_subject |
PUT .../spend.monthly actor=tenant-engine |
200 override 9000 v2 |
allow |
PUT ... actor=flex-auth |
403 write_denied |
deny action_not_granted |
PUT ... actor=ops |
403 write_denied |
deny unknown_subject |
go test ./... green; gofmt -l empty; go vet ./... clean.
T04 - Closure and handoff
id: FLEX-WP-0014-T04
status: done
priority: low
state_hub_task_id: "b2b44215-ef08-5c50-b466-190156a88ef3"
Confirm T01–T03. Notify tenant-engine naming the policy revision. State
plainly whether the nine-action package is in source or deployed —
FLEX-WP-0010 taught that distinction the hard way. Production rollout of a
new image is not implied by this workplan; it is a separate pin after CI
publishes a digest.
Done when the handoff is sent and this workplan is finished.
Done 2026-08-16: T01–T03 confirmed. Policy revision for the handoff is
package tenant-engine.write-api.mutate version v1, now carrying
all nine actions. The version string is unchanged because the package is
additive.
In source, not in the cluster. Production flex-auth-tenant-engine
still serves sha256:9320df39 (seven actions). A CI-built image from
this commit plus a pin of flex-auth-tenant-engine is the remaining
step before a live guardrail check succeeds against the cluster Service.
tenant-engine's own TEN-WP-0006 surface is also not deployed, so the
cluster path is not yet the binding one.
Deployed 2026-08-16 after operator "sync and commit / deploy flex-auth":
CI image main-f304688 /
sha256:1bf060e61122693ce98359c167cc5fe8bdafc84e097e090eaa71af94d0f27cbc
is live on flex-auth-tenant-engine. Approval
8d1efcea-93c7-436b-a749-e5d464e94a33. Rollback target remains
sha256:9320df39. flex-auth-user-engine stayed on sha256:1f529037.
Live /v1/check against the Service:
| Request | Effect | Decision |
|---|---|---|
tenant.create |
allow write_api_policy_matched |
decision:ef18e8bbd2dead6a |
tenant.retire |
allow write_api_policy_matched |
decision:996663f6902d0762 |
tenant.guardrail.read as flex-auth |
allow write_api_policy_matched |
decision:9b9e540de13cf7cf |
tenant.guardrail.set as tenant-engine |
allow write_api_policy_matched |
decision:20c82bc38501a343 |
tenant.guardrail.set as flex-auth |
deny action_not_granted |
decision:a057a7030df65208 |
| misspelled guardrail | deny unknown_action |
decision:18ae70a79c6d47c6 |
| unknown subject | deny unknown_subject |
decision:a98c59c6a34f84ad |
| misspelled lifecycle | deny unknown_action |
decision:1b3012abb4e2b27d |
TEN-WP-0007 can pin the tenant-engine guardrail image. The seven pre-existing actions still allow.