diff --git a/INTENT.md b/INTENT.md index c25e0ff..b3d4165 100644 --- a/INTENT.md +++ b/INTENT.md @@ -1,94 +1,24 @@ ---- -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" ---- - # INTENT +> **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.* + > 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 @@ -97,11 +27,6 @@ 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 @@ -120,16 +45,13 @@ 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 Engine that exposes canonical identity, user, account, +layer: a headless service that exposes canonical identity, user, account, membership, tenant, team, profile, lifecycle, and evidence-facing concepts to -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. +applications 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, @@ -148,9 +70,6 @@ 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, @@ -166,11 +85,6 @@ 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; @@ -182,9 +96,7 @@ point by compilation or by local evaluation. 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; -- `access-engine`, as a PIP consumer of subject, membership, and - access-control facts. + on technical IAM implementation details. ## Product Boundaries @@ -195,19 +107,15 @@ 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, including its own; +- the policy decision point for protected resources; - the owner of NetKingdom security controls or runtime secrets; - the full organization, HR, or directory authority; - a directory server; -- a UI application; -- an observer of production security events; -- an actuation surface for containment, step-up, or authority reduction. +- a UI application. It provides the user-domain and identity-domain APIs, mapping records, catalog -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. +metadata, projections, events, evidence references, and audit records that those +surrounding systems can consume. ## Design Principles @@ -216,17 +124,14 @@ same APIs, not a product identity. - standalone-friendly; - enterprise-integratable; - identity-provider agnostic; -- authorization-engine agnostic — and never a substitute for one; +- authorization-engine agnostic; - 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, 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; +- evidence and lifecycle awareness for identity-domain changes; - concrete user-domain focus with a possible future extraction path toward a generic profile engine. @@ -242,9 +147,3 @@ 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 29cd8bd..6799377 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -1,25 +1,11 @@ # 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 Engine (PIP) for accounts, identity -links, memberships, catalogs, projections, local audit, and events, with an +Headless user-domain and identity-domain service for accounts, identity +links, memberships, catalogs, projections, audit, and events, with an optional in-repo portal. It consumes NetKingdom IAM, authorization, tenant -authority, provisioning, and delivery; it does not own them and it does not -decide them. +authority, provisioning, and delivery; it does not own them. ## In Scope @@ -31,10 +17,8 @@ decide them. preferences; - tenant, application, team, and scope memberships; - hats, realms, services, assets, access profiles, and active access - context as user-domain facts and claim templates; + context as user-domain facts; - 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; @@ -51,22 +35,17 @@ decide 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 of the - modeled concept; +- standalone/local fixtures and a production PostgreSQL store; - integration ports for claims, flex-auth decisions (including a rotating caller token), provisioning, registration verification, tenant - management, outbox delivery, and runtime secrets; -- PEP enforcement of `access-engine` decisions on user-engine-owned - mutations, including a published unreachable-engine stance. + management, outbox delivery, and runtime secrets. ## 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` / `access-engine`; -- caching or compiling an authorization decision, including from hats, - access profiles, or memberships; + `flex-auth`; - durable authorization grants beyond user-engine-owned memberships; - tenant identifier authority, grouping reclassification, and capability role grants — `tenant-engine`; @@ -74,22 +53,16 @@ decide 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; no OpenBao client here; -- a Keycloak or key-cape admin client; +- runtime secret custody — OpenBao / Railiance; - 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; -- 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. +- a generic extracted profile engine. The in-repo portal is an optional surface, not a UI product. Password and -MFA screens stay on the identity provider. `LocalAuthorizationCheckPort` -is a test and standalone double, not a production decision point. +MFA screens stay on the identity provider. ## Boundary Rule @@ -100,13 +73,6 @@ 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/ @@ -114,23 +80,15 @@ 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. 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. +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. Still operator-owned, not remaining product scope: @@ -142,25 +100,10 @@ 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 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 is the stable aspiration. Against it, the repo now does the job it +set out to do. | INTENT aim | Status | | --- | --- | @@ -169,17 +112,10 @@ 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. | | 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: @@ -187,10 +123,6 @@ 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; -- observing production, or actuating containment — estate-wide zeros, - not this repo's gaps to close. +- extracting a generic profile engine. -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. diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index b25d5f3..43d9e26 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -169,4 +169,3 @@ | task | USER-WP-0023-T01 | done | — | workplans/USER-WP-0023-flex-auth-caller-identity.md | | task | USER-WP-0023-T02 | done | — | workplans/USER-WP-0023-flex-auth-caller-identity.md | | task | USER-WP-0023-T03 | done | — | workplans/USER-WP-0023-flex-auth-caller-identity.md | -| intake | USER-IN-0001 | open | — | intakes/intakes.md | diff --git a/history/2026-08-29-security-layer-scope-intent-assessment.md b/history/2026-08-29-security-layer-scope-intent-assessment.md deleted file mode 100644 index 2719f38..0000000 --- a/history/2026-08-29-security-layer-scope-intent-assessment.md +++ /dev/null @@ -1,317 +0,0 @@ -# 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 2b9d58b..47a3db3 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: answered +status: open origin: cross-repo origin_ref: net-kingdom security-layer-model_v0.4 §11 priority: low @@ -32,12 +32,5 @@ 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-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. +updated: '2026-08-28T21:02:12.760565Z' ``` diff --git a/workplans/USER-WP-0024-security-layer-conformance.md b/workplans/USER-WP-0024-security-layer-conformance.md deleted file mode 100644 index 807d541..0000000 --- a/workplans/USER-WP-0024-security-layer-conformance.md +++ /dev/null @@ -1,152 +0,0 @@ ---- -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).