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-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-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-T04 | wait | — | 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
|
||||
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.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.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.update` | `PATCH /tenants/{id}` | `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
|
||||
`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.
|
||||
`tenant-engine`'s role reads (`/roles`, `/roles/live`) are still not gated
|
||||
through flex-auth; they remain `tenant-engine`'s own cache-read and
|
||||
live-lookup endpoints. The guardrail GET is different: it is authorized,
|
||||
because a ceiling is an input to a decision, not an unauthenticated lookup.
|
||||
Every tenant and role read is gated before the store is touched, preventing
|
||||
both unauthorized data access and tenant-existence probing. `user-engine`
|
||||
may read tenant records, `flex-auth` may perform the high-stakes live role
|
||||
lookup, and no caller receives a role-read privilege through a mutation rule.
|
||||
|
||||
## 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",
|
||||
"request": {
|
||||
|
|
|
|||
|
|
@ -6,9 +6,12 @@ version: v1
|
|||
status: ready
|
||||
package: flexauth.tenant_engine.write_api
|
||||
actions:
|
||||
- tenant.read
|
||||
- tenant.create
|
||||
- tenant.role.grant
|
||||
- tenant.role.revoke
|
||||
- tenant.role.read
|
||||
- tenant.role.read.live
|
||||
- tenant.plan.assign
|
||||
- tenant.update
|
||||
- tenant.retire
|
||||
|
|
@ -165,9 +168,12 @@ import future.keywords.if
|
|||
import future.keywords.in
|
||||
|
||||
valid_actions := {
|
||||
"tenant.read",
|
||||
"tenant.create",
|
||||
"tenant.role.grant",
|
||||
"tenant.role.revoke",
|
||||
"tenant.role.read",
|
||||
"tenant.role.read.live",
|
||||
"tenant.plan.assign",
|
||||
"tenant.update",
|
||||
"tenant.retire",
|
||||
|
|
@ -176,15 +182,25 @@ valid_actions := {
|
|||
"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
|
||||
|
||||
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 {
|
||||
allowed
|
||||
|
|
@ -194,9 +210,23 @@ decision := {"effect": "allow", "reason": "write_api_policy_matched"} if {
|
|||
|
||||
allowed if {
|
||||
input.resource.system == "tenant-engine"
|
||||
input.action in read_actions
|
||||
input.action in tenant_read_actions
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,17 @@ resource_types:
|
|||
A tenant's resolved ceiling (spend, seats, and similar limits) plus
|
||||
the provenance of that value. Not an entitlement and not a meter.
|
||||
actions:
|
||||
- name: tenant.read
|
||||
capabilities:
|
||||
- View
|
||||
- Audit
|
||||
planes:
|
||||
- Identity
|
||||
- Audit
|
||||
exposure_modes:
|
||||
- Metadata
|
||||
metadata:
|
||||
required_context: []
|
||||
- name: tenant.create
|
||||
capabilities:
|
||||
- Create
|
||||
|
|
@ -68,6 +79,30 @@ actions:
|
|||
- Metadata
|
||||
metadata:
|
||||
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
|
||||
capabilities:
|
||||
- Bind
|
||||
|
|
|
|||
|
|
@ -51,6 +51,23 @@
|
|||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"name": "tenant.read",
|
||||
"capabilities": [
|
||||
"View",
|
||||
"Audit"
|
||||
],
|
||||
"planes": [
|
||||
"Identity",
|
||||
"Audit"
|
||||
],
|
||||
"exposure_modes": [
|
||||
"Metadata"
|
||||
],
|
||||
"metadata": {
|
||||
"required_context": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tenant.create",
|
||||
"capabilities": [
|
||||
|
|
@ -104,6 +121,42 @@
|
|||
"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",
|
||||
"capabilities": [
|
||||
|
|
@ -258,6 +311,23 @@
|
|||
"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)."
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
|
|
@ -265,7 +335,8 @@
|
|||
"id": "group:tenant-engine-writers",
|
||||
"display_name": "tenant-engine Write API Callers",
|
||||
"members": [
|
||||
"tenant-engine"
|
||||
"tenant-engine",
|
||||
"user-engine"
|
||||
],
|
||||
"tenant": "tenant:platform"
|
||||
},
|
||||
|
|
@ -274,7 +345,8 @@
|
|||
"display_name": "tenant-engine Guardrail Readers",
|
||||
"members": [
|
||||
"flex-auth",
|
||||
"tenant-engine"
|
||||
"tenant-engine",
|
||||
"user-engine"
|
||||
],
|
||||
"tenant": "tenant:platform"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,20 @@ subjects:
|
|||
tenant.role.revoke, tenant.plan.assign, the lifecycle actions
|
||||
tenant.update, tenant.retire, tenant.reactivate, and the
|
||||
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
|
||||
type: Service
|
||||
display_name: flex-auth service principal
|
||||
|
|
@ -40,10 +54,12 @@ groups:
|
|||
display_name: tenant-engine Write API Callers
|
||||
members:
|
||||
- tenant-engine
|
||||
- user-engine
|
||||
tenant: tenant:platform
|
||||
- id: group:tenant-engine-readers
|
||||
display_name: tenant-engine Guardrail Readers
|
||||
members:
|
||||
- flex-auth
|
||||
- tenant-engine
|
||||
- user-engine
|
||||
tenant: tenant:platform
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue