Implement USER-WP-0024 security layer conformance
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:
parent
c7b6148a70
commit
4349758608
22 changed files with 1242 additions and 89 deletions
46
docs/evidence-classification.md
Normal file
46
docs/evidence-classification.md
Normal 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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue