Compare commits

..

No commits in common. "e85811521059b70ce1d4b449407a7c7f5cac5e80" and "7604d317768e77c0efca4989abb41ca0b93ff8a2" have entirely different histories.

2 changed files with 4 additions and 99 deletions

View file

@ -1,6 +1,6 @@
# flex-auth caller identity contract
Status: caller side implemented and deployed; live proof pending flex-auth A2 promotion (FLEX-WP-0015-T02).
Status: source implemented; production promotion pending.
user-engine calls `flex-auth-user-engine` with a projected Kubernetes
ServiceAccount token whose audience is exactly `flex-auth`. The adapter reads
@ -23,80 +23,6 @@ The tenant authority seam is distinct: user-engine identifies itself as actor
own flex-auth decision before store access. Tenant ids remain opaque and are
URL-encoded. No client may infer existence from an unauthorized read.
As of 2026-08-18 the deployed user-engine image carries this file-based caller
token, so the caller side is no longer the laggard. The remaining asymmetry runs
the other way: flex-auth's A2 enforcement is implemented but unpromoted, so the
header is sent and ignored. That ordering is safe — a caller that authenticates
against a service that does not yet check is harmless, whereas the reverse
would have returned 401 to every decision.
## 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.
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.

View file

@ -59,25 +59,4 @@ ServiceAccount token manifest. Prove a valid caller succeeds, no token returns
401, and user-engine cannot represent another protected system. This is a live
operator rollout and was not performed by the source change.
2026-08-18 readiness review: our half is done. The `rapp-user-engine` managed
package is now the apply home, and image `sha256:c501aeb2…` from `7604d31`
which contains the caller-token change — is live and passed `make verify-live`.
The projected ServiceAccount token manifest is in place: audience `flex-auth`,
mounted at `/var/run/secrets/flex-auth-caller/token`, with
`USER_ENGINE_FLEX_AUTH_TOKEN_FILE` pointing at it. flex-auth's deploy carries
the binding `user-engine=system:serviceaccount:user-engine:user-engine`.
The blocker is now precisely one upstream item. FLEX-WP-0015-T02 is `wait`:
ADR 0004's TokenReview choke point exists in flex-auth source, but the running
digest is unchanged, so production still accepts unauthenticated callers and
our Authorization header is sent and ignored. Running the probe today would
pass steps 1 and 3 and silently fail step 2 — a false pass on the only
assertion that proves enforcement. The probe is therefore written down rather
than run: see `docs/flex-auth-caller-identity.md`, which carries all three
checks as commands plus the digests to record.
This task stays `wait` on FLEX-WP-0015-T02 promotion through FLEX-WP-0011,
and on an operator shell with cluster credentials, which agent sessions in
this repo do not hold.
Contract: `docs/flex-auth-caller-identity.md`.