Implement USER-WP-0024 security layer conformance
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 36s

Declare Engine/PIP machine-readably, publish a total fail-closed PEP
stance map, stop minting local decision ids on engine-unavailable DENY,
bind allows to a 30s request lifetime, confine the local authorization
double, classify evidence and emit a denial/revocation heartbeat, and
prove access-control facts remain claims.

Assistant: grok
Assistant-Session: 01a04cea-f0d6-7ab3-9ffd-881eb6bea6cb
This commit is contained in:
tegwick 2026-08-29 12:53:16 +02:00
parent c7b6148a70
commit 4349758608
22 changed files with 1242 additions and 89 deletions

View file

@ -127,10 +127,11 @@ self-service and admin portal, public-registration orchestration, and
flex-auth caller identity (live A2 on `flex-auth-user-engine`) are in the
repo and, where applicable, on Railiance.
`USER-WP-0024` is the active workplan: layer declaration is in `INTENT.md`;
machine-readable conformance, the published PEP stance map, decision
lifetime, evidence cadence, and the access-control-fact claim contract
are not yet shipped.
`USER-WP-0024` ships the layer-conformance follow-through: `layer.yaml`
and `pep-stance.yaml`, fail-closed stance recording without a minted
decision id, request-bound allow lifetime, local-authorization
confinement, evidence classification and heartbeat, and the
access-control-fact claim contract.
Still operator-owned, not remaining product scope:
@ -142,18 +143,19 @@ Still operator-owned, not remaining product scope:
- `policy.enabled` and tenant-engine caller `enforce` belong to flex-auth
/ tenant-engine.
Layer-model residue that is remaining product scope, tracked in
`USER-WP-0024` and assessed in
Layer-model residue that `USER-WP-0024` closed, assessed in
`history/2026-08-29-security-layer-scope-intent-assessment.md`:
- no `layer.yaml` and no conformance check;
- unreachable-engine stance is fail-closed in
`FlexAuthHTTPAdapter` but unpublished, untested against a map, and
recorded as a locally minted `decision_id`;
- `AuthorizationDecision` carries no lifetime;
- denials have no declared emission cadence or heartbeat;
- `LocalAuthorizationCheckPort` defaults to allow and is a second
decision point if a production runtime ever constructed it.
- `layer.yaml` plus `scripts/check_layer_conformance.py`;
- published `pep-stance.yaml`, fail-closed total, tested equal to
`FlexAuthHTTPAdapter`; engine-unavailable DENY records stance
application and carries no `decision_id`;
- `AuthorizationDecision` allows are request-bound with a 30s lifetime;
- denials and revocations are load-bearing; cadence is
`record_evidence_heartbeat()`;
- `LocalAuthorizationCheckPort` cannot be constructed when
`USER_ENGINE_FLEX_AUTH_TOKEN_FILE` is set, and `runtime.py` does not
import it.
## Against INTENT.md
@ -169,12 +171,12 @@ and PEP obligations.
| Users, links, memberships, catalogs, projections, events | Met. |
| NetKingdom identity-domain integration layer | Met for the owned slice. Consumes KeyCape, flex-auth, tenant-engine, identity-provisioner, audit-core, email-connect. |
| Applications answer who / which scopes / what to project | Met via `/me`, identity context, catalogs, and projections. |
| Engine / PIP declaration in own voice | Met in `INTENT.md` as of 2026-08-29. Closes the declaration half of `USER-IN-0001`. |
| Subject context is a claim, never a decision | Held in production path (`FlexAuthHTTPAdapter`). Not yet proven for hats/access-control facts under §6.1, and the local double can still decide. |
| PEP-shaped: no side effect without a decision or recorded stance | Partial. Production asks flex-auth and fail-closes on errors. Stance is unpublished; unavailable DENY mints a local `decision_id`. |
| Published unreachable-engine stance map, tested equal to shipped behaviour | Missing. |
| Every allow has a lifetime | Missing on `AuthorizationDecision`. |
| Evidence bound: no completeness claim; cadence for load-bearing events | Partial. Mutations enqueue audit+outbox in the same store transaction. Completeness is not claimed in code; docs do not classify load-bearing vs attributive or declare a cadence. |
| Engine / PIP declaration in own voice | Met in `INTENT.md` and `layer.yaml`. Closes `USER-IN-0001`. |
| Subject context is a claim, never a decision | Held. Production asks flex-auth. Hats and access-control facts have no effect field; tests forbid compiling them into allow/deny. The local double is confined to tests/standalone. |
| PEP-shaped: no side effect without a decision or recorded stance | Met. Unavailable DENY records `stance_applied=fail_closed` and no `decision_id`. |
| Published unreachable-engine stance map, tested equal to shipped behaviour | Met (`pep-stance.yaml`). Gate-house still owes the §13.1 inventory row. |
| Every allow has a lifetime | Met. Production allows are request-bound, 30s. Standing grants are denied. |
| Evidence bound: no completeness claim; cadence for load-bearing events | Met. Classification and heartbeat in `docs/evidence-classification.md` and `UserEngineService.record_evidence_heartbeat()`. |
| Not an IdP, PDP, secret store, directory, or org authority | Held. |
| Optional UI, not UI-driven | Held, with a narrower reading: an optional portal now lives *in this repo* over the same APIs. INTENT's "not a UI application" still applies to product identity. |
| Canon-aligned mappings without taking IAM as SoT | Met (`USER-WP-0007`, interface card). Access-review, policy, and control remain references, not owned records. |
@ -191,6 +193,5 @@ Still aspirational, and deliberately not started here:
- observing production, or actuating containment — estate-wide zeros,
not this repo's gaps to close.
Those remain INTENT, not a hole in SCOPE, except the layer-conformance
rows above, which are now SCOPE because INTENT has adopted the accepted
standard.
Those remain INTENT, not a hole in SCOPE. Layer-conformance rows above
are met by `USER-WP-0024`; the §13.1 inventory row is gate-house's.