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

@ -0,0 +1,46 @@
# Local evidence classification
Status: implemented
Updated: 2026-08-29
Standard: `net-kingdom/canon/standards/security-layer-model_v0.7.md` §9.6
Companion: `net-kingdom/SECURITY-COMPANION.md` §6
Local audit records and the durable outbox prove the records they hold
were **not altered or truncated after arrival**. They do not prove that
an event never sent did not happen.
Sound sentences:
- the archive proves the records it holds were not altered or truncated after arrival
- absence of a record is not evidence of non-occurrence
Unsound sentences, not used here:
- the audit record proves it happened
- there is no record, so it did not happen
- the trail is complete
## Classification
| Kind | Test | Local form |
| --- | --- | --- |
| **Load-bearing** | a control's soundness depends on the event being present | authorization denials; `account.status_changed`; `tenant_account.status_changed`; `prepared_account.revoked`; `family_invitation.revoked` |
| **Attributive** | forensic reconstruction; no control branches on presence | user/profile/membership-added/catalog/registration/hat events |
| **Heartbeat** | positive claim that can itself go missing | `user_engine.evidence.heartbeat` |
Successful mutations enqueue local audit and outbox in the same store
transaction. That prevents accidental omission (crash between save and
emit) of the *local* record. Drain to `audit-core` is after commit.
## Cadence
Rate monitoring cannot detect suppression of rare denials and
revocations. The required form is a **heartbeat**:
`UserEngineService.record_evidence_heartbeat()` emits a positive claim
with counts of load-bearing classes already on the local trail. The
payload sets `completeness_claimed: false`. Expected interval: one hour
when a drain loop is running. The heartbeat is the claim that can go
missing; it is not a completeness proof.
Adversarial omission at a compromised source is a known residual.
Nothing in this model prevents it.

View file

@ -1,12 +1,17 @@
# Evidence Gap Examples
Status: candidate
Updated: 2026-06-05
Updated: 2026-08-29
`user-engine` should not pretend missing review or governance material exists.
When identity-domain context lacks evidence, policy, control, review, or task
references, the gap must be explicit and handoff-ready.
Local audit and the durable outbox prove the records they hold were not
altered or truncated after arrival. Absence of a record is not evidence
of non-occurrence. Classification and the denial/revocation heartbeat
are in `docs/evidence-classification.md`. Completeness is not claimed.
## Gap Shape
```yaml

View file

@ -87,8 +87,14 @@ contexts are omitted from projections for other applications.
## Export Boundary
`export_access_control_facts` returns an adapter-neutral manifest plus facts.
External authorization engines or ACL systems can consume these facts, but
they remain responsible for final policy decisions and runtime enforcement.
Hats, access profiles, and `AccessControlFact` values are PIP **claims**.
They are not an authorization decision. Compiling them into a local
allow or deny before `access-engine` runs is an early decision
(security-layer-model §6.1) and is forbidden.
`select_active_hat` and `export_access_control_facts` never return an
effect. `access-engine` remains the only decision point; consumers that
need an allow or deny must ask it.
## Redaction And Diagnostics

View file

@ -4,9 +4,12 @@
Use `readiness()` for dependency checks and `operability_snapshot()` for
runtime counters and invariant checks. The snapshot currently reports store
readiness, audit correlation completeness, outbox diagnostic availability, and
counts for users, accounts, tenant accounts, memberships, applications,
catalogs, profile values, audit records, and pending outbox events.
readiness, whether written audit records carry a correlation id, outbox
diagnostic availability, and counts for users, accounts, tenant accounts,
memberships, applications, catalogs, profile values, audit records, and
pending outbox events. Those checks describe the records in hand. They
do not prove that every event that should have been emitted was emitted.
See `docs/evidence-classification.md`.
## Structured Logs