Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
217 lines
10 KiB
Markdown
217 lines
10 KiB
Markdown
---
|
|
id: FLEX-WP-0035
|
|
type: workplan
|
|
title: "Per-identity application access readback and readable protected-system registry for user-engine"
|
|
domain: infotech
|
|
repo: access-engine
|
|
status: finished
|
|
owner: claude
|
|
topic_slug: netkingdom
|
|
created: "2026-10-09"
|
|
updated: "2026-10-10"
|
|
related: [USER-WP-0026, USER-WP-0028]
|
|
state_hub_workstream_id: "cb9755fa-6824-5288-81c4-3aa2fbff1a9f"
|
|
---
|
|
|
|
user-engine (message `f1637c32`, 2026-10-09, routed by net-kingdom `19a0cbcd`)
|
|
needs to show a signed-in person which applications they can use. Its
|
|
`USER-WP-0026-T03` and `USER-WP-0028-T02` are `blocked_on` access-engine. The
|
|
PEP stance is already fail-closed: no decision renders `unavailable`, and allow
|
|
is never inferred from membership, platform role, registration, or a missing
|
|
record.
|
|
|
|
## What exists today (2026-10-09 review)
|
|
|
|
- `POST /v1/batch_check` takes several resources in one call and returns one
|
|
decision per resource, each with a `lifetime` (`expires_at`) per the decision
|
|
envelope. Shape-wise this is close to request (1).
|
|
- Caller auth authorizes the caller token against **every** `resource.system`
|
|
in the batch (`cmd/flex-auth/main.go`, `/v1/batch_check`). The
|
|
`flex-auth-user-engine` caller is bound to its own protected system, so a
|
|
readback across other systems is a 403 today. That is correct behaviour for
|
|
enforcement, and the reason this needs design rather than a config change.
|
|
- There is no generic "may use this application" action. Each package has its
|
|
own action vocabulary, so "access to system X" has no defined meaning yet.
|
|
- There is no registry read endpoint. The protected-system registry is loaded
|
|
from files and never served.
|
|
|
|
## Define the readback contract
|
|
|
|
```task
|
|
id: FLEX-WP-0035-T01
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "0585a684-d462-5caa-937f-b1730efddbd8"
|
|
```
|
|
|
|
Decide the action that a readback evaluates per system: a declared
|
|
`application.access` action each consumer package must opt into, versus a
|
|
per-system mapping held in the registry. A system that declares nothing is
|
|
returned as `not_applicable`, never allow. State per input class the
|
|
revocation-visibility deadline the readback carries (§9.7). Decide whether the
|
|
readback is a decision for audit purposes (decision record, emission) or an
|
|
explicitly non-enforcing advisory read. A displayed allow must not be usable as
|
|
a PEP decision by another system.
|
|
|
|
Gate: written contract in `docs/` plus an ADR or decision record; reviewed with
|
|
user-engine before code.
|
|
|
|
2026-10-09: user-engine accepted the contract (message `f54df082`) with three
|
|
amendments, now folded in: the `/v1/check` subject shape with `tenant`, the
|
|
rendering table, and a separate `USER_ENGINE_READBACK_TOKEN_FILE`. Also added: a
|
|
realm-level-claims-only rule. `FLEX-DEC-2026-019` is accepted.
|
|
|
|
2026-10-09: activated by the founder. Draft contract
|
|
`docs/application-access-readback.md` and `FLEX-DEC-2026-019` (proposed). Key
|
|
finding: each pin loads one consumer's package, so no existing pin can answer
|
|
across applications. The draft proposes a dedicated readback pin with
|
|
multi-package mode, a `purpose: "readback"` binding that keeps decisions
|
|
non-transferable, and a new optional `client_binding` manifest field. Sent to
|
|
user-engine with four review questions. The gate closes on their reply.
|
|
|
|
## Caller authorization for cross-system readback
|
|
|
|
```task
|
|
id: FLEX-WP-0035-T02
|
|
status: done
|
|
priority: high
|
|
depends_on: [FLEX-WP-0035-T01]
|
|
state_hub_task_id: "eb0b6a91-7750-553b-b87f-c4262eb9df07"
|
|
```
|
|
|
|
Add a distinct caller capability (readback scope) instead of widening the
|
|
`flex-auth-user-engine` binding to other systems' check rights. The readback
|
|
caller must not be able to obtain a decision that another system's PEP would
|
|
accept, and its decisions must say so in the binding.
|
|
|
|
Gate: tests prove a readback-scoped token gets 403 on `/v1/check` for a foreign
|
|
system and is admitted only on the readback route.
|
|
|
|
2026-10-09 done. `flex-auth serve-readback` is a separate subcommand that mounts
|
|
no `/v1/check` or `/v1/batch_check` (404 there). `--readback-caller` is bound
|
|
under the non-system key `application-readback`. `CheckRequest.purpose` is
|
|
hashed into `request_digest`, `submitted_request_digest` and
|
|
`approval_binding_digest`, but only when set, so enforcement digests are
|
|
unchanged. `/v1/check` refuses a request that carries it. Tests:
|
|
`cmd/flex-auth/readback_test.go`
|
|
(`TestReadbackPrincipalCannotCheckOnEnforcementPin`,
|
|
`TestReadbackHTTPCallerAndRequestShape`, `TestReadbackWithholdsUncommittedAllow`)
|
|
and `pkg/api/purpose_test.go`.
|
|
|
|
## Readable protected-system registry
|
|
|
|
```task
|
|
id: FLEX-WP-0035-T03
|
|
status: done
|
|
priority: medium
|
|
depends_on: [FLEX-WP-0035-T01]
|
|
state_hub_task_id: "5dbb3280-8490-58a5-a0bb-f3883e568f58"
|
|
```
|
|
|
|
Serve a read-only projection: system id, display name, key-cape client binding.
|
|
Include the registry-snapshot digest so the catalogue is tied to the same
|
|
snapshot that decisions record (G1, `FLEX-WP-0019`). Grants and revocations stay
|
|
inside access-engine; user-domain admins get no grant surface from this work.
|
|
|
|
Gate: endpoint plus schema, admitted only for the readback caller, tests.
|
|
|
|
2026-10-09 done. `GET /v1/readback/systems`; optional `client_binding` on
|
|
`protected_system_manifest`. Schemas `readback_request`, `readback_response`
|
|
and `readback_systems` are checked against live service output
|
|
(`TestReadbackShapesMatchSchemas`). schemaguard gained `maxItems` for this.
|
|
|
|
## informed-decision declares `application.access`
|
|
|
|
```task
|
|
id: FLEX-WP-0035-T05
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "701cbd99-1a84-5290-8f66-15fa7f407d3b"
|
|
```
|
|
|
|
informed-decision is the first participant. Its `informed-decision.lifecycle`
|
|
package reads `authentication_audience`, which the readback must not use
|
|
(contract §3). Add an `application.access` rule over realm-level claims only,
|
|
as a new package version with fixtures. Coordinate the version bump with
|
|
informed-decision, because its own pin must run the same digest. Add
|
|
`client_binding` to its registry entry.
|
|
|
|
Gate: package valid with allow/deny fixtures, informed-decision agrees, and
|
|
the rule reads no audience, authorized-party or client-scoped claim.
|
|
|
|
2026-10-09 source done. `informed-decision.lifecycle` is now v2
|
|
(`sha256:da01e045…`, `pin.json` updated). It adds `application.access`, which
|
|
requires `purpose == "readback"` and the existing `human` bar: net-kingdom-admins,
|
|
KeyCape AAL2 `pwd`+`otp` within 12h, platform tenant. It has no workload path
|
|
and reads no audience or client-scoped claim. v1's twelve operations are
|
|
unchanged. Eight Go cases cover it, plus a fixture showing that it denies
|
|
without the readback purpose. The catalogue `examples/application-readback/`
|
|
binds informed-decision to `https://kc.coulomb.social` /
|
|
`informed-decision-approver`. `TestReadbackInformedDecisionCatalogue` proves the
|
|
readback over the shipped files. The live lifecycle pin still runs v1 (image
|
|
`bdac62bc`) until the T04 rollout.
|
|
|
|
2026-10-10 done. Founder agreed to lifecycle v2 for informed-decision
|
|
(informed-decision itself had not replied to `7fcb1eb7`).
|
|
|
|
## Deploy the readback pin
|
|
|
|
```task
|
|
id: FLEX-WP-0035-T04
|
|
status: done
|
|
depends_on: [FLEX-WP-0035-T05]
|
|
priority: medium
|
|
state_hub_task_id: "82a8a7e6-54e9-5262-a271-24ce2f161362"
|
|
```
|
|
|
|
Add `values/flex-auth-application-readback.yaml` (or a deploy manifest) for a
|
|
dedicated `flex-auth-application-readback` pin, plus a test asserting that its
|
|
package paths match each participant's own pin values (digest-equality, §4).
|
|
Add the user-engine ingress rule, then get the founder's go for the rollout.
|
|
|
|
Promised to user-engine (reply to `d7e02758`, 2026-10-10), so the values file
|
|
must match: URL
|
|
`http://flex-auth-application-readback.flex-auth.svc.cluster.local.:8080`
|
|
(trailing dot, in-cluster only), caller token audience `flex-auth`
|
|
(`serve-readback --caller-audience` default), readback caller
|
|
`system:serviceaccount:user-engine:user-engine`.
|
|
|
|
2026-10-10 repo side done (`bb14ec6`). `values/application-readback.yaml` and the
|
|
chart's `readback.enabled` mode, which renders `--readback-caller`. The lifecycle
|
|
pin moves to `e8c57c18` (`main-b5da745`, v2). Rollback target is `bdac62bc`.
|
|
`TestReadbackPinMatchesParticipantPins` checks that the readback pin uses the same
|
|
image and package path as each participant's own pin. Rollout order: the lifecycle
|
|
pin, then the readback pin.
|
|
|
|
2026-10-10 done. The founder ran both helm upgrades: the lifecycle pin is at
|
|
revision 2 and `flex-auth-application-readback` at revision 1, both on `e8c57c18`.
|
|
Verified: both Ready, `/healthz` 200 on both, the readback routes return 401
|
|
without a token, `/v1/check` returns 404 on the readback pin, anonymous lifecycle
|
|
`/v1/check` returns 401, and ingress admits only `user-engine/user-engine`.
|
|
user-engine's live check with its own token is still to come.
|
|
The pin ships with the image that contains `serve-readback`. Hand user-engine
|
|
the evidence that unblocks `USER-WP-0026-T03`.
|
|
|
|
## Completion review — 2026-10-10
|
|
|
|
All five tasks are done. Fresh read-only verification on railiance01 (node
|
|
92.205.62.239) confirms the lifecycle and application-readback deployments
|
|
are each Ready 1/1 on identical immutable image
|
|
`sha256:e8c57c18a4a9d5583be099146984f7e9cd881446218b65aeefd7619b18aa9229`.
|
|
Readback ingress is restricted to the `user-engine` namespace and
|
|
`app.kubernetes.io/name=user-engine` pods on port 8080. The source regression
|
|
run `go test ./cmd/access-engine ./pkg/api -run 'TestReadback|TestPurpose'
|
|
-count=1` passes, including participant image/package parity, non-transferable
|
|
readback purpose, unauthorized-caller refusal, enforcement-route isolation,
|
|
MFA denial, response schema and outbox failure handling.
|
|
|
|
The earlier task records retain founder rollout authority and native anonymous
|
|
route/health receipts; this review performed no deployment or credential action.
|
|
|
|
The consumer integration and actual user-engine token probe remain live in the
|
|
existing owner task `USER-WP-0026-T03` (and the related consumer UI task
|
|
`USER-WP-0028-T02`). Those checks do not require unfinished Access Engine
|
|
implementation. The service contract and URL are in this workplan and
|
|
`docs/application-access-readback.md`. No new residual workplan is needed: the
|
|
receiving owner records already carry the work. This closure does not assert
|
|
that the consumer has integrated or that other applications opted in.
|