From 96430299b72f5d3ef90917f55575d749ddbfeb80 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 29 Aug 2026 11:56:06 +0200 Subject: [PATCH] Declare Engine/PIP and open USER-WP-0024 for layer conformance Adopt security-layer-model v0.7 in INTENT.md in this repository's own voice, restate SCOPE.md from that declaration, and record the scope-versus-implementation assessment. USER-IN-0001 is answered; runtime follow-through is USER-WP-0024. Assistant: grok Assistant-Session: 01a04cea-f0d6-7ab3-9ffd-881eb6bea6cb --- INTENT.md | 151 +++++++-- SCOPE.md | 108 ++++-- ...-security-layer-scope-intent-assessment.md | 317 ++++++++++++++++++ intakes/intakes.md | 11 +- ...USER-WP-0024-security-layer-conformance.md | 152 +++++++++ 5 files changed, 692 insertions(+), 47 deletions(-) create mode 100644 history/2026-08-29-security-layer-scope-intent-assessment.md create mode 100644 workplans/USER-WP-0024-security-layer-conformance.md diff --git a/INTENT.md b/INTENT.md index b3d4165..c25e0ff 100644 --- a/INTENT.md +++ b/INTENT.md @@ -1,24 +1,94 @@ -# INTENT +--- +layer: Engine +role: PIP +standard: net-kingdom/canon/standards/security-layer-model_v0.7.md +companion: net-kingdom/SECURITY-COMPANION.md +declared: "2026-08-29" +--- -> **NetKingdom layering review — 2026-08-28.** This repository's role was reviewed -> against the NetKingdom IT-security layer model: **Taxonomy → Tooling → Engines → -> Staff**, layered by determinism and by the kind of artifact each layer produces. -> Findings and the argument behind them: -> `gate-house/history/2026-08-28-security-layer-model-and-gate-house-recut.md`. -> The model is `net-kingdom/canon/standards/security-layer-model_v0.1.md` (proposed), -> ratified by `gate-house/decisions/decisions.md` GH-DEC-2026-001. -> -> The layer rule that binds every repository: **Staff never touches tooling -> directly. It acts only through engine APIs.** -> -> **This repository is Engine — deterministic API for a modeled concept.** Add the layer label. The existing boundary contract (`net-kingdom/canon/standards/user-engine-boundary-contract_v0.1.md`) holds unchanged. Worth making explicit that user-engine supplies subject context as input to the authorization decision and never renders one. -> -> *This note records what should change. The body below is not yet adapted.* +# INTENT > This file captures why this repository exists, the direction it is moving > toward, and the kind of system it is meant to become. It is intentionally > aspirational and stable, not a description of current implementation. +## Layer + +**Engine, role PIP.** This is user-engine's own declaration under the +NetKingdom Security Layer Model +([`SECURITY-COMPANION.md`](../net-kingdom/SECURITY-COMPANION.md) v0.2; +statute [`security-layer-model_v0.7.md`](../net-kingdom/canon/standards/security-layer-model_v0.7.md), +accepted 2026-08-29). A layer stated about this repository by another +repository is not a declaration; this one is. + +The same authoritative user-domain inputs yield the same result. That +determinism is the test. user-engine is not Staff: it is not a judgment +surface. It is not Tooling: it does not package identity infrastructure or +hold runtime secrets. It is not the PDP: `access-engine` (`flex-auth` until +the governed rename) is the only decision point in the estate. + +The catalog row we accept: **users, accounts, memberships.** The existing +boundary contract +(`net-kingdom/canon/standards/user-engine-boundary-contract_v0.1.md`) holds +unchanged. What the model adds, and what this file now states in our own +voice: + +- we **supply subject context as claims** a decision consumes; +- we **never render, cache, or compile an authorization decision**; +- a new engine is a PIP unless the statute is amended, and we stay one. + +**PEP-shaped, same layer.** Protected mutations in this service — account +lifecycle, memberships, invitations, registration orchestration, hat +selection, catalog and profile writes — proceed only after a decision from +`access-engine`, or under a published unreachable-engine stance whose +application is recorded in place of a decision. Being PEP-shaped does not +move the repository out of Engine/PIP. The stance map belongs at a path +named in the machine-readable layer declaration, registered in statute +§13.1; shipping fail-closed in an adapter is not publication. + +**Not contested.** Engine/PIP is the right assignment. What we raise back +is that §4 catalogues us as Engine/PIP and does not mark the PEP shape +that our protected mutations already have. That is a register finding, +not a layer dispute. + +### Binding rules this repository keeps + +1. **One decision point.** `access-engine` renders authorization decisions. + user-engine asks, enforces, and records. Access-control facts, hats, + memberships, and projections are inputs and read models, never a local + allow. +2. **Compiled data that determines an outcome is still deciding.** A + registry, cache, or access-profile schema that resolved a result before + the engine ran would be an early decision. Provenance of anything we + export as claims must remain reconstructable from the decision record + that consumed it. +3. **Doctrine arrives as a claim.** Nothing we hold — membership, hat, + assurance forwarded from IAM, tenant capability roles — becomes + authority except by reaching `access-engine` as a request claim or a + versioned policy rule. +4. **Adaptive systems may only tighten.** user-engine does not manufacture + authority, and it does not assume the estate can contain anything + automatically. Actuation is unowned and held at zero. +5. **Every allow has a lifetime.** A decision we enforce is used within + its stated binding and lifetime, not as a standing grant. +6. **Evidence proves alteration and truncation, not omission at source.** + Local audit and the durable outbox prove the records they hold. They + do not prove that an event never sent did not happen. Completeness is + not claimed. +7. **Agents working this repository are Staff principals**, not a second + layer. No standing credential, tool use only as a conduit or an Engine + API, memory is not a state plane, every action reconstructable as the + caller's. How to get a credential or a lane is `ops-warden`'s, not + this file's. + +**Tooling.** §5 binds Staff. This Engine owns the persistence of the +concept it models (the user-domain store). It consumes verified IAM +Profile claims from `key-cape` / Keycloak / `local-identity`, and runtime +secret *references* injected by Railiance. It holds no OpenBao client and +no Keycloak admin client. A `SecretProvider` port exists so an engine +surface can replace environment injection when `secrets-engine` exposes +one; until then that capability is at zero here, not taken locally. + ## Purpose `user-engine` exists to provide a reusable, headless user-domain and @@ -27,6 +97,11 @@ preference, membership, identity-context, and application-specific user attribute management without coupling those concerns to a particular identity provider, authorization engine, security stack, or user interface. +As a PIP it is the estate's source of user-domain facts that authorization +and consuming applications may take as claims: who the subject is in a +domain context, which account and memberships apply, which profile +material may be projected, and which identity-context mappings hold. + ## Primary Utility The project provides a canonical user layer that can start small in a @@ -45,13 +120,16 @@ It manages: - profile projections for consuming applications; - lifecycle and profile-change events. +Those records are the PIP surface. They are not a policy package. + ## Strategic Direction `user-engine` is intended to become the NetKingdom identity-domain integration -layer: a headless service that exposes canonical identity, user, account, +layer: a headless Engine that exposes canonical identity, user, account, membership, tenant, team, profile, lifecycle, and evidence-facing concepts to -applications without requiring those applications to know the technical IAM, -security, authorization, audit, or secret-management implementation details. +applications, and that supplies those facts as claims to `access-engine`, +without requiring those applications to know the technical IAM, security, +authorization, audit, or secret-management implementation details. NetKingdom infrastructure remains the source of truth for authentication, credential assurance, token issuance, policy decisions, security controls, @@ -70,6 +148,9 @@ applications and agents to answer: claims-enrichment contexts; - which lifecycle, access-review, evidence, or integration gaps need attention. +It should never answer whether an actor may act. That answer is +`access-engine`'s, including when the resource is a user-engine record. + ## Strategic Role `user-engine` separates user-domain management from authentication, @@ -85,6 +166,11 @@ identity-context mappings. It should reference, map to, or consume adjacent canon entities when their source of truth belongs to NetKingdom infrastructure, security, access-control, governance, or organization systems. +Hats, realms, services, assets, and access profiles stay user-domain facts +and claim templates. They exist so `access-engine` and consuming runtimes +can see the same subject context; they do not become a second decision +point by compilation or by local evaluation. + ## Intended Users - application developers adding user/account functionality to a service; @@ -96,7 +182,9 @@ security, access-control, governance, or organization systems. context; - domain services that need identity-canon aligned user, actor, principal, subject, tenant, team, membership, and evidence references without depending - on technical IAM implementation details. + on technical IAM implementation details; +- `access-engine`, as a PIP consumer of subject, membership, and + access-control facts. ## Product Boundaries @@ -107,15 +195,19 @@ It does not aim to be: - a full identity provider; - a password, passkey, session, or MFA system; - a fine-grained authorization engine; -- the policy decision point for protected resources; +- the policy decision point for protected resources, including its own; - the owner of NetKingdom security controls or runtime secrets; - the full organization, HR, or directory authority; - a directory server; -- a UI application. +- a UI application; +- an observer of production security events; +- an actuation surface for containment, step-up, or authority reduction. It provides the user-domain and identity-domain APIs, mapping records, catalog -metadata, projections, events, evidence references, and audit records that those -surrounding systems can consume. +metadata, projections, events, evidence references, and local audit records +that those surrounding systems can consume. Platform audit custody stays +with `audit-core`. Optional UI in this repository is a surface over the +same APIs, not a product identity. ## Design Principles @@ -124,14 +216,17 @@ surrounding systems can consume. - standalone-friendly; - enterprise-integratable; - identity-provider agnostic; -- authorization-engine agnostic; +- authorization-engine agnostic — and never a substitute for one; - NetKingdom-integrated without hiding source-of-truth boundaries; - canon-aligned through explicit entity and relationship mappings; - catalog-driven customization; - explicit ownership, visibility, mutability, and sensitivity of attributes; - layered profiles instead of one global metadata blob; - deterministic and inspectable effective profile resolution; -- evidence and lifecycle awareness for identity-domain changes; +- evidence and lifecycle awareness for identity-domain changes, bounded by + what an archive can actually prove; +- fail closed on unreachable `access-engine` for protected mutations, with + that stance published rather than implied by adapter code; - concrete user-domain focus with a possible future extraction path toward a generic profile engine. @@ -147,3 +242,9 @@ intent: applications should be able to consume user, account, identity-link, actor, principal, subject, tenant, team, membership, profile, lifecycle, and evidence context through stable APIs and mappings without taking a direct dependency on the technical IAM, authorization, security, or audit substrate. + +It succeeds as a PIP when `access-engine` can consume those facts as claims +and reconstruct their application from the decision record, and as a +PEP-shaped engine when every protected mutation it performs is bound to a +decision or to a recorded stance, with a lifetime, and without a second +decision point hiding in a cache, a hat, or a local adapter. diff --git a/SCOPE.md b/SCOPE.md index 6799377..29cd8bd 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -1,11 +1,25 @@ # SCOPE +## Layer + +**Engine, role PIP** — deterministic API for users, accounts, and +memberships. Subject context is an input to the authorization decision and +never a decision. Protected mutations are PEP-shaped: they consume +`access-engine` (`flex-auth`) and do not substitute for it. + +Declaration: `INTENT.md` frontmatter. Statute: +`net-kingdom/canon/standards/security-layer-model_v0.7.md` (accepted). +Working companion: `net-kingdom/SECURITY-COMPANION.md`. Boundary contract +unchanged: +`net-kingdom/canon/standards/user-engine-boundary-contract_v0.1.md`. + ## One-Liner -Headless user-domain and identity-domain service for accounts, identity -links, memberships, catalogs, projections, audit, and events, with an +Headless user-domain and identity-domain Engine (PIP) for accounts, identity +links, memberships, catalogs, projections, local audit, and events, with an optional in-repo portal. It consumes NetKingdom IAM, authorization, tenant -authority, provisioning, and delivery; it does not own them. +authority, provisioning, and delivery; it does not own them and it does not +decide them. ## In Scope @@ -17,8 +31,10 @@ authority, provisioning, and delivery; it does not own them. preferences; - tenant, application, team, and scope memberships; - hats, realms, services, assets, access profiles, and active access - context as user-domain facts; + context as user-domain facts and claim templates; - identity-context read models for domain consumers; +- PIP exports: subject, membership, and access-control facts for + `access-engine` and other engines to consume as claims; - canon interface cards, entity and relationship mappings, and explicit gap records; - application registry for profile consumers; @@ -35,17 +51,22 @@ authority, provisioning, and delivery; it does not own them. (create, read, update, retire, reactivate, recover); - optional CSRF-protected portal over the same APIs (self-service, onboarding, tenant admin, platform operator); -- standalone/local fixtures and a production PostgreSQL store; +- standalone/local fixtures and a production PostgreSQL store of the + modeled concept; - integration ports for claims, flex-auth decisions (including a rotating caller token), provisioning, registration verification, tenant - management, outbox delivery, and runtime secrets. + management, outbox delivery, and runtime secrets; +- PEP enforcement of `access-engine` decisions on user-engine-owned + mutations, including a published unreachable-engine stance. ## Out Of Scope - login, OIDC/SAML token issuance, passwords, passkeys, sessions, and MFA lifecycle — `key-cape`, Keycloak, or `local-identity`; - final authorization policy decisions and the protected-system registry — - `flex-auth`; + `flex-auth` / `access-engine`; +- caching or compiling an authorization decision, including from hats, + access profiles, or memberships; - durable authorization grants beyond user-engine-owned memberships; - tenant identifier authority, grouping reclassification, and capability role grants — `tenant-engine`; @@ -53,16 +74,22 @@ authority, provisioning, and delivery; it does not own them. consuming apps (e.g. coulomb-social) and KeyCape client policy; - policy, control, access-review, exception, and organization source-of-truth ownership; -- runtime secret custody — OpenBao / Railiance; +- runtime secret custody — OpenBao / Railiance; no OpenBao client here; +- a Keycloak or key-cape admin client; - platform audit store and transactional SMTP — `audit-core` and `email-connect`; - full SCIM server, enterprise directory replacement, or inbound SAML/OIDC federation (demand-triggered; Keycloak expanded mode is the published path); -- a generic extracted profile engine. +- a generic extracted profile engine; +- observing production security events — `kings-guard`, and currently + unstaffed; +- actuation: reduce authority, require step-up, isolate a workload — an + Engine concept held at zero estate-wide. The in-repo portal is an optional surface, not a UI product. Password and -MFA screens stay on the identity provider. +MFA screens stay on the identity provider. `LocalAuthorizationCheckPort` +is a test and standalone double, not a production decision point. ## Boundary Rule @@ -73,6 +100,13 @@ deployment, event transport, durable audit, secrets, organization records, or additional UI — and they integrate through explicit adapters. They must not become hidden sources of profile or identity-domain truth. +user-engine must not become a hidden source of authorization truth. +Memberships, hats, and access-control facts are claims. `access-engine` +renders the decision. When `access-engine` is unreachable, shipped +behaviour is fail-closed for protected mutations; that stance still has +to be published, tested equal to the code, and recorded as stance +application rather than as a minted local decision id. + Governing published contracts: - IAM Profile v0.3 — https://policy.coulomb.social/standards/iam-profile/v0.3/ @@ -80,15 +114,23 @@ Governing published contracts: - NetKingdom architecture — https://policy.coulomb.social/architecture/net-kingdom/v0.1/ - User-engine boundary contract (accepted, not yet published) — `~/net-kingdom/canon/standards/user-engine-boundary-contract_v0.1.md` +- NetKingdom Security Layer Model v0.7 (accepted) — + `~/net-kingdom/canon/standards/security-layer-model_v0.7.md` +- Working companion — + `~/net-kingdom/SECURITY-COMPANION.md` ## Current Status -Workplans `USER-WP-0001` through `USER-WP-0023` are finished. There is no -active workplan. The isolated MVP, multi-tenancy, catalogs, canon -alignment, durable PostgreSQL store, 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. +Workplans `USER-WP-0001` through `USER-WP-0023` are finished. The isolated +MVP, multi-tenancy, catalogs, canon alignment, durable PostgreSQL store, +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. Still operator-owned, not remaining product scope: @@ -100,10 +142,25 @@ 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 +`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. + ## Against INTENT.md -INTENT is the stable aspiration. Against it, the repo now does the job it -set out to do. +INTENT is the stable aspiration. Against the pre-layer product aims, the +repo still does the job it set out to do. Against the layer declaration +now in INTENT, SCOPE is narrower than INTENT on conformance artifacts +and PEP obligations. | INTENT aim | Status | | --- | --- | @@ -112,10 +169,17 @@ set out to do. | 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. | | 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. | | Path from local setup to governed NetKingdom deploy | Met. | +| No OpenBao / key-cape admin client | Held. `SecretProvider` is an unused port; runtime reads env. | Still aspirational, and deliberately not started here: @@ -123,6 +187,10 @@ Still aspirational, and deliberately not started here: demand, targeting published Keycloak expanded mode; - first-class access-review and governance records; - a dedicated agent consumption product (projections exist); -- extracting a generic profile engine. +- extracting a generic profile engine; +- observing production, or actuating containment — estate-wide zeros, + not this repo's gaps to close. -Those remain INTENT, not a hole in SCOPE. +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. diff --git a/history/2026-08-29-security-layer-scope-intent-assessment.md b/history/2026-08-29-security-layer-scope-intent-assessment.md new file mode 100644 index 0000000..2719f38 --- /dev/null +++ b/history/2026-08-29-security-layer-scope-intent-assessment.md @@ -0,0 +1,317 @@ +# Security layer model — scope vs intent vs implementation + +**Date:** 2026-08-29 +**Repository:** user-engine +**Audience:** user-engine, gate-house, flex-auth, ops-warden +**Status:** current-state review, not a decision record +**Sources:** + +- `net-kingdom/SECURITY-COMPANION.md` v0.2 (operative form; start here) +- `net-kingdom/canon/standards/security-layer-model_v0.7.md` (accepted statute) +- `net-kingdom/canon/standards/user-engine-boundary-contract_v0.1.md` +- `INTENT.md` and `SCOPE.md` as adapted this session +- `intakes/intakes.md` `USER-IN-0001` +- gate-house broadcast 2026-08-29 (accepted v0.7; nine undeclared repositories) +- working references: `ops-warden/layer.yaml`, `ops-warden/pep-stance.yaml`, + `kings-guard/layer.yaml` +- implementation: `src/user_engine/service.py`, + `src/user_engine/adapters/flex_auth.py`, + `src/user_engine/adapters/local.py`, + `src/user_engine/domain/models.py`, + `src/user_engine/runtime.py`, + `src/user_engine/projections.py` + +The statute governs where the companion and the statute disagree. No +disagreement was found that this review needed to work around. + +--- + +## 1. Executive assessment + +user-engine is already the product INTENT described before the layer +model: a headless user-domain service with ports, a PostgreSQL store, a +portal, and a live flex-auth caller. That slice is finished through +`USER-WP-0023`. + +The accepted layer model asks for something the product workplans never +carried: an own-voice Engine/PIP declaration, a machine-readable +conformance surface, and the PEP obligations that attach to any runtime +that causes a protected side effect. + +Until this session the repository carried only a gate-house review note +on `INTENT.md` that named a layer and admitted the body was unadapted. +That is not a declaration (companion §2, statute §11). `USER-IN-0001` +stayed open. + +This review records the declaration, the SCOPE restatement, and the +implementation gaps that must move. It does not contest Engine/PIP. + +--- + +## 2. What we declare, and what we do not contest + +| Item | Position | +| --- | --- | +| Layer | **Engine** | +| Role | **PIP** | +| Catalog owns | users, accounts, memberships | +| Boundary contract | unchanged | +| One decision point | `access-engine` / `flex-auth`; we supply claims and enforce results | +| Contest the proposed layer? | **No.** Same inputs, same user-domain result. Not inference, not Tooling, not the PDP. | + +**Finding, not a contest.** Statute §4 catalogues user-engine as Engine/PIP +and does not mark it PEP-shaped. Companion §5 and statute §6.4 say a PEP +is any runtime that causes a protected side effect; being PEP-shaped does +not change layer. user-engine's production mutations (account lifecycle, +memberships, invitations, registration, hat selection, catalog and +profile writes) are protected side effects gated on flex-auth. We are +PEP-shaped in fact. §13.1 currently inventories `ops-warden` (published) +and `ops-mason` (missing). user-engine is a third PEP-shaped consumer +with no row. One row was already the finding; a silent third is worse. + +Raised for gate-house to inventory. We do not ask to be recatalogued as +Staff. + +--- + +## 3. Intent vs scope after the adaptation + +INTENT is now the layer declaration plus the previous product aspiration. +SCOPE is the implemented product plus the layer obligations that are now +in-scope because INTENT adopted them. + +| Area | INTENT | SCOPE after this session | Implementation | +| --- | --- | --- | --- | +| Engine / PIP declaration | Required, own voice, frontmatter | Declared; points at companion and statute | Frontmatter present. No `layer.yaml`. | +| Users, accounts, memberships | Own them | Own them | Shipped. | +| Claims, not decisions | Subject context is a PIP input | Explicit; local double excluded from production | Production asks flex-auth. Hats/facts not yet proven under §6.1. Local port can still decide. | +| PEP stance map | Published, tested equal to code, inventoried in §13.1 | Named as remaining product scope | Fail-closed in `FlexAuthHTTPAdapter`; unpublished. | +| Allow lifetime | Every allow has a lifetime | Named as missing | `AuthorizationDecision` has effect, id, reason, obligations. No TTL or binding. | +| Evidence bound | No completeness claim; cadence for load-bearing | Named as partial | Local outbox is transactional with mutations. No classification, no cadence. | +| Tooling contacts | No OpenBao or key-cape admin client | Out of scope | Held. Env-injected secrets. Unused `SecretProvider` port. | +| Observation / actuation | Do not assume either exists | Out of scope | Not implemented. Correct. | + +SCOPE is not silently claiming INTENT's new layer obligations as done. +The remaining holes are listed in SCOPE "Current Status" and mapped to +`USER-WP-0024`. + +--- + +## 4. Implementation assessment against the companion + +Companion sections are the working checklist. Statute citations in +parentheses. + +### 4.1 Declare your layer (companion §2 / statute §11) + +**Before this session:** undeclared. Gate-house prose in `INTENT.md` is +explicitly not a declaration. + +**After this session:** `INTENT.md` frontmatter `layer: Engine`, +`role: PIP`, plus own-voice body. That is the form companion §2 asks +for. + +**Still missing:** a machine-readable declaration file equivalent to +`ops-warden/layer.yaml`. Statute §11 allows frontmatter *or* an +equivalent file; the working references use a file because that is what +a conformance script can fail a commit against. user-engine has no +checker, so a new OpenBao import would not fail the build. Necessity: +add `layer.yaml` and a check adapted from kings-guard (Engine, not Staff; +expect no Tooling client, record non-Tooling clients). + +### 4.2 Rules that bind everyone (companion §3 / statute §6, §8, §9.7) + +| Rule | Shipped behaviour | Gap | +| --- | --- | --- | +| One decision point | Production `FlexAuthHTTPAdapter` POSTs `/v1/check`. Service `_authorize` denies on non-allow. | `LocalAuthorizationCheckPort` *is* a local PDP (default ALLOW, reason `"local"`). Runtime assembly uses flex-auth only, but nothing prevents a production constructor from passing the local port. | +| Compiled data still deciding | Access-control facts and hats are documented as claim templates (`docs/hats-realms-services-assets-access-profiles.md`). Projections cache profile material only (`projections.py`). | No test that a consumer cannot treat `AccessControlFact` or an active hat as an allow. Tenant capability roles are forwarded, not evaluated — held, documented in the architecture blueprint. | +| Doctrine as a claim | We do not apply zone stance or maturity ourselves. | None for doctrine we author — we author none. | +| Adaptive systems only tighten | We do not raise authority on missing evidence. | Held. Do not plan around actuation; it is at zero. | +| Staff never touches Tooling | We are Engine; §5 does not bind this shape the same way. PostgreSQL is the modeled-concept store, not a §4 Tooling row. | Record PostgreSQL, State Hub, and env-injected secrets as non-Tooling / own-store in `layer.yaml` so the check is total. | +| Every allow has a lifetime | Not modelled. | `AuthorizationDecision` must carry a lifetime (TTL or session/obligation binding). Replay of a stored verdict outside that lifetime is a second decision point. Today we do not cache verdicts, which is the safe subset, but we also cannot state a deadline (statute §9.7.2: a PEP MUST state one). | + +### 4.3 If you touch Tooling (companion §4 / statute §5) + +Catalogued Tooling is `key-cape` and `OpenBao`. + +| Contact | Present? | Shape | +| --- | --- | --- | +| OpenBao HTTP/CLI | No | Blocked-clean on a secrets-engine signing/read surface. Runtime secrets come from the environment / projected files (`runtime.py`). | +| key-cape admin / LDAP | No | Consumes verified IAM Profile claims (OIDC/JWKS). That is Engine input, not a Staff Tooling client. | +| PostgreSQL | Yes — own store | Modeled-concept persistence for a PIP. Not §4 Tooling. List it in the declaration so the check is total. | +| State Hub | Agents writing progress, not the service | Not catalogued. List as non-Tooling. Carve-out sunsets; we do not become a second state plane. | + +No §5.1/§5.2/§5.3 Staff contact exists. Conformance state for Tooling +contact is **conforming** (Engine with no catalogued Tooling client), +provided we declare the own-store and non-Tooling clients rather than +leave them silent. + +`SecretProvider` in `ports.py` has no implementation. Leave it as a port. +Do not add an OpenBao adapter to close it. + +### 4.4 If you cause side effects (companion §5 / statute §6.4) + +user-engine is PEP-shaped. Four obligations: + +1. **No side effect without a decision record, or a recorded stance.** + Mutations go through `_authorize` then `_record_mutation` inside + `store.transaction()`. On flex-auth transport/parse failure, + `FlexAuthHTTPAdapter` returns `DENY` with reason `"authorization + service unavailable"` and a **locally minted** `decision_id` + (`AuthorizationDecision.decision_id` default factory). `_authorize` + then writes that id onto the deny audit record. That is a fake + decision id, not a recorded stance application. Statute v0.7 + obligation 1 is explicit: where the engine is unreachable, record + the application of the stance *in place of* the decision. + `decision_id` must be absent unless a decision was rendered. + +2. **Do not replay a verdict outside its binding and lifetime.** No + verdict cache exists in the flex-auth adapter. Good. Negative + caching is not used. Keep it that way unless a cache is declared + with lifetime. The mechanical replay test (canonical request digest + + lifetime) is not implemented because we do not replay; still need + a lifetime on the decision we *hold for the mutation*. + +3. **Publish the unreachable-engine stance map.** Shipped behaviour is + fail-closed for every call — no per-zone map, no published file, no + test that file equals code. `runtime.py` comment says "Production + authorization and delivery are fail-closed HTTP boundaries." That is + a comment, which statute §6.4 forbids as the form of a stance. + Reference shape: `ops-warden/pep-stance.yaml`. user-engine's map can + be simpler (one protected-system, fail-closed total, including + unknown) but it must be total, published, and tested. + +4. **Stay reconstructable**, bounded by evidence (below). Deny audits + are written outside the mutation transaction, which is correct + (there is no mutation). They currently look like engine decisions. + +### 4.5 Evidence (companion §6 / statute §9.6) + +| Kind | What we have | Classification | +| --- | --- | --- | +| Successful user-domain mutation | `append_audit` + `append_outbox` inside the same `store.transaction()` as the save | Accidental omission of the *local* record is prevented. Drain to `audit-core` is after commit (local outbox pattern). | +| Authorization denial | `append_audit` in `_authorize`, not transactional with a mutation | Load-bearing if any control treats absence of a deny as "it was allowed". Today nothing else branches on that record — attributive in practice, load-bearing in intent (denials are the class the statute names). | +| Membership removal / account disable | Mutation path with outbox | Load-bearing for access-review and for PIP consumers who cache facts. | + +What we may not claim, and currently do not claim in code: + +- the audit record proves it happened; +- there is no record, so it did not happen. + +Docs (`docs/evidence-gap-examples.md`) talk about missing *external* +review references, not about this bound. Completeness of the local trail +is easy to over-claim in portal copy and operability docs. Necessity: +classify events, declare attributive vs load-bearing, declare a cadence +or heartbeat for denials and revocations (rate monitoring will not work +at this volume), and never describe the trail as complete. + +No production observation exists to cite (companion §10). This review +does not. + +### 4.6 Agents (companion §7 / statute §3.4) + +The service is an Engine. Agents *working the repository* are Staff +principals. Credential routing is already inlined in `AGENTS.md` via +`warden route`. No standing user-engine credential is vended to agents. +No change to runtime is required; INTENT now states the four agent +rules so a future convenience (a long-lived portal token in an agent +session, treating tool availability as permission) fails the +declaration. + +### 4.7 Conformance state (companion §8 / statute §11) + +| Question | State | +| --- | --- | +| Tooling contact | **Conforming** — none to catalogued Tooling, once listed | +| Capability we refuse to take locally (secrets-engine API, actuation, observation) | **Blocked-clean** — not worse than conforming | +| PEP stance unpublished; fake decision ids | **Undeclared violation** of §6.4 until the map and recording ship | +| Layer declaration | **In progress** — own-voice frontmatter done; machine-readable file and checker not done | + +Blocked-clean on actuation and observation is the correct posture. +Do not open a kings-guard or OpenBao client to look more complete. + +### 4.8 Railiance workloads (companion §9) + +`rapp-user-engine` is the managed package. Decisions come from +flex-auth; credentials from the platform after deploy-time injection; +evidence toward `audit-core` via the outbox. Axis-to-layer mapping is +unsettled (statute §20.3); this review does not invent one. + +### 4.9 Estate zeros (companion §10) + +Do not cite "observed in operation." Do not plan automatic containment, +step-up, or isolation as user-engine work. + +--- + +## 5. Necessities for the current implementation + +Ranked by whether the accepted standard is already violated in shipped +behaviour, versus missing declaration machinery. + +### Must change (shipped behaviour vs v0.7) + +1. **Stop minting a local `decision_id` on engine-unavailable DENY.** + Record stance application (zone or equivalent scope, failure mode, + outcome). `decision_id` present only when flex-auth rendered one. +2. **Publish `pep-stance.yaml`** (or equivalent) named from the layer + declaration. Total, no implicit default. Test: published map equals + `FlexAuthHTTPAdapter` behaviour. Ask gate-house to add the row to + §13.1. +3. **Give `AuthorizationDecision` a lifetime** and refuse to treat an + allow with no end as a standing grant. Even without a verdict cache, + the PEP must state one deadline. + +### Must add (declaration and checkability) + +4. **`layer.yaml`** plus a conformance script/test adapted from + kings-guard: Engine/PIP, empty `tooling_contacts`, listed + non-Tooling clients (Postgres own-store, State Hub, env secrets, + flex-auth Engine API, key-cape OIDC as claim input). +5. **Confine `LocalAuthorizationCheckPort`** to tests and explicitly + standalone fixtures. Production `create_application()` already uses + flex-auth; make construction of a default-allow adapter in the + runtime path fail. + +### Should add (evidence and PIP contract) + +6. **Classify local audit/outbox events** as load-bearing or + attributive. Declare a heartbeat or reconciliation for denials and + membership/account revocations. Fix docs language so the trail is + never "complete." +7. **Prove hats / `AccessControlFact` are claims.** A test that + selecting a hat or exporting facts never returns an allow/deny, and + a consumer contract sentence in the hats doc and the boundary-facing + docs: compiling those facts into a local decision is §6.1. + +### Do not do + +- Do not add an OpenBao or key-cape admin client. +- Do not cache flex-auth allows. +- Do not observe production or build containment. +- Do not treat `USER-IN-0001` as closed by the old gate-house note; + it closes when this repository's own files declare, which they now + do for INTENT, with implementation follow-through on `USER-WP-0024`. + +--- + +## 6. Mapping to work + +| Necessity | Workplan task | +| --- | --- | +| Own-voice INTENT + SCOPE + this review | `USER-WP-0024-T01` (done this session) | +| `layer.yaml` + conformance check | `USER-WP-0024-T02` | +| PEP stance map, stance recording, lifetime, local-port confinement | `USER-WP-0024-T03` | +| Evidence classification and cadence | `USER-WP-0024-T04` | +| Access-control facts as claims | `USER-WP-0024-T05` | +| Intake close and §13.1 registration request | `USER-WP-0024-T06` | + +--- + +## 7. Adoption note for gate-house + +user-engine answers `USER-IN-0001`: **Engine, role PIP**, in our own +voice, in `INTENT.md`. We do not contest the proposed layer. We raise +that we are PEP-shaped for our own mutations and are absent from +statute §13.1. Findings against the accepted text remain welcome; this +review is one. diff --git a/intakes/intakes.md b/intakes/intakes.md index 47a3db3..2b9d58b 100644 --- a/intakes/intakes.md +++ b/intakes/intakes.md @@ -7,7 +7,7 @@ id: USER-IN-0001 kind: intake title: 'Declaration requested: state this repository''s layer in INTENT.md (security layer model §11)' -status: open +status: answered origin: cross-repo origin_ref: net-kingdom security-layer-model_v0.4 §11 priority: low @@ -32,5 +32,12 @@ description: 'A conformance sweep on 2026-08-28 found this repository has no lay for what this repository actually does, that is more useful to us than a label added to close a checkbox. Standard: net-kingdom/canon/standards/security-layer-model_v0.4.md.' created: '2026-08-28T21:02:12.760565Z' -updated: '2026-08-28T21:02:12.760565Z' +updated: '2026-08-29' +answered: '2026-08-29' +answer: >- + Own-voice declaration in INTENT.md: layer Engine, role PIP. Subject + context is a claim, never a decision. Protected mutations are PEP-shaped + without changing layer. Not contested. Runtime follow-through is + USER-WP-0024. Assessment: + history/2026-08-29-security-layer-scope-intent-assessment.md. ``` diff --git a/workplans/USER-WP-0024-security-layer-conformance.md b/workplans/USER-WP-0024-security-layer-conformance.md new file mode 100644 index 0000000..807d541 --- /dev/null +++ b/workplans/USER-WP-0024-security-layer-conformance.md @@ -0,0 +1,152 @@ +--- +id: USER-WP-0024 +type: workplan +title: "Security layer model conformance for Engine/PIP" +domain: communication +repo: user-engine +status: active +owner: grok +topic_slug: netkingdom +created: "2026-08-29" +updated: "2026-08-29" +depends_on: + - USER-WP-0023 +--- + +# USER-WP-0024 — security layer model conformance + +Adopt the accepted NetKingdom Security Layer Model +(`net-kingdom/canon/standards/security-layer-model_v0.7.md`, companion +`net-kingdom/SECURITY-COMPANION.md`) as this repository's own declaration +and close the implementation gaps that leave a PEP-shaped Engine short of +the standard. + +Assessment: +`history/2026-08-29-security-layer-scope-intent-assessment.md`. + +Working references: `ops-warden/layer.yaml` and +`kings-guard/layer.yaml` (no Tooling contacts). Credential and lane +questions go to `ops-warden`, not this workplan. + +## T01 — Declare Engine/PIP in own voice + +```task +id: USER-WP-0024-T01 +status: done +priority: high +``` + +Replace the gate-house review note in `INTENT.md` with this repository's +own declaration: frontmatter `layer: Engine`, `role: PIP`, plus prose that +subject context is a claim, never a decision, and that protected +mutations are PEP-shaped without changing layer. Restate `SCOPE.md` from +that INTENT. Record the scope-vs-intent-vs-implementation assessment +under `history/`. + +Done 2026-08-29. Closes the declaration half of `USER-IN-0001`. Does not +by itself make the runtime conforming. + +## T02 — Machine-readable layer declaration and check + +```task +id: USER-WP-0024-T02 +status: todo +priority: high +``` + +Add `layer.yaml` (or equivalent) naming Engine/PIP, empty catalogued +Tooling contacts, non-Tooling / own-store clients (PostgreSQL modeled +concept, State Hub, env-injected secrets, flex-auth Engine API, OIDC +claim input), and the path of the PEP stance map. Adapt +`kings-guard/scripts/check_layer_conformance.py` and a unit test so an +undeclared OpenBao, Vault, LDAP, or cluster client fails the commit that +introduces it. + +Do not add a Tooling client to have something to declare. + +**Done when:** `layer.yaml` is the machine-readable declaration, the +checker exits 0 on the current tree, and a test covers the empty +Tooling-contact claim. + +## T03 — PEP obligations on the production path + +```task +id: USER-WP-0024-T03 +status: todo +priority: high +``` + +Ship the four §6.4 obligations for this PEP-shaped Engine: + +1. Publish `pep-stance.yaml` (total, no implicit default). Production + remains fail-closed, including unknown. A test asserts the file equals + `FlexAuthHTTPAdapter` behaviour. +2. On engine-unavailable DENY, record stance application (scope, failure + mode, outcome). `decision_id` is present only when flex-auth rendered + a decision. Stop minting a local id via `AuthorizationDecision`'s + default factory on that path. +3. Give `AuthorizationDecision` an explicit lifetime (TTL or + session/obligation binding). Do not add a verdict cache. +4. Confine `LocalAuthorizationCheckPort` to tests and standalone + fixtures. Production `create_application()` must be unable to + construct a default-allow adapter. + +**Done when:** the published map matches shipped fail-closed behaviour, +unavailable DENY audits carry no engine decision id, allows carry a +lifetime, and the local port cannot enter the production runtime +assembly. + +## T04 — Evidence bound and cadence + +```task +id: USER-WP-0024-T04 +status: todo +priority: medium +``` + +Classify local audit and outbox events as load-bearing or attributive. +Keep mutation+audit+outbox atomic in the store transaction (already +true). For denials and membership/account revocations — low-volume +load-bearing classes — declare a heartbeat or reconciliation, not a +rate. Update `docs/evidence-gap-examples.md` and operability copy so the +trail is never described as complete, and so absence of a record is not +read as non-occurrence. + +**Done when:** the classification is in-repo, the heartbeat or +reconciliation form is named, and docs use the sound evidence sentences. + +## T05 — Access-control facts remain claims + +```task +id: USER-WP-0024-T05 +status: todo +priority: medium +``` + +Prove under statute §6.1 that hats, access profiles, and +`AccessControlFact` exports cannot be mistaken for an authorization +decision. Add a test that selection and export never return allow/deny. +Document the consumer contract: compiling those facts into a local +decision is an early decision and is forbidden. + +**Done when:** a test and the hats/boundary docs state the claim +contract, and no user-engine API on that path exposes an effect. + +## T06 — Close the intake and request the stance-map row + +```task +id: USER-WP-0024-T06 +status: done +priority: medium +``` + +Mark `USER-IN-0001` answered: Engine/PIP, not contested. Message +`gate-house` with the declaration path, the PEP-shaped finding for +statute §13.1 (user-engine is a third PEP-shaped consumer; the register +currently has one published row), and a pointer at this workplan for the +runtime follow-through. + +Done 2026-08-29. Intake marked answered in `intakes/intakes.md`. Message +`c014d12a-fe7a-4a90-ba73-03a5ac3e7f73` to `gate-house` carries the +declaration, the non-contest, and the §13.1 request (row to be added +when `pep-stance.yaml` ships in T03).