WIP: Claude Code rate-limit checkpoint (20dd5562)
This commit is contained in:
parent
3de72fe6f5
commit
150b1f55fb
8 changed files with 230 additions and 15 deletions
23
.claude/RESUME.md
Normal file
23
.claude/RESUME.md
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Claude Code — resume checkpoint
|
||||||
|
|
||||||
|
Session: 20dd5562-9f87-4ec7-9e89-0182ad59f4fa
|
||||||
|
Written: 2026-08-18T13:25:17.115Z
|
||||||
|
Trigger: rate-limited
|
||||||
|
Ref with your in-progress files: refs/claude/checkpoint-20dd5562
|
||||||
|
|
||||||
|
## To resume
|
||||||
|
|
||||||
|
claude --resume 20dd5562-9f87-4ec7-9e89-0182ad59f4fa
|
||||||
|
|
||||||
|
(or open Claude Code in this directory and run /resume)
|
||||||
|
|
||||||
|
## Plan (from TodoWrite state)
|
||||||
|
|
||||||
|
No task list was active; see transcript via the resume command above.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Don't want these changes? Resume this session (above), then run
|
||||||
|
`/rewind` to roll back the turn's tool edits (bash-made changes
|
||||||
|
excluded). refs/claude/checkpoint-20dd5562 holds a full snapshot until this session's
|
||||||
|
next checkpoint, or for up to ~2 weeks.
|
||||||
|
|
@ -90,7 +90,7 @@
|
||||||
| task | FLEX-WP-0014-T03 | done | — | workplans/FLEX-WP-0014-tenant-guardrail-policy-actions.md |
|
| task | FLEX-WP-0014-T03 | done | — | workplans/FLEX-WP-0014-tenant-guardrail-policy-actions.md |
|
||||||
| task | FLEX-WP-0014-T04 | done | — | workplans/FLEX-WP-0014-tenant-guardrail-policy-actions.md |
|
| task | FLEX-WP-0014-T04 | done | — | workplans/FLEX-WP-0014-tenant-guardrail-policy-actions.md |
|
||||||
| task | FLEX-WP-0015-T01 | done | — | workplans/FLEX-WP-0015-tenancy-posture-conformance.md |
|
| task | FLEX-WP-0015-T01 | done | — | workplans/FLEX-WP-0015-tenancy-posture-conformance.md |
|
||||||
| task | FLEX-WP-0015-T02 | progress | — | workplans/FLEX-WP-0015-tenancy-posture-conformance.md |
|
| task | FLEX-WP-0015-T02 | wait | — | workplans/FLEX-WP-0015-tenancy-posture-conformance.md |
|
||||||
| task | FLEX-WP-0015-T03 | done | — | workplans/FLEX-WP-0015-tenancy-posture-conformance.md |
|
| task | FLEX-WP-0015-T03 | done | — | workplans/FLEX-WP-0015-tenancy-posture-conformance.md |
|
||||||
| task | FLEX-WP-0015-T04 | wait | — | workplans/FLEX-WP-0015-tenancy-posture-conformance.md |
|
| task | FLEX-WP-0015-T04 | wait | — | workplans/FLEX-WP-0015-tenancy-posture-conformance.md |
|
||||||
| task | FLEX-WP-0015-T05 | done | — | workplans/FLEX-WP-0015-tenancy-posture-conformance.md |
|
| task | FLEX-WP-0015-T05 | done | — | workplans/FLEX-WP-0015-tenancy-posture-conformance.md |
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,14 @@ flex-auth protected system (`FLEX-WP-0008-T01`). Action names here match
|
||||||
(`tenant_engine/src/tenant_engine/authz.py`) — coordinated between the two
|
(`tenant_engine/src/tenant_engine/authz.py`) — coordinated between the two
|
||||||
repos, not independently invented.
|
repos, not independently invented.
|
||||||
|
|
||||||
| Action | tenant-engine write endpoint | Resource type | Decision effects |
|
| Action | tenant-engine endpoint | Resource type | Decision effects |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
|
| `tenant.read` | `GET /tenants/{id}` | `tenant` | `allow`, `deny` |
|
||||||
| `tenant.create` | `POST /tenants` | `tenant` | `allow`, `deny` |
|
| `tenant.create` | `POST /tenants` | `tenant` | `allow`, `deny` |
|
||||||
| `tenant.role.grant` | `POST /tenants/{id}/roles/grant` | `role-grant` | `allow`, `deny` |
|
| `tenant.role.grant` | `POST /tenants/{id}/roles/grant` | `role-grant` | `allow`, `deny` |
|
||||||
| `tenant.role.revoke` | `POST /tenants/{id}/roles/revoke` | `role-grant` | `allow`, `deny` |
|
| `tenant.role.revoke` | `POST /tenants/{id}/roles/revoke` | `role-grant` | `allow`, `deny` |
|
||||||
|
| `tenant.role.read` | `GET /tenants/{id}/roles` | `role-grant` | `allow`, `deny` |
|
||||||
|
| `tenant.role.read.live` | `GET /tenants/{id}/roles/live` | `role-grant` | `allow`, `deny` |
|
||||||
| `tenant.plan.assign` | `POST /tenants/{id}/plan` | `plan-assignment` | `allow`, `deny` |
|
| `tenant.plan.assign` | `POST /tenants/{id}/plan` | `plan-assignment` | `allow`, `deny` |
|
||||||
| `tenant.update` | `PATCH /tenants/{id}` | `tenant` | `allow`, `deny` |
|
| `tenant.update` | `PATCH /tenants/{id}` | `tenant` | `allow`, `deny` |
|
||||||
| `tenant.retire` | `POST /tenants/{id}/retire` | `tenant` | `allow`, `deny` |
|
| `tenant.retire` | `POST /tenants/{id}/retire` | `tenant` | `allow`, `deny` |
|
||||||
|
|
@ -26,12 +29,12 @@ without granting the write. That split is used today: `flex-auth` may
|
||||||
may do both. Reasoning is recorded in
|
may do both. Reasoning is recorded in
|
||||||
`examples/tenant-engine/policy_package.md`.
|
`examples/tenant-engine/policy_package.md`.
|
||||||
|
|
||||||
These are the only nine actions `tenant-engine` sends to `POST /v1/check` —
|
These are the only twelve actions `tenant-engine` sends to `POST /v1/check` —
|
||||||
its `FlexAuthCheckClient` (`TEN-WP-0003`) never sends anything else.
|
its `FlexAuthCheckClient` (`TEN-WP-0003`) never sends anything else.
|
||||||
`tenant-engine`'s role reads (`/roles`, `/roles/live`) are still not gated
|
Every tenant and role read is gated before the store is touched, preventing
|
||||||
through flex-auth; they remain `tenant-engine`'s own cache-read and
|
both unauthorized data access and tenant-existence probing. `user-engine`
|
||||||
live-lookup endpoints. The guardrail GET is different: it is authorized,
|
may read tenant records, `flex-auth` may perform the high-stakes live role
|
||||||
because a ceiling is an input to a decision, not an unauthenticated lookup.
|
lookup, and no caller receives a role-read privilege through a mutation rule.
|
||||||
|
|
||||||
## Not in scope for this vocabulary
|
## Not in scope for this vocabulary
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,40 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"id": "fixture:user-engine-tenant-read-allow",
|
||||||
|
"request": {
|
||||||
|
"id": "check:user-engine-tenant-read-t1",
|
||||||
|
"tenant": "tenant:friendly:binky",
|
||||||
|
"subject": {"id": "user-engine", "type": "service"},
|
||||||
|
"action": "tenant.read",
|
||||||
|
"resource": {"id": "t-1", "type": "tenant", "system": "tenant-engine"},
|
||||||
|
"context": {}
|
||||||
|
},
|
||||||
|
"expect": {"effect": "allow", "reason": "write_api_policy_matched"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fixture:flex-auth-live-role-read-allow",
|
||||||
|
"request": {
|
||||||
|
"id": "check:flex-auth-live-role-read-t1",
|
||||||
|
"tenant": "tenant:friendly:binky",
|
||||||
|
"subject": {"id": "flex-auth", "type": "service"},
|
||||||
|
"action": "tenant.role.read.live",
|
||||||
|
"resource": {"id": "t-1", "type": "role-grant", "system": "tenant-engine"},
|
||||||
|
"context": {}
|
||||||
|
},
|
||||||
|
"expect": {"effect": "allow", "reason": "write_api_policy_matched"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fixture:user-engine-live-role-read-deny",
|
||||||
|
"request": {
|
||||||
|
"id": "check:user-engine-live-role-read-t1",
|
||||||
|
"tenant": "tenant:friendly:binky",
|
||||||
|
"subject": {"id": "user-engine", "type": "service"},
|
||||||
|
"action": "tenant.role.read.live",
|
||||||
|
"resource": {"id": "t-1", "type": "role-grant", "system": "tenant-engine"},
|
||||||
|
"context": {}
|
||||||
|
},
|
||||||
|
"expect": {"effect": "deny", "reason": "action_not_granted"}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "fixture:tenant-engine-create-allow",
|
"id": "fixture:tenant-engine-create-allow",
|
||||||
"request": {
|
"request": {
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,12 @@ version: v1
|
||||||
status: ready
|
status: ready
|
||||||
package: flexauth.tenant_engine.write_api
|
package: flexauth.tenant_engine.write_api
|
||||||
actions:
|
actions:
|
||||||
|
- tenant.read
|
||||||
- tenant.create
|
- tenant.create
|
||||||
- tenant.role.grant
|
- tenant.role.grant
|
||||||
- tenant.role.revoke
|
- tenant.role.revoke
|
||||||
|
- tenant.role.read
|
||||||
|
- tenant.role.read.live
|
||||||
- tenant.plan.assign
|
- tenant.plan.assign
|
||||||
- tenant.update
|
- tenant.update
|
||||||
- tenant.retire
|
- tenant.retire
|
||||||
|
|
@ -165,9 +168,12 @@ import future.keywords.if
|
||||||
import future.keywords.in
|
import future.keywords.in
|
||||||
|
|
||||||
valid_actions := {
|
valid_actions := {
|
||||||
|
"tenant.read",
|
||||||
"tenant.create",
|
"tenant.create",
|
||||||
"tenant.role.grant",
|
"tenant.role.grant",
|
||||||
"tenant.role.revoke",
|
"tenant.role.revoke",
|
||||||
|
"tenant.role.read",
|
||||||
|
"tenant.role.read.live",
|
||||||
"tenant.plan.assign",
|
"tenant.plan.assign",
|
||||||
"tenant.update",
|
"tenant.update",
|
||||||
"tenant.retire",
|
"tenant.retire",
|
||||||
|
|
@ -176,15 +182,25 @@ valid_actions := {
|
||||||
"tenant.guardrail.set",
|
"tenant.guardrail.set",
|
||||||
}
|
}
|
||||||
|
|
||||||
read_actions := {"tenant.guardrail.read"}
|
tenant_read_actions := {"tenant.read", "tenant.role.read"}
|
||||||
|
|
||||||
|
live_read_actions := {"tenant.role.read.live"}
|
||||||
|
|
||||||
|
guardrail_read_actions := {"tenant.guardrail.read"}
|
||||||
|
|
||||||
|
read_actions := tenant_read_actions | live_read_actions | guardrail_read_actions
|
||||||
|
|
||||||
mutate_actions := valid_actions - read_actions
|
mutate_actions := valid_actions - read_actions
|
||||||
|
|
||||||
known_subjects := {"tenant-engine", "flex-auth"}
|
known_subjects := {"tenant-engine", "user-engine", "flex-auth"}
|
||||||
|
|
||||||
read_subjects := {"tenant-engine", "flex-auth"}
|
tenant_read_subjects := {"tenant-engine", "user-engine"}
|
||||||
|
|
||||||
mutate_subjects := {"tenant-engine"}
|
live_read_subjects := {"flex-auth"}
|
||||||
|
|
||||||
|
guardrail_read_subjects := {"tenant-engine", "flex-auth"}
|
||||||
|
|
||||||
|
mutate_subjects := {"tenant-engine", "user-engine"}
|
||||||
|
|
||||||
decision := {"effect": "allow", "reason": "write_api_policy_matched"} if {
|
decision := {"effect": "allow", "reason": "write_api_policy_matched"} if {
|
||||||
allowed
|
allowed
|
||||||
|
|
@ -194,9 +210,23 @@ decision := {"effect": "allow", "reason": "write_api_policy_matched"} if {
|
||||||
|
|
||||||
allowed if {
|
allowed if {
|
||||||
input.resource.system == "tenant-engine"
|
input.resource.system == "tenant-engine"
|
||||||
input.action in read_actions
|
input.action in tenant_read_actions
|
||||||
input.subject.type == "service"
|
input.subject.type == "service"
|
||||||
input.subject.id in read_subjects
|
input.subject.id in tenant_read_subjects
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed if {
|
||||||
|
input.resource.system == "tenant-engine"
|
||||||
|
input.action in live_read_actions
|
||||||
|
input.subject.type == "service"
|
||||||
|
input.subject.id in live_read_subjects
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed if {
|
||||||
|
input.resource.system == "tenant-engine"
|
||||||
|
input.action in guardrail_read_actions
|
||||||
|
input.subject.type == "service"
|
||||||
|
input.subject.id in guardrail_read_subjects
|
||||||
}
|
}
|
||||||
|
|
||||||
allowed if {
|
allowed if {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,17 @@ resource_types:
|
||||||
A tenant's resolved ceiling (spend, seats, and similar limits) plus
|
A tenant's resolved ceiling (spend, seats, and similar limits) plus
|
||||||
the provenance of that value. Not an entitlement and not a meter.
|
the provenance of that value. Not an entitlement and not a meter.
|
||||||
actions:
|
actions:
|
||||||
|
- name: tenant.read
|
||||||
|
capabilities:
|
||||||
|
- View
|
||||||
|
- Audit
|
||||||
|
planes:
|
||||||
|
- Identity
|
||||||
|
- Audit
|
||||||
|
exposure_modes:
|
||||||
|
- Metadata
|
||||||
|
metadata:
|
||||||
|
required_context: []
|
||||||
- name: tenant.create
|
- name: tenant.create
|
||||||
capabilities:
|
capabilities:
|
||||||
- Create
|
- Create
|
||||||
|
|
@ -68,6 +79,30 @@ actions:
|
||||||
- Metadata
|
- Metadata
|
||||||
metadata:
|
metadata:
|
||||||
required_context: []
|
required_context: []
|
||||||
|
- name: tenant.role.read
|
||||||
|
capabilities:
|
||||||
|
- View
|
||||||
|
- Audit
|
||||||
|
planes:
|
||||||
|
- Identity
|
||||||
|
- Policy
|
||||||
|
- Audit
|
||||||
|
exposure_modes:
|
||||||
|
- Metadata
|
||||||
|
metadata:
|
||||||
|
required_context: []
|
||||||
|
- name: tenant.role.read.live
|
||||||
|
capabilities:
|
||||||
|
- View
|
||||||
|
- Audit
|
||||||
|
planes:
|
||||||
|
- Identity
|
||||||
|
- Policy
|
||||||
|
- Audit
|
||||||
|
exposure_modes:
|
||||||
|
- Metadata
|
||||||
|
metadata:
|
||||||
|
required_context: []
|
||||||
- name: tenant.plan.assign
|
- name: tenant.plan.assign
|
||||||
capabilities:
|
capabilities:
|
||||||
- Bind
|
- Bind
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,23 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"actions": [
|
"actions": [
|
||||||
|
{
|
||||||
|
"name": "tenant.read",
|
||||||
|
"capabilities": [
|
||||||
|
"View",
|
||||||
|
"Audit"
|
||||||
|
],
|
||||||
|
"planes": [
|
||||||
|
"Identity",
|
||||||
|
"Audit"
|
||||||
|
],
|
||||||
|
"exposure_modes": [
|
||||||
|
"Metadata"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"required_context": []
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "tenant.create",
|
"name": "tenant.create",
|
||||||
"capabilities": [
|
"capabilities": [
|
||||||
|
|
@ -104,6 +121,42 @@
|
||||||
"required_context": []
|
"required_context": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "tenant.role.read",
|
||||||
|
"capabilities": [
|
||||||
|
"View",
|
||||||
|
"Audit"
|
||||||
|
],
|
||||||
|
"planes": [
|
||||||
|
"Identity",
|
||||||
|
"Policy",
|
||||||
|
"Audit"
|
||||||
|
],
|
||||||
|
"exposure_modes": [
|
||||||
|
"Metadata"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"required_context": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tenant.role.read.live",
|
||||||
|
"capabilities": [
|
||||||
|
"View",
|
||||||
|
"Audit"
|
||||||
|
],
|
||||||
|
"planes": [
|
||||||
|
"Identity",
|
||||||
|
"Policy",
|
||||||
|
"Audit"
|
||||||
|
],
|
||||||
|
"exposure_modes": [
|
||||||
|
"Metadata"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"required_context": []
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "tenant.plan.assign",
|
"name": "tenant.plan.assign",
|
||||||
"capabilities": [
|
"capabilities": [
|
||||||
|
|
@ -258,6 +311,23 @@
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"description": "flex-auth's own service identity. Authorized only for tenant.guardrail.read so the PDP can consume ceilings without being able to change them (FLEX-WP-0014)."
|
"description": "flex-auth's own service identity. Authorized only for tenant.guardrail.read so the PDP can consume ceilings without being able to change them (FLEX-WP-0014)."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "user-engine",
|
||||||
|
"type": "Service",
|
||||||
|
"display_name": "user-engine service principal",
|
||||||
|
"organization_relation": "ServiceProvider",
|
||||||
|
"roles": [
|
||||||
|
"Operator"
|
||||||
|
],
|
||||||
|
"groups": [
|
||||||
|
"group:tenant-engine-writers",
|
||||||
|
"group:tenant-engine-readers"
|
||||||
|
],
|
||||||
|
"tenant": "tenant:platform",
|
||||||
|
"metadata": {
|
||||||
|
"description": "user-engine's service identity. It owns the portal workflow that reads and mutates tenant lifecycle records through tenant-engine."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"groups": [
|
"groups": [
|
||||||
|
|
@ -265,7 +335,8 @@
|
||||||
"id": "group:tenant-engine-writers",
|
"id": "group:tenant-engine-writers",
|
||||||
"display_name": "tenant-engine Write API Callers",
|
"display_name": "tenant-engine Write API Callers",
|
||||||
"members": [
|
"members": [
|
||||||
"tenant-engine"
|
"tenant-engine",
|
||||||
|
"user-engine"
|
||||||
],
|
],
|
||||||
"tenant": "tenant:platform"
|
"tenant": "tenant:platform"
|
||||||
},
|
},
|
||||||
|
|
@ -274,7 +345,8 @@
|
||||||
"display_name": "tenant-engine Guardrail Readers",
|
"display_name": "tenant-engine Guardrail Readers",
|
||||||
"members": [
|
"members": [
|
||||||
"flex-auth",
|
"flex-auth",
|
||||||
"tenant-engine"
|
"tenant-engine",
|
||||||
|
"user-engine"
|
||||||
],
|
],
|
||||||
"tenant": "tenant:platform"
|
"tenant": "tenant:platform"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,20 @@ subjects:
|
||||||
tenant.role.revoke, tenant.plan.assign, the lifecycle actions
|
tenant.role.revoke, tenant.plan.assign, the lifecycle actions
|
||||||
tenant.update, tenant.retire, tenant.reactivate, and the
|
tenant.update, tenant.retire, tenant.reactivate, and the
|
||||||
guardrail actions tenant.guardrail.read, tenant.guardrail.set.
|
guardrail actions tenant.guardrail.read, tenant.guardrail.set.
|
||||||
|
- id: user-engine
|
||||||
|
type: Service
|
||||||
|
display_name: user-engine service principal
|
||||||
|
organization_relation: ServiceProvider
|
||||||
|
roles:
|
||||||
|
- Operator
|
||||||
|
groups:
|
||||||
|
- group:tenant-engine-writers
|
||||||
|
- group:tenant-engine-readers
|
||||||
|
tenant: tenant:platform
|
||||||
|
metadata:
|
||||||
|
description: >-
|
||||||
|
user-engine's service identity. It owns the portal workflow that reads
|
||||||
|
and mutates tenant lifecycle records through tenant-engine.
|
||||||
- id: flex-auth
|
- id: flex-auth
|
||||||
type: Service
|
type: Service
|
||||||
display_name: flex-auth service principal
|
display_name: flex-auth service principal
|
||||||
|
|
@ -40,10 +54,12 @@ groups:
|
||||||
display_name: tenant-engine Write API Callers
|
display_name: tenant-engine Write API Callers
|
||||||
members:
|
members:
|
||||||
- tenant-engine
|
- tenant-engine
|
||||||
|
- user-engine
|
||||||
tenant: tenant:platform
|
tenant: tenant:platform
|
||||||
- id: group:tenant-engine-readers
|
- id: group:tenant-engine-readers
|
||||||
display_name: tenant-engine Guardrail Readers
|
display_name: tenant-engine Guardrail Readers
|
||||||
members:
|
members:
|
||||||
- flex-auth
|
- flex-auth
|
||||||
- tenant-engine
|
- tenant-engine
|
||||||
|
- user-engine
|
||||||
tenant: tenant:platform
|
tenant: tenant:platform
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue