diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 6fb36f7..d0f30d9 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -15,6 +15,7 @@ | workplan | TEN-WP-0004 | finished | — | workplans/TEN-WP-0004-production-runtime.md | | workplan | TEN-WP-0005 | finished | — | workplans/TEN-WP-0005-tenant-update-and-retirement-api.md | | workplan | TEN-WP-0006 | finished | — | workplans/TEN-WP-0006-guardrail-quota-policy.md | +| workplan | TEN-WP-0007 | ready | — | workplans/TEN-WP-0007-guardrail-production-rollout.md | | task | ADHOC-2026-07-24-T01 | done | — | workplans/ADHOC-2026-07-24.md | | task | TEN-WP-0001-T01 | done | — | workplans/TEN-WP-0001-statehub-bootstrap.md | | task | TEN-WP-0001-T02 | done | — | workplans/TEN-WP-0001-statehub-bootstrap.md | @@ -43,3 +44,7 @@ | task | TEN-WP-0006-T03 | done | — | workplans/TEN-WP-0006-guardrail-quota-policy.md | | task | TEN-WP-0006-T04 | done | — | workplans/TEN-WP-0006-guardrail-quota-policy.md | | task | TEN-WP-0006-T05 | done | — | workplans/TEN-WP-0006-guardrail-quota-policy.md | +| task | TEN-WP-0007-T01 | todo | — | workplans/TEN-WP-0007-guardrail-production-rollout.md | +| task | TEN-WP-0007-T02 | wait | — | workplans/TEN-WP-0007-guardrail-production-rollout.md | +| task | TEN-WP-0007-T03 | wait | — | workplans/TEN-WP-0007-guardrail-production-rollout.md | +| task | TEN-WP-0007-T04 | wait | — | workplans/TEN-WP-0007-guardrail-production-rollout.md | diff --git a/deploy/tenant-engine.yaml b/deploy/tenant-engine.yaml index eee5c18..0883bd8 100644 --- a/deploy/tenant-engine.yaml +++ b/deploy/tenant-engine.yaml @@ -42,7 +42,7 @@ spec: value: /data/tenant-engine.db - name: TENANT_ENGINE_FLEX_AUTH_URL value: http://flex-auth-tenant-engine.flex-auth.svc.cluster.local:8080 - image: forgejo.coulomb.social/coulomb/tenant-engine@sha256:08be0b1dcdc65575592b7be665c28e09a82316ea3d4c9b551ccb753f25360612 + image: forgejo.coulomb.social/coulomb/tenant-engine@sha256:44ca65f3cdd5967b0124e16b6aba10bf1ac2747e1dde96cbef3dd11ae7cc9574 livenessProbe: httpGet: path: /health diff --git a/workplans/TEN-WP-0007-guardrail-production-rollout.md b/workplans/TEN-WP-0007-guardrail-production-rollout.md index 4a42cfd..76195d4 100644 --- a/workplans/TEN-WP-0007-guardrail-production-rollout.md +++ b/workplans/TEN-WP-0007-guardrail-production-rollout.md @@ -12,6 +12,7 @@ updated: "2026-08-16" depends_on: - TEN-WP-0006 unblocks: [] +state_hub_workstream_id: "9c4d1bb8-47a4-47ff-841f-e8f2ddd01b59" --- # TEN-WP-0007 - Guardrail production rollout @@ -46,8 +47,9 @@ step independently observable. ```task id: TEN-WP-0007-T01 -status: todo +status: done priority: high +state_hub_task_id: "2f977044-7de1-4dba-aba6-d8a4da6cdcac" ``` `src/**` is a trigger path in `.forgejo/workflows/image.yaml` and the guardrail @@ -61,12 +63,47 @@ Done when the digest is recorded here and the image is confirmed to contain the guardrail routes (`GET /openapi.json` on the built image lists `/tenants/{tenant_id}/guardrails`). +Done 2026-08-16. The forge API turned out to be readable without credentials, +so this was not blocked after all. + +| Fact | Value | +| --- | --- | +| Immutable image | `forgejo.coulomb.social/coulomb/tenant-engine@sha256:44ca65f3cdd5967b0124e16b6aba10bf1ac2747e1dde96cbef3dd11ae7cc9574` | +| CI tag | `main-955fe33` (workflow run #2, `success`) | +| Built from | `955fe33` — "Finish TEN-WP-0006: guardrail and quota policy" | +| API version | `0.1.0` (unchanged — additive routes only) | + +**Why `955fe33` and not `main` HEAD.** Three commits landed after it +(`e5c1061`, `9f1e58a`, `4d29b8c`), so the newest commit is *not* the newest +image. None of the three touches a CI trigger path — they are `WORK-RECORDS.md`, +a workplan file, and `.custodian-brief.md` — so no build was triggered and none +was needed. `955fe33` is the last commit containing source, and therefore the +correct image. Checked rather than assumed, because "latest commit" and "latest +image" diverging silently is exactly how a stale artifact ships. + +`latest` resolves to the same digest, which corroborates it. + +Verified by running the pulled image locally, not by trusting the tag: + +``` +/health {"status":"ok","version":"0.1.0"} +/tenants/{tenant_id}/guardrails GET +/tenants/{tenant_id}/guardrails/{limit_key} PUT, DELETE +``` + +12 paths total: the two guardrail paths plus all ten pre-existing ones +(create, role read/grant/revoke, roles/live, plan, PATCH, retire, reactivate, +health) — so the additive claim holds against the artifact, not just the diff. + +Note for future runs: the image listens on **8090**, not 8000. + ## T02 - Wait for the flex-auth nine-action pin ```task id: TEN-WP-0007-T02 -status: wait +status: done priority: high +state_hub_task_id: "a5e6f0e5-0a77-4075-b0f6-8ed9bd3b5e2c" ``` Waiting on FLEX-WP-0014 step 1. Two things to collect when it lands: @@ -80,12 +117,39 @@ Waiting on FLEX-WP-0014 step 1. Two things to collect when it lands: Done when guardrail checks return allow instead of `deny unknown_action` against the live decision surface. +Done 2026-08-16 by flex-auth (FLEX-WP-0014 step 1). + +| Fact | Value | +| --- | --- | +| flex-auth image | `@sha256:1bf060e61122693ce98359c167cc5fe8bdafc84e097e090eaa71af94d0f27cbc` | +| CI tag | `main-f304688` | +| Package | `tenant-engine.write-api.mutate` v1, **nine** actions | +| Approval | `8d1efcea-93c7-436b-a749-e5d464e94a33` | +| Rollback target | `@sha256:9320df39…` (seven actions) | + +Live `/v1/check` after their pin: + +``` +tenant.guardrail.read actor=flex-auth allow decision:9b9e540de13cf7cf +tenant.guardrail.set actor=tenant-engine allow decision:20c82bc38501a343 +tenant.guardrail.set actor=flex-auth deny action_not_granted +misspelled guardrail deny unknown_action +unknown subject deny unknown_subject +tenant.create / tenant.retire allow (still) +``` + +Both asks answered: **the seven pre-existing actions still allow**, so the +silent-rollback failure mode that hit TEN-WP-0005 did not recur, and +`flex-auth-user-engine` was not moved. The read/write split is real, not +nominal — `flex-auth` itself is denied the write. + ## T03 - Apply the migration and pin the tenant-engine image ```task id: TEN-WP-0007-T03 status: wait priority: high +state_hub_task_id: "ea509a74-2030-4484-a6e0-f4543a8d394f" ``` The guardrail schema change is two new tables created by the same @@ -108,12 +172,34 @@ operator step. Done when the deployment is pinned to the T01 digest and the pre-existing tenants are intact. +Status 2026-08-16: **repo side done, cluster apply outstanding.** +`deploy/tenant-engine.yaml` now pins +`@sha256:44ca65f3cdd5967b0124e16b6aba10bf1ac2747e1dde96cbef3dd11ae7cc9574` +(was the TEN-WP-0005 lifecycle digest `08be0b1d…`). The manifest already +targets `flex-auth-tenant-engine.flex-auth.svc.cluster.local:8080`, which is +the deployment flex-auth just re-pinned to nine actions, so no env change is +needed. + +Remaining, and it needs an operator with cluster credentials: + +```bash +kubectl -n tenant-engine apply -f deploy/tenant-engine.yaml +kubectl -n tenant-engine rollout status deploy/tenant-engine +# confirm the pre-existing tenant survived the forward-only migration +kubectl -n tenant-engine exec deploy/tenant-engine -- \ + curl -s localhost:8090/tenants/tenant:trial:portalcheck +``` + +`kubectl` returns `Unauthorized` from this workstation, so I have not applied +it and cannot verify the live result. + ## T04 - Live verification and consumer confirmation ```task id: TEN-WP-0007-T04 status: wait priority: high +state_hub_task_id: "6e4961e2-25e4-43c4-9f39-113610306fe9" ``` Against a disposable tenant, in the TEN-WP-0005-T05 evidence style: