From e54f67c537789261c0dad41259744262c13be454 Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 18 Aug 2026 12:52:40 +0200 Subject: [PATCH] Document the live caller-identity and lifecycle probes The caller side of USER-WP-0023 is deployed, but FLEX-WP-0015-T02 is still wait: flex-auth's TokenReview choke point exists in source while the running digest is unchanged, so production accepts unauthenticated callers and our Authorization header is ignored. Running the T03 probe now would record a false pass on the negative case. Captures both owed live exercises as concrete commands so they can be run by whoever holds cluster credentials the moment flex-auth promotes A2, and notes that they share one rollout. Co-Authored-By: Claude Opus 5 --- docs/flex-auth-caller-identity.md | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/docs/flex-auth-caller-identity.md b/docs/flex-auth-caller-identity.md index ea89c56..b97c70e 100644 --- a/docs/flex-auth-caller-identity.md +++ b/docs/flex-auth-caller-identity.md @@ -26,3 +26,74 @@ URL-encoded. No client may infer existence from an unauthorized read. The current deployed image predates this file-based caller token. Promote only with the matching flex-auth A2 image and bindings; otherwise enforcing flex-auth will correctly return 401 to the old caller. + +## Live promotion probe (USER-WP-0023-T03) + +The caller side is deployed: image `sha256:c501aeb2…` (user-engine `7604d31`) +runs in namespace `user-engine` as ServiceAccount `user-engine`, with the +projected token mounted at `/var/run/secrets/flex-auth-caller/token` under +audience `flex-auth` and `USER_ENGINE_FLEX_AUTH_TOKEN_FILE` pointing at it. + +**This probe cannot pass yet, and the reason is upstream.** FLEX-WP-0015-T02 +is `wait`: ADR 0004's TokenReview choke point is implemented in flex-auth +source, but the running digest is unchanged, so production still accepts +unauthenticated callers. Our Authorization header is currently sent and +ignored. Run the probe only after flex-auth promotes A2 through +FLEX-WP-0011; before then step 2 returns a normal decision instead of 401 and +would record a false pass. + +Run from an operator shell with cluster credentials (this repo's sessions do +not hold them). `POD` is any ready user-engine pod. + +```bash +POD=$(kubectl -n user-engine get pod -l app.kubernetes.io/name=user-engine \ + -o jsonpath='{.items[0].metadata.name}') +CHECK=http://flex-auth-user-engine.flex-auth.svc.cluster.local:8080/v1/check +BODY='{"actor":{"issuer":"https://kc.coulomb.social","subject":"probe", + "tenant":"tenant:friendly:binky","principal_type":"human", + "audience":["user-engine"],"roles":["tenant-admin"]}, + "resource":{"system":"user-engine","type":"user-engine:user","id":"probe"}, + "action":"user.update","tenant":"tenant:friendly:binky", + "correlation_id":"probe-t03","context":{}}' +``` + +1. **A valid caller succeeds.** Expect `200` and a decision id. + ```bash + kubectl -n user-engine exec "$POD" -c portal -- sh -c \ + "curl -sS -o /dev/null -w '%{http_code}\n' -X POST $CHECK \ + -H 'Content-Type: application/json' \ + -H \"Authorization: Bearer \$(cat /var/run/secrets/flex-auth-caller/token)\" \ + -d '$BODY'" + ``` +2. **No token is refused.** Expect `401`, not a decision. + ```bash + kubectl -n user-engine exec "$POD" -c portal -- sh -c \ + "curl -sS -o /dev/null -w '%{http_code}\n' -X POST $CHECK \ + -H 'Content-Type: application/json' -d '$BODY'" + ``` +3. **user-engine cannot represent another protected system.** Re-run step 1 + with `resource.system` set to `tenant-engine`. Expect a refusal from the + binding, not an allow: the token binds `user-engine` to + `system:serviceaccount:user-engine:user-engine` only. + +Record all three results, the flex-auth digest they ran against, and the +user-engine digest, in USER-WP-0023-T03 before marking it done. A pass on +steps 1 and 3 without a 401 on step 2 means enforcement is still off. + +## Live cross-service check (USER-WP-0021) + +Owed separately to tenant-engine after production served 404 on the lifecycle +routes from 2026-08-13 to 2026-08-16 while our contract-level suite passed +throughout. Contract evidence structurally cannot see a pin rollback, so this +must be exercised live, from a pod, against the real authority. + +Use a disposable tenant — never a real one, since retire is a lifecycle +mutation. Through the portal's own platform routes as a platform operator: +`GET` the record, `PATCH` its display name, `retire`, then `reactivate`, +echoing each returned version as the next `If-Match`. Expect `200` throughout +and no `404`, which is what the rollback window produced. Capture the +tenant-engine digest alongside the results; both silent rollbacks this month +were digest-level and neither raised an alert. + +This shares the operator rollout with the T03 probe above and is most +efficiently run in the same session.