From 0e2efa8fcf4afb0aea25b1cb93dfe4a5ab177d09 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 29 Aug 2026 14:43:49 +0200 Subject: [PATCH] Align INTENT and SCOPE to security layer model v0.7; plan conformance work The standard was accepted at v0.7 on 2026-08-29. Four of flex-auth's review findings are in the accepted text: 9.3's two-owner split, 6.4.2 scoped to the decision's own binding with our canonical request digest as its mechanical test, 9.7.2 split by role, and 17 moving the decision-record schema to access-engine. INTENT.md - Machine-readable layer declaration in frontmatter (layer: Engine, role: PDP), which section 11 requires and we did not have. audit-core noted our declaration was legible only by following the decision trail. - PDP failure semantics stated: our outage is consumer residue, not input degradation; fail-open is not expressible by a PDP at all. - Four owned obligations added: the decision-record schema as our contract, the request digest as the published replay test, a lifetime on every allow, and visibility deadlines per input class. - A Layer Conformance section stating the state honestly: conforming with one declared gap, no Tooling client, not PEP-shaped. - Vocabulary correction: earlier text dropped "control plane" as Staff vocabulary. Section 8 binds it to the Engine layer, which is why kings-guard was asked to release it. The term is ours; we prefer "decision engine" for precision, not boundary. SCOPE.md - Layer and role in the one-liner; the four obligations In Scope; five boundaries established in review but never written down Out of Scope. - Three capability blocks marked planned for workplans completed in May are now current; two blocks added. - Superseded ADR-0006 citation corrected to ADR-0009, which retires the global flag outright rather than deferring it. history/2026-08-29-layer-model-v0.7-alignment-review.md checks each obligation against the code and finds six gaps. FLEX-WP-0019 closes them, with T02 before T04 because a visibility deadline for registry-borne facts is unfalsifiable until provenance can identify the snapshot a decision read. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_012sgN4GH5ZYT8pJVkCR6dcP Assistant: claude-code Assistant-Model: opus Assistant-Process: 4014348@bnt-lap001 Assistant-Session: a993abda-65a0-4ea8-8ccd-0fcd78c92ac0 --- .repo-manager/index.json | 71 +++++++-- INTENT.md | 135 ++++++++++++----- SCOPE.md | 70 +++++++-- WORK-RECORDS.md | 1 + ...08-29-layer-model-v0.7-alignment-review.md | 136 +++++++++++++++++ .../FLEX-WP-0019-layer-model-conformance.md | 143 ++++++++++++++++++ 6 files changed, 492 insertions(+), 64 deletions(-) create mode 100644 history/2026-08-29-layer-model-v0.7-alignment-review.md create mode 100644 workplans/FLEX-WP-0019-layer-model-conformance.md diff --git a/.repo-manager/index.json b/.repo-manager/index.json index 1375fef..168a277 100644 --- a/.repo-manager/index.json +++ b/.repo-manager/index.json @@ -2,9 +2,9 @@ "schema": "repo_manager.index.v1", "slug": "flex-auth", "repo_root": "/home/worsch/flex-auth", - "head_sha": "54aae6b6b365407c102c511e30c392b459423be2", - "observed_at": "2026-08-29T00:42:15.556704Z", - "source_fingerprint": "3cadc015fd19f01f7381a51c7d7b0aa461f875bfe59253be427e9d5d8f373795", + "head_sha": "5753b47ccb72f96689df9ec5eeb7be100f6817cd", + "observed_at": "2026-08-29T08:19:42.754823Z", + "source_fingerprint": "2d3773fc38408c64d1a9a4efc7335ed792f361cdf6464f04389615c9b443fa53", "source_files": [ ".repo-classification.yaml", "INTENT.md", @@ -1071,7 +1071,7 @@ "status": "resolved", "title": "Review of security layer model v0.4: assent with findings, one rule contested", "source_path": "decisions/decisions.md", - "uuid": null, + "uuid": "2e96321d-c7bf-4127-9c30-f3def32e5bee", "parent_id": null, "extra": { "record": { @@ -1101,7 +1101,47 @@ "updated": "2026-08-29T00:42:13.009799Z", "rationale": "Assent to security-layer-model v0.4, with one rule contested and two capability assignments not accepted as assented. Section 9.3 conflicts with shipped assented behavior: it rules engine-unreachability fallback into the engine, where it cannot live, and collides with ops-warden ADR-0009's per-zone consumer PEP map. Section 13 names access-engine as intended owner of containment (accept as proposed owner only, pending per 9.2) and of authentication/assurance evidence (declined as stated; the identity layer and audit-core own that). Three consistency defects: frontmatter status proposed contradicts section 14 'accepted'; the adoption count reads seven of fifteen with remaining eight against sixteen estate-authored repositories and nine listed; section 14 says three repositories above a table of four. FLEX-IN-0002 answered: the approval boundary unblocks T03 design, T05 additionally needs the approval claim bound to the NewDecisionBinding request digest and a named owner and ordering for single consumption; the maturity claim route is practical as a request claim but not as registry content until the self-declared provenance digest gap closes.", "decided_by": "flex-auth (reviewing side)", - "decided_at": "2026-08-29T00:42:13.009799Z" + "decided_at": "2026-08-29T00:42:13.009799Z", + "state_hub_decision_id": "2e96321d-c7bf-4127-9c30-f3def32e5bee" + } + } + }, + { + "kind": "decision", + "id": "FLEX-DEC-2026-003", + "status": "resolved", + "title": "Review of security layer model v0.6 and companion v0.1: assent, two answers, five findings", + "source_path": "decisions/decisions.md", + "uuid": null, + "parent_id": null, + "extra": { + "record": { + "id": "FLEX-DEC-2026-003", + "kind": "decision", + "title": "Review of security layer model v0.6 and companion v0.1: assent, two answers, five findings", + "status": "resolved", + "origin": "cross-repo", + "origin_ref": "net-kingdom security-layer-model_v0.6 + companion_v0.1", + "standard": "net-kingdom/canon/standards/security-layer-model_v0.6.md", + "owner": "flex-auth", + "affects": [ + "flex-auth", + "gate-house", + "net-kingdom", + "info-tech-canon", + "ops-warden", + "approval-engine" + ], + "requested_dispositions": [ + "assent", + "revise", + "reject" + ], + "created": "2026-08-29T08:18:39.602701Z", + "updated": "2026-08-29T08:19:41.832549Z", + "rationale": "Assent to v0.6 and companion v0.1, with two answers and five findings, none blocking. Q1: section 6.4.2 is right but collides with 9.7.1's session-bound allow, needs the request digest as its mechanical replay test, and should rule explicitly on deny-caching. Q2: the visibility deadline does land on a PDP and harder than at a PEP, but must be per input class rather than one number, and it makes flex-auth's registry-provenance gap load-bearing rather than untidy. Findings: 6.4's stance-map register does not exist in section 13 and neither document says where a map is published; the companion omits it too, which is the sufficiency gap gate-house asked for; section 17 puts the decision-record schema in Taxonomy when it is the PDP's output artifact, inverting the section 2 rule flex-auth used to decline authentication evidence; sections 17-19 are H1 outside the hierarchy; section 19 grades the document it lives in and will age.", + "decided_by": "flex-auth (reviewing side)", + "decided_at": "2026-08-29T08:19:41.832549Z" } } }, @@ -1147,7 +1187,7 @@ "status": "closed", "title": "Review requested: security layer model v0.3 (approval-engine, maturity-engine)", "source_path": "intakes/intakes.md", - "uuid": null, + "uuid": "01a04afa-307b-7e9e-b210-261badabcec7", "parent_id": null, "extra": { "record": { @@ -1171,7 +1211,8 @@ } ], "closed_at": "2026-08-29T00:42:14.888434Z", - "outcome": "assented with findings \u2014 see FLEX-DEC-2026-002" + "outcome": "assented with findings \u2014 see FLEX-DEC-2026-002", + "state_hub_intake_id": "01a04afa-307b-7e9e-b210-261badabcec7" } } } @@ -1179,17 +1220,17 @@ "events": [ { "type": "repo.command.applied", - "command": "repo.work.close_intake", - "operation": "close", - "correlation_id": "1e832e3c-4e51-4dfd-919d-40593c18b2ab", - "kind": "intake", - "id": "FLEX-IN-0002", - "git_sha": "54aae6b6b365407c102c511e30c392b459423be2", + "command": "repo.work.resolve_decision", + "operation": "resolve", + "correlation_id": "fe430c1e-83bb-4dba-b6fc-337baa1a0059", + "kind": "decision", + "id": "FLEX-DEC-2026-003", + "git_sha": "5753b47ccb72f96689df9ec5eeb7be100f6817cd", "files_touched": [ - "intakes/intakes.md" + "decisions/decisions.md" ], "source": "repo-manager", - "emitted_at": "2026-08-29T00:42:15.556791Z" + "emitted_at": "2026-08-29T08:19:42.754925Z" } ] } diff --git a/INTENT.md b/INTENT.md index 3632c09..6ad79e0 100644 --- a/INTENT.md +++ b/INTENT.md @@ -1,45 +1,53 @@ +--- +# NetKingdom security layer declaration (security-layer-model_v0.7 §11). +# Machine-readable because §11 requires it: prose cannot distinguish a +# declaration from a transcribed review. Reference form: ops-warden layer.yaml. +layer: Engine +role: PDP +framework: netkingdom-security-layer-model +standard_version: "0.7" +declared_by: decisions/decisions.md FLEX-DEC-2026-001, FLEX-DEC-2026-002, FLEX-DEC-2026-003 +declared_at: "2026-08-29" +pep_stance: null # not PEP-shaped: flex-auth renders decisions, it causes no protected side effect +tooling_contacts: [] # §5 binds Staff; flex-auth holds no Tooling client +--- + # Flex-Auth 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. +> **Layer declaration.** flex-auth is **Engine / PDP** under the NetKingdom +> Security Layer Model (`net-kingdom/canon/standards/security-layer-model_v0.7.md`, +> accepted 2026-08-29). It is **the only policy decision point in NetKingdom** +> (§6): no other repository, in any layer, renders or caches an authorization +> decision. The frontmatter above is the machine-readable form §11 requires; this +> paragraph is the declaration in flex-auth's own voice. > -> The layer rule that binds every repository: **Staff never touches tooling -> directly. It acts only through engine APIs.** +> The layer rule that binds every repository: **Staff never touches Tooling +> directly. It acts only through Engine APIs.** flex-auth is one of those APIs. > -> **This repository is Engine — and the only policy decision point in NetKingdom.** -> Reframed on 2026-08-28 per `gate-house/decisions/decisions.md` GH-DEC-2026-001: -> "control plane" dropped as Staff vocabulary, the layer and the exclusive -> decision-point role stated, policy authoring separated from policy evaluation, -> gate-house's authority context adopted as input claims, and the access -> lane/rule demarcation recorded. +> **What PDP means here, and what it does not.** As the estate's only PDP, +> flex-auth's outage is *consumer residue*, not input degradation (§9.3): when +> flex-auth is unreachable there is no evaluator in the path, so what happens +> next is the consumer's declared stance and never flex-auth's to express. +> Fail-open is not expressible by a PDP at all. When flex-auth is reachable but +> cannot reach its own inputs, the fallback is flex-auth's, deterministic, and +> fails to reduced authority. The PIP engines — user, tenant, zone, approval, +> maturity — supply facts flex-auth consumes as claims; `audit-core` records; +> `secrets-engine` holds credential lifecycle downstream of a decision. > -> *Reframe applied. **Assent given on 2026-08-28** — `decisions/decisions.md` -> FLEX-DEC-2026-001, answering intake `FLEX-IN-0001`: flex-auth assents to the Engine -> framing, to `access-engine` as the ruled name, and to the authoring/evaluation -> split. One item remains: the rename itself, a separate governed migration — it -> touches `FLEX-WP` prefix ownership, State Hub identifiers, ops-warden's routing -> tables, zone-engine's binding boundary text, and secrets-engine integrations — -> and is not authorized by GH-DEC-2026-001. flex-auth adds two conditions on it -> (FLEX-DEC-2026-001 item 2): repository identity and runtime identity rename in +> flex-auth reached this position by review rather than assertion, and holds +> three records: `FLEX-DEC-2026-001` (Engine framing, the `access-engine` name, +> the authoring/evaluation split), `FLEX-DEC-2026-002` (§9.3 contested and +> upheld), and `FLEX-DEC-2026-003` (v0.6 review; §6.4.2, §9.7.2 and §17 as +> adopted in v0.7). +> +> *One item remains open: the ruled rename to `access-engine`. It is a separate +> governed migration — it touches `FLEX-WP` prefix ownership, State Hub +> identifiers, ops-warden's routing tables, zone-engine's boundary text, and +> secrets-engine integrations — and carries two conditions from +> `FLEX-DEC-2026-001`: repository identity and runtime identity rename in > separate revertible steps, repository first, because the enforcing ops-warden > pin binds tokens to the protected-system name; and `FLEX-WP` prefix ownership > stays with the repository.* -> -> **Known non-conformance — registry provenance.** Standard §6 holds that -> compiled data determining an outcome is still deciding, and that provenance -> must stay reconstructable from the decision. `DecisionProvenance` carries the -> evaluator, mode, policy package, policy version, and directory ETag, but no -> digest of the registry snapshot. A decision that turned on registry content -> cannot be replayed from its own provenance. flex-auth accepts this as its own -> gap rather than claiming conformance; until it is closed, outcome-determining -> content belongs in the versioned policy package, not the registry — for -> zone-engine's zone stance, for gate-house's authority ceilings, and for -> everyone else on the same terms. > This file captures **why this repository exists**, the **direction it is > moving toward**, and the **kind of system it is meant to become**. @@ -53,14 +61,24 @@ for organizations that want to grow from simple access rules into enterprise-grade authorization without giving up clear ownership, local development ergonomics, or inspectable policy decisions. -It is an **Engine** in the NetKingdom security layer model -(`net-kingdom/canon/standards/security-layer-model_v0.1.md`): a deterministic +It is an **Engine**, role **PDP**, in the NetKingdom security layer model +(`net-kingdom/canon/standards/security-layer-model_v0.7.md`): a deterministic API for a modeled concept, where the same authoritative input state yields the -same result. It is deliberately not described as a control plane — that is -Staff-layer vocabulary, and flex-auth is not Staff. +same result. Within NetKingdom it is **the only policy decision point** (standard §6). No other repository, in any layer, may render or cache an authorization decision. +Engine typing (§3.3) makes the corollary explicit: a new engine is a PIP unless +the standard is amended, so *"we need an engine for X"* can never become *"X now +decides"*. + +A note on vocabulary, corrected here. Earlier versions of this file dropped +"control plane" on the grounds that it was Staff-layer vocabulary. That reason +was wrong on the standard's own terms — §8 binds **control plane to the Engine +layer**, which is why kings-guard was asked to release it. The term is +flex-auth's to use. This file prefers **decision engine** anyway, because it +names what flex-auth does rather than where it sits, but the preference is +style, not a boundary. It is the **authorization layer** in the path from verified identity to protected resources: @@ -118,6 +136,19 @@ authenticated; flex-auth decides what that actor is allowed to do. - Relationship facts and inherited access. - PDP adapter coordination. - Decision logging, explanations, and audit export. +- **The decision-record schema**, published as flex-auth's own contract + (§17). A decision record is the PDP's output artifact — the one thing in the + estate only flex-auth produces — so §2 keeps its schema here rather than in + Taxonomy. flex-auth argued this against its own interest and took the work on. +- **The canonical request digest** over normalized subject, action, resource, + and context. It is the mechanical test for §6.4.2: a consumer may replay a + verdict iff the digest matches and the decision's lifetime holds. +- **A stated lifetime on every allow** (§9.7.1) — a TTL, or a binding to a + session or obligation that ends. An allow with no stated end is a standing + grant. +- **A revocation visibility deadline per input class** (§9.7.2) — approval-claim + freshness, registry snapshot cadence, policy package activation, directory + ETag. A single number at a PDP is either a fiction or the worst case. ### Gate House Owns the Doctrine the Decision Serves @@ -237,12 +268,12 @@ adopting their models as its own: - a local directory plus policy-evaluation engine for self-contained delegated setups -Flex-auth remains the stable control plane even when the backend changes. +Flex-auth remains the stable decision point even when the backend changes. ## Consumer Patterns Two consumer shapes drive flex-auth, and the first one to ship deliberately -is not a document pipeline — proving the control plane stays generic. +is not a document pipeline — proving the decision engine stays generic. **First shipped consumer — an action gate (ops-warden SSH signing).** A protected system asks flex-auth a single "may this actor perform this action @@ -273,6 +304,30 @@ Together these shape flex-auth around real authorization needs — both point-in-time action gates and result-filtering pipelines — without making the policy service consumer-specific. +## Layer Conformance + +flex-auth's conformance state under §11 is **conforming with one declared gap**. +It holds no Tooling client, so the §5 shapes do not apply to it, and it is not +PEP-shaped, so it owes no stance map under §6.4. + +**The declared gap — registry-snapshot digest in decision provenance (§13).** +`DecisionProvenance` carries the evaluator, mode, policy package, policy version, +directory ETag, and decision time, but no digest of the registry snapshot that +supplied resource, subject, and relationship facts. A decision that turned on +registry content cannot be replayed from its own provenance. + +v0.7 §9.7.2 promotes this from housekeeping to a **conformance prerequisite**, +on flex-auth's own argument: a stated visibility deadline for a fact carried by +a registry snapshot is unfalsifiable while provenance holds no snapshot digest, +because nobody can determine afterwards which snapshot a decision read. The +deadline and the digest are one gap seen from two sides. + +Until it closes, one rule holds and flex-auth applies it to everyone equally, +including itself: **outcome-determining content belongs in the versioned policy +package, not in registry content** — for zone stance, for gate-house's authority +ceilings, for maturity levels, and for flex-auth's own facts. Registry content +carries membership and identity; the policy package carries effect. + ## Non-Goals - Flex-auth is not an identity provider. diff --git a/SCOPE.md b/SCOPE.md index 02b9748..08a3c80 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -2,8 +2,10 @@ ## One-liner -flex-auth is a policy-as-code authorization registry and control plane for -NetKingdom-aligned systems. +flex-auth is a policy-as-code authorization registry and decision engine for +NetKingdom-aligned systems. Under the NetKingdom Security Layer Model +(`security-layer-model_v0.7`, accepted 2026-08-29) it is **Engine / PDP** — the +estate's only policy decision point. ## Core Idea @@ -33,6 +35,14 @@ can be coordinated behind a stable flex-auth API. - Markitect as the first concrete protected-system consumer. - Delegated PDP and directory adapter boundaries after the standalone core is stable. +- The **decision-record schema**, published as flex-auth's own contract + (`security-layer-model_v0.7` §17). +- The **canonical request digest** as the published replay test for consumers + (§6.4.2). +- **Decision lifetimes** on every allow, and **revocation visibility deadlines + stated per input class** (§9.7). +- **Registry-snapshot provenance**, so a decision that turned on registry + content is replayable from its own record (§6.1, §9.7.2). ## Out of Scope @@ -46,6 +56,18 @@ can be coordinated behind a stable flex-auth API. model. - Forcing a single PDP backend or directory provider. - Hiding policy behavior behind opaque administrative toggles. +- Expressing fail-open. When flex-auth is unreachable there is no evaluator in + the path; the residue is the consumer's declared stance (§9.3). +- Owning enforcement. A PEP's obligations under §6.4 are the consumer's, + including its published stance map. +- Owning the approval object, its storage, or its lifecycle — that is + `approval-engine`'s (§9.4). flex-auth validates approvals as input claims and + never mutates them. +- Owning evidence of authentication or assurance. flex-auth consumes assurance + claims and never redefines them; it owns evidence of the decision + (`FLEX-DEC-2026-002`, recorded as declined in §13). +- Authoring security doctrine. gate-house owns invariants, authority ceilings, + operating modes, and the authority context flex-auth consumes as claims. ## Relevant When @@ -105,13 +127,19 @@ ops-warden adopted a calling identity (`WARDEN-WP-0031`): anonymous `/v1/check` is 401, a token bound to another protected system is 403. Production `policy.enabled` is **deliberately off, and is not coming back as a -boolean.** ops-warden's `ADR-0006` (2026-08-19) defers it permanently: +boolean.** ops-warden's `ADR-0009` (2026-08-22, superseding `ADR-0006`) settles it: `policy.enabled` is one repo-wide flag, and with `fail_closed: true` it makes flex-auth a hard dependency of every `warden sign` — including the SSH certificates the ops-bridge tunnels depend on, one of which carries the policy -call itself. Enforcement is being rescoped to *security zones* (`zone-engine` -`ZONE-WP-0001`, consumer side `WARDEN-WP-0032`). flex-auth reviewed that model as -the PDP on 2026-08-19 and holds one boundary: zone **membership** compiles into +call itself. `ADR-0009` retires `policy.enabled` and the global `policy.fail_closed` +outright and replaces them with a total per-zone stance map in the consumer PEP +(`ops-warden/pep-stance.yaml`): open for `z0`-`z2` and `unknown`, closed for +`z3-critical`. That map is the reference shape for `security-layer-model_v0.7` +§6.4 obligation 3, and it is the estate's only published one — §13.1's register +has one row, which the standard records as itself the finding. + +flex-auth reviewed the zone model as the PDP on 2026-08-19 and holds one +boundary: zone **membership** compiles into the registry snapshot flex-auth already consumes (no schema change needed), while per-zone **stance** belongs in the versioned policy package, because registry content is absent from decision provenance. **Fail-open is not expressible by a @@ -119,6 +147,16 @@ PDP at all** and stays a consumer-side setting. The enforcing pins stay enforcing regardless — an enforcing pin with no enforcing consumer still turns the anonymous path into a 401 rather than a decision. +**Layer position (2026-08-29).** The NetKingdom Security Layer Model was +accepted at v0.7. flex-auth is **Engine / PDP**, declared machine-readably in +`INTENT.md` frontmatter per §11. Four of flex-auth's review findings were adopted +into the standard: §9.3's two-owner split for degraded mode, §6.4.2 scoped to the +decision's own binding with the canonical request digest as its mechanical test +and negative caching ruled explicitly, §9.7.2 split by role, and §17 moving the +decision-record schema to flex-auth. Conformance state is **conforming with one +declared gap** — the registry-snapshot digest in decision provenance, which §9.7.2 +promotes to a conformance prerequisite. `FLEX-WP-0019` carries the work. + State Hub integration is present through: - `.custodian-brief.md` @@ -212,20 +250,34 @@ description: Repo intent, boundaries, research, workplan map, and State Hub agen ```capability type: workplan title: Standalone policy core plan -status: planned +status: current description: Workplan for schemas, local registry, policy packages, check APIs, list/explain, decision log, CLI/service skeleton, tests, and examples. ``` ```capability type: workplan title: Markitect consumer integration plan -status: planned +status: current description: Workplan for Markitect resource namespace, manifest import, action vocabulary, check fixtures, contract tests, and integration flow. ``` ```capability type: workplan title: Delegated adapter plan -status: planned +status: current description: Workplan for Topaz evaluation, relationship PDP adapters, rule PDP adapters, Keycloak AuthZ path, directory resolvers, and operations docs. ``` + +```capability +type: contract +title: Decision-record contract +status: current +description: Published decision-record schema, canonical request digest as the replay test for consumers, and decision provenance — flex-auth's own contract under security-layer-model v0.7 section 17. +``` + +```capability +type: orientation +title: Layer declaration +status: current +description: Machine-readable Engine/PDP declaration in INTENT.md frontmatter with conformance state and the one declared gap, per security-layer-model v0.7 section 11. +``` diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 072cad0..910dac7 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -111,3 +111,4 @@ | intake | FLEX-IN-0002 | closed | — | intakes/intakes.md | | decision | FLEX-DEC-2026-001 | resolved | — | decisions/decisions.md | | decision | FLEX-DEC-2026-002 | resolved | — | decisions/decisions.md | +| decision | FLEX-DEC-2026-003 | resolved | — | decisions/decisions.md | diff --git a/history/2026-08-29-layer-model-v0.7-alignment-review.md b/history/2026-08-29-layer-model-v0.7-alignment-review.md new file mode 100644 index 0000000..b183691 --- /dev/null +++ b/history/2026-08-29-layer-model-v0.7-alignment-review.md @@ -0,0 +1,136 @@ +--- +id: flex-auth-2026-08-29-layer-model-alignment +type: history +title: "Layer model v0.7 alignment: INTENT, SCOPE, and the gap between them" +domain: infotech +repo: flex-auth +owner: flex-auth +created: "2026-08-29" +standard: net-kingdom/canon/standards/security-layer-model_v0.7.md +records: decisions/decisions.md FLEX-DEC-2026-001, FLEX-DEC-2026-002, FLEX-DEC-2026-003 +workplan: workplans/FLEX-WP-0019-layer-model-conformance.md +--- + +# Layer model v0.7 alignment review + +The NetKingdom Security Layer Model was accepted at **v0.7** on 2026-08-29. +`INTENT.md` and `SCOPE.md` were aligned to it in the same session. This document +records what changed, then asks the question the alignment exists to answer: +**where does the stated scope now outrun the implementation, and what has to be +built to close it.** + +## 1. How flex-auth arrived at v0.7 + +flex-auth reviewed three versions and holds three records. Four of its findings +became text in the accepted standard: + +| Finding | Adopted as | +| --- | --- | +| §9.3 collapsed two failures with different owners | §9.3 rewritten: input degradation is the engine's, engine-unreachability is necessarily the consumer's | +| §6.4.2 forbade the session-bound allow §9.7.1 permits | §6.4.2 scoped to the decision's own binding and lifetime, with flex-auth's canonical request digest as the mechanical test, and negative caching ruled permitted where the refusal is recorded | +| A PDP's visibility deadline is per input class, not one number | §9.7.2 split by role | +| The decision-record schema is the PDP's output, not Taxonomy's | §17 moves it to `access-engine` | + +The last of those was argued **against flex-auth's own interest** — it takes work +on rather than off — and the standard says so, because that is what makes the +§2 ownership rule credible when flex-auth also uses it to decline work. + +Two of flex-auth's positions were recorded rather than adopted: containment +surface ownership stays **proposed** (§13, pending under §9.2), and +authentication/assurance evidence is recorded as **declined**. + +## 2. What changed in INTENT.md + +1. **A machine-readable layer declaration** in frontmatter — `layer: Engine`, + `role: PDP` — which §11 requires and flex-auth did not have. `audit-core` + named this precisely: flex-auth's declaration was "legible as one only by + following its decision trail." Prose cannot distinguish a declaration from a + transcribed review. +2. **The PDP role stated with its failure semantics**: flex-auth's outage is + consumer residue, not input degradation. Fail-open is not expressible by a + PDP at all. +3. **Four obligations added to what flex-auth owns**: the decision-record + schema as its own contract, the canonical request digest as the published + replay test, a lifetime on every allow, and revocation visibility deadlines + per input class. +4. **A `Layer Conformance` section** stating the conformance state honestly: + conforming with one declared gap, no Tooling client, not PEP-shaped. +5. **A vocabulary correction.** Earlier versions dropped "control plane" as + Staff vocabulary. That reason was wrong on the standard's own terms — §8 + binds control plane to the **Engine layer**, which is why kings-guard was + asked to release it. The term is flex-auth's to use. This file prefers + "decision engine" as a matter of precision, not boundary. + +## 3. What changed in SCOPE.md + +The one-liner now states the layer and role. In Scope gained the four v0.7 +obligations. Out of Scope gained five boundaries flex-auth had established in +review but never written down: fail-open, enforcement, the approval object, +authentication evidence, and doctrine authoring. Three capability blocks were +still marked `planned` for workplans completed in May and are now `current`, and +two capability blocks were added. A superseded citation of ops-warden `ADR-0006` +was corrected to `ADR-0009`, which retires the global flag outright rather than +deferring it. + +## 4. Scope versus implementation — the gap + +This is the operative section. Each row is checked against the code, not +against the documents. + +| # | Obligation | Standard | Implementation today | Gap | +| --- | --- | --- | --- | --- | +| G1 | Machine-readable layer declaration | §11 | `INTENT.md` frontmatter, added in this session | **closed**, needs a conformance test | +| G2 | Registry-snapshot digest in decision provenance | §6.1, §9.7.2, §13 | `DecisionProvenance` carries evaluator, mode, policy package, policy version, directory ETag, decision time — **no snapshot digest** | **open — conformance prerequisite** | +| G3 | Every allow carries an explicit lifetime | §9.7.1 | `DecisionEnvelope` has **no lifetime field**. `ActionAuthorizationValidity` has `not_before`/`expires_at`, but that is the durable approval object, not the decision | **open** | +| G4 | Revocation visibility deadline per input class | §9.7.2 | Not stated anywhere — no document, no field | **open** | +| G5 | Decision-record schema published as flex-auth's contract | §17 | `schemas/decision_envelope.schema.json` exists and describes `binding` and `provenance` via `$defs`. It carries no lifetime, no policy-package **digest** (name and version only), and no input-claim digests | **partial** | +| G6 | Canonical request digest as the §6.4.2 replay test | §6.4.2 | `NewDecisionBinding` computes it over normalized subject, action, resource, context; it is in every binding and regression-tested | **closed in code, unpublished as consumer guidance** | +| G7 | Doctrine reaches the decision as claim or versioned rule | §6.2 | Context claims and versioned policy packages both exist | **closed** | +| G8 | Approvals consumed as input claims, never mutated | §9.4 | flex-auth mutates no approval. `FLEX-WP-0017` T03/T05 wait on `approval-engine` | **closed on flex-auth's side; blocked externally** | + +### Why G2 is the one that matters most + +G2 and G4 are the same gap seen from two sides, and the standard now says so in +flex-auth's own words. A stated visibility deadline for a fact carried by a +registry snapshot is **unfalsifiable** while provenance holds no snapshot +digest, because nobody can determine afterwards which snapshot a decision read. +Publishing G4 without closing G2 would be publishing a number nobody can check. + +G2 is also cheap. `registry.Snapshot` is already a deterministic, +JSON-serializable struct with a test asserting that determinism +(`TestStoreLoadsAndSavesDeterministicSnapshot`), and `pkg/api/canonical.go` +already carries the canonical-JSON-plus-SHA-256 pattern that +`NewDecisionBinding` uses. The work is to reuse that pattern over the snapshot +and thread the result into `DecisionProvenance`, including through the delegated +adapters, which already share the canonical binding constructor. + +### Why G3 is larger than it looks + +Adding a lifetime field is trivial. Deciding **what the lifetime is** is not: a +TTL, a session binding, or an obligation binding, per §9.7.1. flex-auth has no +session concept, so the honest first step is a policy-package-declared TTL with +an explicit default, and a deny for any allow that would otherwise have no +stated end. That is a policy-surface change and needs fixtures, not just a +struct field. + +### A capability flex-auth claimed and cannot yet exercise + +flex-auth told gate-house it is the repository positioned to notice when the +aggregate of consumer stances diverges from what the policy packages say. §13.1 +now exists as the register, and it has **one row** — `ops-warden`, with +`ops-mason` catalogued PEP-shaped and unpublished. The claim is sound and the +data is not there yet. This is not a flex-auth gap to close alone, but flex-auth +should be able to read the register once it is populated, and should say so +rather than let the claim sit unexercised. + +## 5. What this does not change + +flex-auth's boundaries are unchanged by v0.7. It still does not author doctrine, +own approvals, own enforcement, or own authentication evidence. The rename to +`access-engine` remains a separate governed migration under the two conditions +in `FLEX-DEC-2026-001`, and nothing in the alignment starts it. + +## 6. Outcome + +`FLEX-WP-0019` carries G1's test, G2, G3, G4, G5, and G6. G7 needs nothing. +G8 stays with `FLEX-WP-0017`, blocked on `approval-engine`. diff --git a/workplans/FLEX-WP-0019-layer-model-conformance.md b/workplans/FLEX-WP-0019-layer-model-conformance.md new file mode 100644 index 0000000..c3fe780 --- /dev/null +++ b/workplans/FLEX-WP-0019-layer-model-conformance.md @@ -0,0 +1,143 @@ +--- +id: FLEX-WP-0019 +type: workplan +title: "Layer model v0.7 conformance: provenance, lifetimes, deadlines, and the decision contract" +domain: infotech +repo: flex-auth +status: ready +owner: codex +topic_slug: netkingdom +planning_priority: P1 +planning_order: 119 +created: "2026-08-29" +updated: "2026-08-29" +--- + +# FLEX-WP-0019 - Layer model v0.7 conformance + +The NetKingdom Security Layer Model was accepted at v0.7 on 2026-08-29 and +`INTENT.md`/`SCOPE.md` were aligned to it. The alignment review +(`history/2026-08-29-layer-model-v0.7-alignment-review.md`) identified six gaps +between what flex-auth now states and what it implements. This workplan closes +them. + +Two are conformance obligations rather than features: the registry-snapshot +digest (§9.7.2 promotes it from housekeeping to a prerequisite, on flex-auth's +own argument) and the per-input-class visibility deadlines that depend on it. + +Ordering is load-bearing. **T02 precedes T04** — publishing a visibility +deadline for registry-borne facts before provenance can identify the snapshot +would publish a number nobody can check. + +## Assert the layer declaration mechanically + +```task +id: FLEX-WP-0019-T01 +status: todo +priority: medium +``` + +`INTENT.md` frontmatter carries `layer: Engine`, `role: PDP` per §11. Add a +conformance check and test asserting the declaration parses, states a layer in +the §3 vocabulary, states a role because the layer is Engine, and that no +Tooling client exists in the tree — the Engine-side analogue of ops-warden's +`scripts/check_layer_conformance.py`, which is offered estate-wide as a +reference form. + +Closes G1's remaining half. Wire it into `make test`. + +## Add the registry-snapshot digest to decision provenance + +```task +id: FLEX-WP-0019-T02 +status: todo +priority: high +``` + +`DecisionProvenance` carries no digest of the registry snapshot that supplied +resource, subject, and relationship facts, so a decision that turned on registry +content cannot be replayed from its own record (§6.1). This is flex-auth's one +declared gap in §13 and a conformance prerequisite under §9.7.2. + +Reuse the canonical-JSON plus SHA-256 pattern already in `pkg/api/canonical.go` +over `registry.Snapshot`, which is already deterministic and has a test asserting +it (`TestStoreLoadsAndSavesDeterministicSnapshot`). Thread the digest into +`DecisionProvenance`, through the standalone engine and through every delegated +adapter that shares the canonical constructor, and into +`schemas/decision_envelope.schema.json`. Add regression coverage that a changed +snapshot changes the digest and that two decisions over the same snapshot agree. + +## Give every allow an explicit lifetime + +```task +id: FLEX-WP-0019-T03 +status: todo +priority: high +``` + +§9.7.1 requires every allow to carry an explicit lifetime — a TTL, or a binding +to a session or obligation that ends. `DecisionEnvelope` has no lifetime field; +`ActionAuthorizationValidity` has one but belongs to the durable approval object, +not the decision. + +The struct field is trivial; the semantics are not. flex-auth has no session +concept, so the first honest shape is a **policy-package-declared TTL with an +explicit default**, and a deny for any allow that would otherwise have no stated +end. That is a policy-surface change: it needs package schema support, fixtures +covering declared TTL, defaulted TTL, and the no-stated-end deny, and it must +appear in the decision record and the published schema. + +## State revocation visibility deadlines per input class + +```task +id: FLEX-WP-0019-T04 +status: todo +priority: high +``` + +§9.7.2 requires a PDP to state a deadline **per input class**, because a decision +is a join over sources with unrelated refresh behaviour. Enumerate the classes +flex-auth actually joins — approval-claim freshness, registry snapshot cadence, +policy package activation, directory ETag — and state a deadline for each, with +the mechanism that bounds it rather than an aspiration. + +Depends on **T02**: the registry deadline is unfalsifiable until provenance can +identify the snapshot a decision read. Publish as +`docs/decision-input-freshness.md` and reference it from `INTENT.md`. + +## Publish the decision-record schema as flex-auth's contract + +```task +id: FLEX-WP-0019-T05 +status: todo +priority: medium +``` + +§17 moves the decision-record schema from Taxonomy to `access-engine`, on +flex-auth's own argument that a decision record is the PDP's output artifact. +`schemas/decision_envelope.schema.json` exists and describes `binding` and +`provenance`, but carries no lifetime (T03), no snapshot digest (T02), no +policy-package **digest** — name and version only — and no input-claim digests. + +Complete it, version it as a published contract rather than an internal shape, +and document it in `docs/` as the artifact consumers may rely on. Taxonomy holds +only the shared field vocabulary the claim schema references; this schema is +flex-auth's. + +## Publish the request digest as the replay test + +```task +id: FLEX-WP-0019-T06 +status: todo +priority: medium +``` + +§6.4.2 makes replay permitted **iff the canonical request digest matches and the +decision's lifetime holds**, and names flex-auth's digest as the mechanical test. +The digest is computed and regression-tested but is documented nowhere a consumer +would look. + +Publish what it is computed over, what normalization applies, its stability +guarantees across versions, and worked examples of a permitted retry and a +refused replay. Consumers cannot honour §6.4.2 against an undocumented digest, +and negative caching under §6.4.2 needs the same guidance.