Make the layer declaration a boundary, and review the boundaries it implies.
INTENT.md pinned standard_version: "0.7" in the frontmatter §11 requires. That conflated two things the standard separates itself: assent "records assent to a BOUNDARY, given at the version named. It is not assent to the current text." flex-auth is Engine/PDP at v0.6, v0.7, v0.8 and after; the role does not change when the text is amended. The field was also decorative — parsed into Declaration.StandardVersion and never validated — so the version was load-bearing only via a test asserting it equalled 0.7. That test is inverted rather than deleted: internal/layer now rejects a version pin in the declaration and requires conformance_record to name a file that exists. Version-scoped state moves to docs/conformance/security-layer-conformance.md, a derived artifact carrying what it derives from and the version derived at, as §11 requires of derived artifacts. SCOPE.md: gap assessment replaces "conforming with one declared gap" with three gaps, each with an owner and a route. G2 is new — flex-auth declares no emission guarantee where §11 requires one of every §4 source of evidence. It is recorded as a gap rather than as conformance because the flattering reading, that audit-core is the source and flex-auth merely produces, has been asserted by nobody but flex-auth. Also corrects the stance register from two rows to five. Fixing one line meant reading what the declaration asserts, and a boundary is only half held here. docs/conformance/boundaries-review.md checks the other halves across twelve counterparts and finds four security-relevant repositories with no layer declaration at all — including key-cape, the identity source whose claims flex-auth consumes as normative input. That boundary is asserted from one side only. Recorded as unstated, never as agreed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 28468@bnt-lap001 Assistant-Session: c76569b2-6056-4dad-aea4-49cd7a018f5d
This commit is contained in:
parent
5a2e1959e5
commit
c0d0d92e9f
7 changed files with 470 additions and 26 deletions
21
INTENT.md
21
INTENT.md
|
|
@ -1,13 +1,22 @@
|
||||||
---
|
---
|
||||||
# NetKingdom security layer declaration (security-layer-model_v0.7 §11).
|
# NetKingdom security layer declaration (security-layer-model §11).
|
||||||
# Machine-readable because §11 requires it: prose cannot distinguish a
|
# Machine-readable because §11 requires it: prose cannot distinguish a
|
||||||
# declaration from a transcribed review. Reference form: ops-warden layer.yaml.
|
# declaration from a transcribed review. Reference form: ops-warden layer.yaml.
|
||||||
|
#
|
||||||
|
# This declares a BOUNDARY, and carries no standard version on purpose. The
|
||||||
|
# standard draws the distinction itself: assent "records assent to a BOUNDARY,
|
||||||
|
# given at the version named. It is not assent to the current text." flex-auth is
|
||||||
|
# Engine / PDP at v0.6, v0.7, v0.8 and after; the role does not change when the
|
||||||
|
# text is amended, so a version here would make every revision read as though it
|
||||||
|
# invalidated the declaration. Version-scoped conformance state is derived, moves,
|
||||||
|
# and lives in conformance_record below — which carries the version it was
|
||||||
|
# derived at, as §11 requires of a derived artifact.
|
||||||
layer: Engine
|
layer: Engine
|
||||||
role: PDP
|
role: PDP
|
||||||
framework: netkingdom-security-layer-model
|
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_by: decisions/decisions.md FLEX-DEC-2026-001, FLEX-DEC-2026-002, FLEX-DEC-2026-003
|
||||||
declared_at: "2026-08-29"
|
declared_at: "2026-08-29"
|
||||||
|
conformance_record: docs/conformance/security-layer-conformance.md
|
||||||
pep_stance: null # not PEP-shaped: flex-auth renders decisions, it causes no protected side effect
|
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
|
tooling_contacts: [] # §5 binds Staff; flex-auth holds no Tooling client
|
||||||
---
|
---
|
||||||
|
|
@ -15,8 +24,8 @@ tooling_contacts: [] # §5 binds Staff; flex-auth holds no Tooling client
|
||||||
# Flex-Auth Intent
|
# Flex-Auth Intent
|
||||||
|
|
||||||
> **Layer declaration.** flex-auth is **Engine / PDP** under the NetKingdom
|
> **Layer declaration.** flex-auth is **Engine / PDP** under the NetKingdom
|
||||||
> Security Layer Model (`net-kingdom/canon/standards/security-layer-model_v0.7.md`,
|
> Security Layer Model (`net-kingdom/canon/standards/`, the Security
|
||||||
> accepted 2026-08-29). It is **the only policy decision point in NetKingdom**
|
> Layer Model; declared 2026-08-29). It is **the only policy decision point in NetKingdom**
|
||||||
> (§6): no other repository, in any layer, renders or caches an authorization
|
> (§6): no other repository, in any layer, renders or caches an authorization
|
||||||
> decision. The frontmatter above is the machine-readable form §11 requires; this
|
> decision. The frontmatter above is the machine-readable form §11 requires; this
|
||||||
> paragraph is the declaration in flex-auth's own voice.
|
> paragraph is the declaration in flex-auth's own voice.
|
||||||
|
|
@ -38,7 +47,7 @@ tooling_contacts: [] # §5 binds Staff; flex-auth holds no Tooling client
|
||||||
> three records: `FLEX-DEC-2026-001` (Engine framing, the `access-engine` name,
|
> three records: `FLEX-DEC-2026-001` (Engine framing, the `access-engine` name,
|
||||||
> the authoring/evaluation split), `FLEX-DEC-2026-002` (§9.3 contested and
|
> 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
|
> upheld), and `FLEX-DEC-2026-003` (v0.6 review; §6.4.2, §9.7.2 and §17 as
|
||||||
> adopted in v0.7).
|
> adopted into the standard).
|
||||||
>
|
>
|
||||||
> *One item remains open: the ruled rename to `access-engine`. It is a separate
|
> *One item remains open: the ruled rename to `access-engine`. It is a separate
|
||||||
> governed migration — it touches `FLEX-WP` prefix ownership, State Hub
|
> governed migration — it touches `FLEX-WP` prefix ownership, State Hub
|
||||||
|
|
@ -62,7 +71,7 @@ enterprise-grade authorization without giving up clear ownership, local
|
||||||
development ergonomics, or inspectable policy decisions.
|
development ergonomics, or inspectable policy decisions.
|
||||||
|
|
||||||
It is an **Engine**, role **PDP**, in the NetKingdom security layer model
|
It is an **Engine**, role **PDP**, in the NetKingdom security layer model
|
||||||
(`net-kingdom/canon/standards/security-layer-model_v0.7.md`): a deterministic
|
(`net-kingdom/canon/standards/`, the Security Layer Model): a deterministic
|
||||||
API for a modeled concept, where the same authoritative input state yields the
|
API for a modeled concept, where the same authoritative input state yields the
|
||||||
same result.
|
same result.
|
||||||
|
|
||||||
|
|
|
||||||
73
SCOPE.md
73
SCOPE.md
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
flex-auth is a policy-as-code authorization registry and decision engine for
|
flex-auth is a policy-as-code authorization registry and decision engine for
|
||||||
NetKingdom-aligned systems. Under the NetKingdom Security Layer Model
|
NetKingdom-aligned systems. Under the NetKingdom Security Layer Model
|
||||||
(`security-layer-model_v0.7`, accepted 2026-08-29) it is **Engine / PDP** — the
|
(declared 2026-08-29) it is **Engine / PDP** — the estate's only policy decision
|
||||||
estate's only policy decision point.
|
point.
|
||||||
|
|
||||||
## Core Idea
|
## Core Idea
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ can be coordinated behind a stable flex-auth API.
|
||||||
- Delegated PDP and directory adapter boundaries after the standalone core is
|
- Delegated PDP and directory adapter boundaries after the standalone core is
|
||||||
stable.
|
stable.
|
||||||
- The **decision-record schema**, published as flex-auth's own contract
|
- The **decision-record schema**, published as flex-auth's own contract
|
||||||
(`security-layer-model_v0.7` §17).
|
(Security Layer Model §17).
|
||||||
- The **canonical request digest** as the published replay test for consumers
|
- The **canonical request digest** as the published replay test for consumers
|
||||||
(§6.4.2) — `binding.submitted_request_digest`, over the request as sent.
|
(§6.4.2) — `binding.submitted_request_digest`, over the request as sent.
|
||||||
`request_digest` is over the *enriched* request and is flex-auth's own
|
`request_digest` is over the *enriched* request and is flex-auth's own
|
||||||
|
|
@ -152,11 +152,13 @@ certificates the ops-bridge tunnels depend on, one of which carries the policy
|
||||||
call itself. `ADR-0009` retires `policy.enabled` and the global `policy.fail_closed`
|
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
|
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
|
(`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`
|
`z3-critical`. That map is the reference shape for the Security Layer Model's
|
||||||
§6.4 obligation 3. It is no longer the only published one: `secrets-engine`
|
§6.4 obligation 3. It is no longer the only published one: `secrets-engine`
|
||||||
published `pep-stance.yaml` (total over catalog stage plus `unknown`, runtime-read
|
published `pep-stance.yaml` (total over catalog stage plus `unknown`, runtime-read
|
||||||
and test-pinned), so §13.1's register now has **two rows** rather than the one the
|
and test-pinned), so §13.1's register grew past the single row the standard
|
||||||
standard recorded as itself the finding. flex-auth reviewed the pair in
|
recorded as itself the finding. It now carries **five** — `ops-warden`,
|
||||||
|
`user-engine`, `tenant-engine`, `secrets-engine`, and `ops-mason` with no map at
|
||||||
|
all — two of them marked non-conformant. flex-auth reviewed the pair in
|
||||||
`docs/stance-register-review.md` — the first exercise of the aggregate-divergence
|
`docs/stance-register-review.md` — the first exercise of the aggregate-divergence
|
||||||
capability it claimed on 2026-08-29 — and found that the two maps take opposite
|
capability it claimed on 2026-08-29 — and found that the two maps take opposite
|
||||||
stances on `unknown` (`fail_open` vs `fail_closed`) and scope on different axes
|
stances on `unknown` (`fail_open` vs `fail_closed`) and scope on different axes
|
||||||
|
|
@ -172,15 +174,52 @@ PDP at all** and stays a consumer-side setting. The enforcing pins stay
|
||||||
enforcing regardless — an enforcing pin with no enforcing consumer still turns
|
enforcing regardless — an enforcing pin with no enforcing consumer still turns
|
||||||
the anonymous path into a 401 rather than a decision.
|
the anonymous path into a 401 rather than a decision.
|
||||||
|
|
||||||
**Layer position (2026-08-29).** The NetKingdom Security Layer Model was
|
**Layer position (declared 2026-08-29).** flex-auth is **Engine / PDP** under
|
||||||
accepted at v0.7. flex-auth is **Engine / PDP**, declared machine-readably in
|
the NetKingdom Security Layer Model, declared machine-readably in `INTENT.md`
|
||||||
`INTENT.md` frontmatter per §11. Four of flex-auth's review findings were adopted
|
frontmatter per §11. That declaration deliberately carries **no standard
|
||||||
into the standard: §9.3's two-owner split for degraded mode, §6.4.2 scoped to the
|
version**: a layer is a boundary and does not change when the text is amended,
|
||||||
decision's own binding with the canonical request digest as its mechanical test
|
which is the distinction the standard itself draws when it says assent is *"to a
|
||||||
and negative caching ruled explicitly, §9.7.2 split by role, and §17 moving the
|
BOUNDARY, given at the version named. It is not assent to the current text."* The
|
||||||
decision-record schema to flex-auth. Conformance state is **conforming with one
|
absence is enforced by test in `internal/layer`, and raised to `gate-house` as
|
||||||
declared gap** — the registry-snapshot digest in decision provenance, which §9.7.2
|
`FLEX-WP-0030` B4 in case §11 should say so generally.
|
||||||
promotes to a conformance prerequisite. `FLEX-WP-0019` carries the work.
|
|
||||||
|
Version-scoped state therefore lives in
|
||||||
|
`docs/conformance/security-layer-conformance.md` — a derived artifact that names
|
||||||
|
what it derives from and the version it was derived at, as §11 requires. It
|
||||||
|
carries the assent history (v0.4 `FLEX-DEC-2026-002`, v0.6 `FLEX-DEC-2026-003`,
|
||||||
|
v0.8 `FLEX-DEC-2026-011`) and the gap table below.
|
||||||
|
|
||||||
|
Findings of flex-auth's 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, §17 moving the decision-record schema to flex-auth, and
|
||||||
|
four more in the v0.8 round (`FLEX-DEC-2026-011`), of which F1 — that a decision
|
||||||
|
must be *attributable* to flex-auth and that a digest comparison does not
|
||||||
|
discharge it — was the finding of the round.
|
||||||
|
|
||||||
|
Conformance state is **conforming with three declared gaps**, each with an owner
|
||||||
|
and a route rather than a sentence:
|
||||||
|
|
||||||
|
| # | Gap | Owner | Route |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| G1 | Registry-snapshot digest absent from decision provenance (§9.7.2 conformance prerequisite) | `flex-auth` | `FLEX-WP-0019` |
|
||||||
|
| G2 | No emission guarantee declared, where §11 requires one of every §4 source of evidence | `gate-house` to rule, then `flex-auth` | `FLEX-WP-0030` B3 |
|
||||||
|
| G3 | Published stance-register review stale — written at two register rows, §13.1 now carries five | `flex-auth` | `FLEX-WP-0029` |
|
||||||
|
|
||||||
|
G2 is new and is recorded as a gap rather than as conformance on purpose: whether
|
||||||
|
flex-auth is a §4 *source of evidence* or only the producer of an artifact
|
||||||
|
`audit-core` sources has been asserted by nobody but flex-auth, and §11 says a
|
||||||
|
source declaring no emission guarantee is not conforming. The conservative entry
|
||||||
|
is the honest one until the boundary is ruled.
|
||||||
|
|
||||||
|
**Boundaries review (2026-09-21).** `FLEX-WP-0030` reviews flex-auth's boundary
|
||||||
|
against every security-relevant counterpart and raises five items: inconsistent
|
||||||
|
`layer:` casing across the estate against §11's mechanical-checkability claim
|
||||||
|
(B1); four security-relevant repositories with no layer declaration at all —
|
||||||
|
`gate-house`, `key-cape`, `ops-mason`, `net-kingdom` — of which `key-cape` is the
|
||||||
|
identity source whose claims flex-auth consumes as normative input, making that
|
||||||
|
boundary asserted from one side only (B2); G2 above (B3); B4; and canon naming
|
||||||
|
`access-engine` while the repository still answers to `flex-auth` (B5).
|
||||||
|
|
||||||
State Hub integration is present through:
|
State Hub integration is present through:
|
||||||
|
|
||||||
|
|
@ -297,12 +336,12 @@ description: Workplan for Topaz evaluation, relationship PDP adapters, rule PDP
|
||||||
type: contract
|
type: contract
|
||||||
title: Decision-record contract
|
title: Decision-record contract
|
||||||
status: current
|
status: current
|
||||||
description: Published decision-record schema, the submitted-request digest as the replay test for consumers, request-enrichment precedence, and decision provenance — flex-auth's own contract under security-layer-model v0.7 section 17.
|
description: Published decision-record schema, the submitted-request digest as the replay test for consumers, request-enrichment precedence, and decision provenance — flex-auth's own contract under the Security Layer Model section 17.
|
||||||
```
|
```
|
||||||
|
|
||||||
```capability
|
```capability
|
||||||
type: orientation
|
type: orientation
|
||||||
title: Layer declaration
|
title: Layer declaration
|
||||||
status: current
|
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.
|
description: Machine-readable Engine/PDP boundary declaration in INTENT.md frontmatter, carrying no standard version by design, with version-stamped conformance state and three declared gaps in docs/conformance/security-layer-conformance.md, per Security Layer Model section 11.
|
||||||
```
|
```
|
||||||
|
|
|
||||||
68
docs/conformance/boundaries-review.md
Normal file
68
docs/conformance/boundaries-review.md
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
# Boundaries review — flex-auth against its security-relevant counterparts
|
||||||
|
|
||||||
|
> **Derived artifact** (§11). Derives from
|
||||||
|
> `net-kingdom/canon/standards/security-layer-model_v0.8.md`
|
||||||
|
> (`status: proposed`, `source_revision: gate-house@516ed4e`), from each
|
||||||
|
> counterpart's `INTENT.md` / `layer.yaml` as of the date below, and from
|
||||||
|
> flex-auth's own `decisions/decisions.md`.
|
||||||
|
> Derived at: **2026-09-21**. Reviewer: `flex-auth` (Engine / PDP).
|
||||||
|
> Carried by `FLEX-WP-0030`.
|
||||||
|
|
||||||
|
## Why this exists
|
||||||
|
|
||||||
|
`INTENT.md` was carrying a standard version it should not (`FLEX-WP-0030` T01).
|
||||||
|
Fixing one line meant reading what the declaration actually asserts, and the
|
||||||
|
declaration asserts a boundary — which is only half of a boundary. The other half
|
||||||
|
is held by someone else, and flex-auth had never checked whether the other halves
|
||||||
|
were stated at all.
|
||||||
|
|
||||||
|
They are not, in four cases. That is the finding of this review, and it was not
|
||||||
|
the one being looked for.
|
||||||
|
|
||||||
|
## The rule this review holds itself to
|
||||||
|
|
||||||
|
§11 is explicit: *"A layer stated **about** a repository by another repository is
|
||||||
|
not a declaration."* So nothing below grades a peer. flex-auth reports what it can
|
||||||
|
observe from outside, names the owner, and says which of three states the boundary
|
||||||
|
is in. **`unstated` is not recorded as `agreed`** — that conflation is the whole
|
||||||
|
reason the four cases below went unnoticed.
|
||||||
|
|
||||||
|
## Boundary status
|
||||||
|
|
||||||
|
| Counterpart | Boundary | State |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `gate-house` | owns invariants, authority ceilings, operating modes; flex-auth authors no doctrine and consumes authority context as claims | **agreed in substance, undeclared in form** — no `layer:` key (B2) |
|
||||||
|
| `key-cape` / NetKingdom SSO | identity source; flex-auth consumes verified claims as normative input and never redefines them | **asserted from one side only** — no `layer:` key (B2) |
|
||||||
|
| `ops-warden` | consumer / PEP; owns SSH CA, OpenBao signing, actor inventory and its own stance map. flex-auth owns the decision, never the credential | **agreed, and the most exercised boundary in the estate** |
|
||||||
|
| `secrets-engine` | consumer / PEP; owns credential lifecycle downstream of a decision | **agreed** |
|
||||||
|
| `approval-engine` | owns the approval object, its storage and lifecycle; flex-auth validates approvals as input claims and never mutates them | **agreed** — `GH-DEC-2026-005`, accepted in `FLEX-DEC-2026-006` against flex-auth's own proposal |
|
||||||
|
| `zone-engine` | zone **membership** compiles into the registry snapshot flex-auth consumes; per-zone **stance** is the consumer's | **held by flex-auth since 2026-08-19; not contested, not confirmed** |
|
||||||
|
| `user-engine` | PIP; supplies subject facts | **agreed** |
|
||||||
|
| `tenant-engine` | PIP and consumer. What `CheckRequest.tenant` denotes on the write API — caller, target, or guardrail scope — is **unanswered since 2026-09-15** | **unclear, and live** — `FLEX-WP-0022`, re-asked 2026-09-20 |
|
||||||
|
| `audit-core` | records evidence; flex-auth produces the decision record | **unclear** — which of the two is the §4 *source of evidence* decides whether flex-auth owes an emission guarantee (B3 / G2) |
|
||||||
|
| `maturity-engine` | PIP; supplies maturity claims. §9.5 forbids ranking blocked-clean below conforming | **agreed** |
|
||||||
|
| `kings-guard` | Staff; raised the authentication/assurance evidence gap that flex-auth **declined** (§13, `FLEX-DEC-2026-002`) | **agreed, by mutual declining** |
|
||||||
|
| `ops-mason` | catalogued PEP-shaped in §4 | **undeclared twice over** — no stance map (§13.1 marks it), no `layer:` key (B2) |
|
||||||
|
|
||||||
|
## Findings raised for resolution
|
||||||
|
|
||||||
|
Full statements in `workplans/FLEX-WP-0030-boundary-declaration-cleanup.md` T04.
|
||||||
|
|
||||||
|
| # | Finding | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| B1 | `layer:` casing is inconsistent across ten declarations; §11 calls the check mechanical, and flex-auth's own validator would fail eight of them | `gate-house` |
|
||||||
|
| B2 | `gate-house`, `key-cape`, `ops-mason`, `net-kingdom` carry no machine-readable layer declaration | each named repository |
|
||||||
|
| B3 | flex-auth declares no emission guarantee; whether it owes one turns on an unruled boundary with `audit-core` | `gate-house`, `audit-core` |
|
||||||
|
| B4 | a layer declaration should not pin a standard version; if §11 agrees it should say so generally | `gate-house` |
|
||||||
|
| B5 | canon names `access-engine`; the repository still answers to `flex-auth` and the rename has not landed | `gate-house` to note |
|
||||||
|
|
||||||
|
## What flex-auth is not claiming
|
||||||
|
|
||||||
|
- **Not a conformance finding against anyone.** B1 and B2 are observations about
|
||||||
|
form. A repository may be entirely conformant in substance and simply not have
|
||||||
|
written the key; `gate-house` is the clearest case.
|
||||||
|
- **Not a request that any peer change casing.** B1 may equally be resolved by
|
||||||
|
ruling the vocabulary case-insensitive, which would make flex-auth's validator
|
||||||
|
the thing that changes.
|
||||||
|
- **Not a grade.** §9.3's two-owner split is flex-auth's own finding and it cuts
|
||||||
|
here: the PDP does not get to score the repositories whose facts it consumes.
|
||||||
72
docs/conformance/security-layer-conformance.md
Normal file
72
docs/conformance/security-layer-conformance.md
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
# Security Layer Model — flex-auth conformance record
|
||||||
|
|
||||||
|
> **Derived artifact** (§11). Derives from
|
||||||
|
> `net-kingdom/canon/standards/security-layer-model_v0.8.md`
|
||||||
|
> (`status: proposed`, `source_revision: gate-house@516ed4e`).
|
||||||
|
> Derived at: **2026-09-21**. Maintained by `flex-auth`.
|
||||||
|
|
||||||
|
This file exists because `INTENT.md` deliberately does not carry one. The layer
|
||||||
|
declaration states a **boundary** — flex-auth is Engine / PDP — and a boundary
|
||||||
|
does not change when the standard text is amended. Conformance state does change,
|
||||||
|
every revision, and belongs in an artifact that is expected to move and that
|
||||||
|
carries the version it was derived at.
|
||||||
|
|
||||||
|
The split is the standard's own. Its frontmatter: *"`assented_by` records assent
|
||||||
|
to a BOUNDARY, given at the version named. It is not assent to the current text."*
|
||||||
|
|
||||||
|
## Declaration
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| Layer | `Engine` |
|
||||||
|
| Role | `PDP` — the estate's only policy decision point (§6) |
|
||||||
|
| Declared at | 2026-08-29 |
|
||||||
|
| Declared by | `FLEX-DEC-2026-001`, `FLEX-DEC-2026-002`, `FLEX-DEC-2026-003` |
|
||||||
|
| PEP stance | `null` — not PEP-shaped; flex-auth renders decisions and causes no protected side effect |
|
||||||
|
| Tooling contacts | none — §5 binds Staff; flex-auth holds no Tooling client |
|
||||||
|
|
||||||
|
## Assent history
|
||||||
|
|
||||||
|
Assent is to the boundary, recorded at the version where it was given.
|
||||||
|
|
||||||
|
| Version | Record | Outcome |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| v0.4 | `FLEX-DEC-2026-002` | §9.3 contested and upheld |
|
||||||
|
| v0.6 | `FLEX-DEC-2026-003` | review; §6.4.2, §9.7.2, §17 adopted into v0.7 |
|
||||||
|
| v0.7 | — | four flex-auth findings adopted |
|
||||||
|
| v0.8 (`proposed`) | `FLEX-DEC-2026-011` | assent with four findings, all adopted |
|
||||||
|
|
||||||
|
## Conformance state at v0.8
|
||||||
|
|
||||||
|
**Conforming, with declared gaps below.** §11's four states: conforming,
|
||||||
|
blocked-clean, declared gap, undeclared violation. flex-auth claims no
|
||||||
|
blocked-clean capability and holds no undeclared violation it is aware of.
|
||||||
|
|
||||||
|
| # | Gap | Owner | Blocked on | Review |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| G1 | Registry-snapshot digest absent from decision provenance. §9.7.2 promotes it to a conformance prerequisite: a decision that turned on registry content must be replayable from its own record. | `flex-auth` | implementation | `FLEX-WP-0019` |
|
||||||
|
| G2 | **Emission guarantee not declared.** §11 requires every §4 repository catalogued as a source of evidence to declare class, cadence and detection surface in its machine-readable declaration, and says a source declaring none is not conforming. flex-auth declares none. | `gate-house` to rule, then `flex-auth` | whether flex-auth is a §4 *source of evidence* or only the producer of an artifact `audit-core` sources — see `FLEX-WP-0030` B3 | `FLEX-WP-0030-T04` |
|
||||||
|
| G3 | Published stance-register review is stale: written at two register rows, §13.1 now carries five, and its Finding 1 was ruled by v0.8 §6.4 obligation 3. | `flex-auth` | second edition | `FLEX-WP-0029` |
|
||||||
|
|
||||||
|
G2 is recorded as a gap rather than as conformance because the flattering reading
|
||||||
|
— that `audit-core` is the source and flex-auth merely produces — has not been
|
||||||
|
confirmed by anyone but flex-auth. §11 says a source that declares no emission
|
||||||
|
guarantee is not conforming; until the boundary is ruled, the conservative entry
|
||||||
|
is the honest one.
|
||||||
|
|
||||||
|
## Not gaps
|
||||||
|
|
||||||
|
- **No standard version in `INTENT.md`.** Deliberate, enforced by test
|
||||||
|
(`internal/layer`), and raised to `gate-house` as `FLEX-WP-0030` B4 in case §11
|
||||||
|
should say so generally.
|
||||||
|
- **Canon says `access-engine`, the repository answers to `flex-auth`.** The
|
||||||
|
rename is ruled (`FLEX-DEC-2026-013`) and sequenced (`FLEX-WP-0020`); the
|
||||||
|
coordinate is pending, not broken. Raised as B5.
|
||||||
|
|
||||||
|
## How this file is kept true
|
||||||
|
|
||||||
|
`internal/layer` asserts that `INTENT.md` carries no `standard_version` and names
|
||||||
|
a `conformance_record` that exists on disk. That is the same property flex-auth
|
||||||
|
praised in `ops-warden`'s stance map: the published artifact is asserted equal to
|
||||||
|
the shipped one by test, rather than merely written down. It does **not** assert
|
||||||
|
the contents below the declaration — a reviewer still has to read this file.
|
||||||
|
|
@ -37,9 +37,15 @@ type Declaration struct {
|
||||||
Layer string `yaml:"layer"`
|
Layer string `yaml:"layer"`
|
||||||
Role string `yaml:"role"`
|
Role string `yaml:"role"`
|
||||||
Framework string `yaml:"framework"`
|
Framework string `yaml:"framework"`
|
||||||
StandardVersion string `yaml:"standard_version"`
|
|
||||||
DeclaredBy string `yaml:"declared_by"`
|
DeclaredBy string `yaml:"declared_by"`
|
||||||
DeclaredAt string `yaml:"declared_at"`
|
DeclaredAt string `yaml:"declared_at"`
|
||||||
|
// ConformanceRecord points at the derived, version-stamped conformance state.
|
||||||
|
// The declaration itself is a boundary and carries no standard version.
|
||||||
|
ConformanceRecord string `yaml:"conformance_record"`
|
||||||
|
// StandardVersion must stay empty: a layer is a boundary, and the standard
|
||||||
|
// says assent is "to a BOUNDARY, given at the version named. It is not
|
||||||
|
// assent to the current text."
|
||||||
|
StandardVersion string `yaml:"standard_version"`
|
||||||
PepStance any `yaml:"pep_stance"`
|
PepStance any `yaml:"pep_stance"`
|
||||||
ToolingContacts []any `yaml:"tooling_contacts"`
|
ToolingContacts []any `yaml:"tooling_contacts"`
|
||||||
}
|
}
|
||||||
|
|
@ -98,6 +104,12 @@ func ValidateDeclaration(decl Declaration) error {
|
||||||
if decl.PepStance != nil {
|
if decl.PepStance != nil {
|
||||||
return fmt.Errorf("flex-auth is not PEP-shaped; pep_stance must be null")
|
return fmt.Errorf("flex-auth is not PEP-shaped; pep_stance must be null")
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(decl.StandardVersion) != "" {
|
||||||
|
return fmt.Errorf("layer declaration must carry no standard_version: a layer is a boundary, not a version-scoped claim; version-stamped state belongs in conformance_record (got %q)", decl.StandardVersion)
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(decl.ConformanceRecord) == "" {
|
||||||
|
return fmt.Errorf("layer declaration must name a conformance_record: §11 requires a derived artifact to carry the version it was derived at")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package layer_test
|
package layer_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -27,8 +28,28 @@ func TestLayerDeclarationConforms(t *testing.T) {
|
||||||
if decl.Framework != "netkingdom-security-layer-model" {
|
if decl.Framework != "netkingdom-security-layer-model" {
|
||||||
t.Fatalf("framework = %q", decl.Framework)
|
t.Fatalf("framework = %q", decl.Framework)
|
||||||
}
|
}
|
||||||
if decl.StandardVersion != "0.7" {
|
// The declaration is a boundary and must NOT pin a standard version: the
|
||||||
t.Fatalf("standard_version = %q; want 0.7", decl.StandardVersion)
|
// role does not change when the standard text is amended. Version-scoped
|
||||||
|
// conformance state lives in the derived record named below.
|
||||||
|
if decl.StandardVersion != "" {
|
||||||
|
t.Fatalf("standard_version = %q; want empty (boundary, not version-scoped)", decl.StandardVersion)
|
||||||
|
}
|
||||||
|
if decl.ConformanceRecord == "" {
|
||||||
|
t.Fatal("conformance_record is empty; version-stamped state must have a home")
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(repoRoot(t), decl.ConformanceRecord)); err != nil {
|
||||||
|
t.Fatalf("conformance_record %q does not exist: %v", decl.ConformanceRecord, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionPinInDeclarationIsRejected(t *testing.T) {
|
||||||
|
err := layer.ValidateDeclaration(layer.Declaration{
|
||||||
|
Layer: "Engine", Role: "PDP",
|
||||||
|
ConformanceRecord: "docs/conformance/security-layer-conformance.md",
|
||||||
|
StandardVersion: "0.8",
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("a standard_version pin in the boundary declaration was accepted")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
223
workplans/FLEX-WP-0030-boundary-declaration-cleanup.md
Normal file
223
workplans/FLEX-WP-0030-boundary-declaration-cleanup.md
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
---
|
||||||
|
id: FLEX-WP-0030
|
||||||
|
type: workplan
|
||||||
|
title: "The layer declaration pins a version it should not, and four security-relevant peers do not declare at all"
|
||||||
|
domain: infotech
|
||||||
|
repo: flex-auth
|
||||||
|
status: ready
|
||||||
|
flavor: review
|
||||||
|
owner: claude
|
||||||
|
topic_slug: netkingdom
|
||||||
|
planning_priority: P1
|
||||||
|
planning_order: 300
|
||||||
|
related_workplans:
|
||||||
|
- FLEX-WP-0019
|
||||||
|
- FLEX-WP-0029
|
||||||
|
created: "2026-09-21"
|
||||||
|
updated: "2026-09-21"
|
||||||
|
---
|
||||||
|
|
||||||
|
# FLEX-WP-0030 — Boundary declaration cleanup and estate boundaries review
|
||||||
|
|
||||||
|
`INTENT.md` declares `standard_version: "0.7"` in the frontmatter that §11
|
||||||
|
requires. That conflates two things the standard itself separates. The standard's
|
||||||
|
own frontmatter says it outright:
|
||||||
|
|
||||||
|
> `assented_by` records assent to a **BOUNDARY**, given at the version named.
|
||||||
|
> It is not assent to the current text.
|
||||||
|
|
||||||
|
A layer is a boundary. flex-auth is Engine / PDP whether the standard is at v0.6,
|
||||||
|
v0.7, v0.8 or v1.0 — the role does not change when the text is amended. Pinning a
|
||||||
|
version in the boundary declaration makes every standard revision look like it
|
||||||
|
invalidates the declaration, and generates exactly the churn seen this month:
|
||||||
|
v0.8 lands, the file still says `0.7`, and a reader cannot tell whether that is
|
||||||
|
staleness or a deliberate position.
|
||||||
|
|
||||||
|
**It is also decorative.** `internal/layer/conformance.go:40` parses
|
||||||
|
`standard_version` into `Declaration.StandardVersion` and `ValidateDeclaration`
|
||||||
|
never reads it. The field is enforced by nothing, in a file whose entire purpose
|
||||||
|
is being mechanically checkable.
|
||||||
|
|
||||||
|
## 1. Make the declaration version-agnostic
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: FLEX-WP-0030-T01
|
||||||
|
status: todo
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Owner: `flex-auth`.
|
||||||
|
|
||||||
|
- Drop `standard_version` from the `INTENT.md` frontmatter. §11 requires a
|
||||||
|
`layer:` key; it does not require a version, and flex-auth's own validator
|
||||||
|
does not check one.
|
||||||
|
- Replace versioned prose references (`security-layer-model_v0.7.md`) with the
|
||||||
|
unversioned standard path. `INTENT.md` is declared "aspirational and stable"
|
||||||
|
in its own words; a stable file should not carry a number that moves quarterly.
|
||||||
|
- Move version-scoped conformance state out of `INTENT.md` into a conformance
|
||||||
|
record that is *supposed* to move: which version was reviewed, the conformance
|
||||||
|
state, and each declared gap with owner, blocker and review date (§11's
|
||||||
|
four-state table).
|
||||||
|
- That split also satisfies §11's derived-artifact rule, which requires a
|
||||||
|
derived artifact to name what it derives from **and the version it was derived
|
||||||
|
at**. The boundary declaration is not derived; the conformance record is, and
|
||||||
|
it is the artifact that should carry the version.
|
||||||
|
- Keep `internal/layer` green, and decide whether `StandardVersion` stays as a
|
||||||
|
tolerated-but-unused field or is removed. Do not leave a parsed field that
|
||||||
|
nothing validates without saying which it is.
|
||||||
|
|
||||||
|
Gate: `INTENT.md` names no standard version; conformance state is version-stamped
|
||||||
|
somewhere that is maintained; `go test ./internal/layer/...` passes.
|
||||||
|
|
||||||
|
## 2. Update SCOPE.md and assess the gaps
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: FLEX-WP-0030-T02
|
||||||
|
status: todo
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Owner: `flex-auth`.
|
||||||
|
|
||||||
|
`SCOPE.md`'s Current State is accurate but drifting: it says §13.1's register has
|
||||||
|
**two rows** (it has five — `FLEX-WP-0029` owns that), and it states conformance
|
||||||
|
as "conforming with one declared gap" without naming where the gap list is
|
||||||
|
maintained.
|
||||||
|
|
||||||
|
Assess and state, per gap: what it is, who owns it, what unblocks it, and when it
|
||||||
|
is next reviewed. Known candidates:
|
||||||
|
|
||||||
|
| Candidate gap | State |
|
||||||
|
| --- | --- |
|
||||||
|
| Registry-snapshot digest in decision provenance | declared, `FLEX-WP-0019`, §9.7.2 conformance prerequisite |
|
||||||
|
| Emission guarantee not declared (see T04 / B3) | **unassessed** — may be a second declared gap |
|
||||||
|
| Stance-register review stale at five rows | `FLEX-WP-0029` |
|
||||||
|
|
||||||
|
Gate: every gap in `SCOPE.md` has an owner and a route; no gap is described only
|
||||||
|
as a sentence.
|
||||||
|
|
||||||
|
## 3. Publish the boundaries review
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: FLEX-WP-0030-T03
|
||||||
|
status: todo
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Owner: `flex-auth`.
|
||||||
|
|
||||||
|
Review flex-auth's boundary against every security-relevant repository it names
|
||||||
|
or is named by, and publish findings as a derived artifact marked with the
|
||||||
|
version it was derived at. Five findings are already identified in T04; the
|
||||||
|
review is the durable form of them.
|
||||||
|
|
||||||
|
Cover at minimum: `gate-house` (doctrine), `key-cape` (identity claims in),
|
||||||
|
`ops-warden`, `secrets-engine`, `user-engine`, `tenant-engine`, `zone-engine`,
|
||||||
|
`approval-engine`, `audit-core`, `maturity-engine`, `kings-guard`, `ops-mason`.
|
||||||
|
|
||||||
|
Gate: the review states, for each counterpart, whether the boundary is agreed,
|
||||||
|
contested, or unstated — and does not record "unstated" as if it were "agreed".
|
||||||
|
|
||||||
|
## 4. Raise the conflicting and unclear boundaries for resolution
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: FLEX-WP-0030-T04
|
||||||
|
status: todo
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Owner: `flex-auth` to raise; the named owner resolves each.
|
||||||
|
|
||||||
|
### B1 — `layer:` case is inconsistent, and §11 calls the check mechanical
|
||||||
|
|
||||||
|
| Value written | Repositories |
|
||||||
|
| --- | --- |
|
||||||
|
| `Engine` | `flex-auth`, `maturity-engine` |
|
||||||
|
| `engine` | `secrets-engine`, `approval-engine`, `user-engine`, `tenant-engine`, `zone-engine`, `audit-core` |
|
||||||
|
| `staff` | `ops-warden`, `kings-guard` |
|
||||||
|
|
||||||
|
flex-auth's own validator rejects anything outside `{Staff, Engine, Tooling}`
|
||||||
|
(`internal/layer/conformance.go:86`), so run across the estate it would fail
|
||||||
|
eight of ten declarations on casing alone. Either the §3 vocabulary is
|
||||||
|
case-insensitive and every checker MUST fold case, or it is not and eight
|
||||||
|
declarations are non-conformant. Today each repository's checker only reads its
|
||||||
|
own file, so the disagreement is invisible by construction.
|
||||||
|
|
||||||
|
Owner: `gate-house`. This is the §11 mechanical-checkability claim meeting its
|
||||||
|
first cross-repository run.
|
||||||
|
|
||||||
|
### B2 — four security-relevant repositories carry no layer declaration
|
||||||
|
|
||||||
|
`gate-house`, `key-cape`, `ops-mason`, and `net-kingdom` have no `layer:` key in
|
||||||
|
`INTENT.md` and no equivalent declaration file. §11 requires one of every
|
||||||
|
estate-authored repository in §4, and is explicit that a layer stated *about* a
|
||||||
|
repository by another repository is not a declaration.
|
||||||
|
|
||||||
|
Two matter directly to flex-auth:
|
||||||
|
|
||||||
|
- **`key-cape`** is the identity source whose verified claims flex-auth consumes
|
||||||
|
as normative input and never redefines. That boundary is load-bearing for
|
||||||
|
every decision flex-auth renders, and it is asserted only from flex-auth's
|
||||||
|
side.
|
||||||
|
- **`gate-house`** authors the obligation. A doctrine owner that has not
|
||||||
|
discharged its own §11 obligation is the standard's §9.1 defect pointed at
|
||||||
|
itself.
|
||||||
|
|
||||||
|
`ops-mason` is already marked non-conformant in §13.1 for publishing no stance
|
||||||
|
map; a missing layer declaration is the same gap one level up.
|
||||||
|
|
||||||
|
Owner: each named repository. flex-auth raises, does not grade.
|
||||||
|
|
||||||
|
### B3 — flex-auth declares no emission guarantee, and may owe one
|
||||||
|
|
||||||
|
§11 requires every repository catalogued in §4 **as a source of evidence** to
|
||||||
|
declare its emission guarantee — class, cadence, and detection surface — in its
|
||||||
|
machine-readable layer declaration, and says a source that declares none is not
|
||||||
|
conforming.
|
||||||
|
|
||||||
|
flex-auth produces the decision record, which §17 moved to flex-auth as its own
|
||||||
|
contract and calls "the one thing in the estate only `access-engine` produces".
|
||||||
|
flex-auth's frontmatter declares no emission guarantee.
|
||||||
|
|
||||||
|
The unclear boundary is whether flex-auth is a §4 *source of evidence* or merely
|
||||||
|
the producer of an artifact that `audit-core` is the source of. The answer
|
||||||
|
decides whether this is a second declared gap or nothing at all. flex-auth should
|
||||||
|
not answer it alone, and should not assume the flattering reading.
|
||||||
|
|
||||||
|
Owner: `gate-house` to rule; `audit-core` to confirm which side of the line it
|
||||||
|
holds.
|
||||||
|
|
||||||
|
### B4 — the declaration pins a version the standard says is not part of the boundary
|
||||||
|
|
||||||
|
Raised here as the general form of T01, because if flex-auth is right that a
|
||||||
|
layer declaration should not carry a standard version, the fix belongs in §11 and
|
||||||
|
not only in flex-auth's file. If §11 wants a version, it should say so and say
|
||||||
|
what it means when the standard moves.
|
||||||
|
|
||||||
|
Owner: `gate-house`.
|
||||||
|
|
||||||
|
### B5 — canon names a repository that does not resolve
|
||||||
|
|
||||||
|
v0.8 refers to the PDP as `access-engine` throughout (§4 catalog row, §13.1,
|
||||||
|
§17), while the repository, runtime, namespace, images, and API vocabulary all
|
||||||
|
remain `flex-auth` per `FLEX-DEC-2026-013`, and the rename has not landed —
|
||||||
|
`access-engine` raw returns 404, verified 2026-09-20 under `FLEX-WP-0020`.
|
||||||
|
|
||||||
|
Neither side is wrong: the rename is ruled and sequenced, and canon named the
|
||||||
|
end state. But a reader of v0.8 cannot resolve the repository it keeps naming,
|
||||||
|
and `reuse-surface` already probed the 404 independently. Worth one line in the
|
||||||
|
standard recording that the coordinate is pending rather than broken.
|
||||||
|
|
||||||
|
Owner: `gate-house` to note; `flex-auth` to ping when `FLEX-WP-0020` lands.
|
||||||
|
|
||||||
|
Gate: every finding above is sent to its named owner with what would resolve it.
|
||||||
|
An unanswered finding stays open and is not closed by silence.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- Bumping flex-auth to declare v0.8. v0.8 is `status: proposed`; T01 removes the
|
||||||
|
version from the declaration entirely, which makes the question moot rather
|
||||||
|
than answering it.
|
||||||
|
- Grading any peer repository's conformance. flex-auth reports what it can
|
||||||
|
observe and names the owner; §9.3's two-owner split cuts here too.
|
||||||
|
- `FLEX-WP-0029`'s stance-register second edition. Adjacent, separately owned.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue