flex-auth/decisions/decisions.md

747 lines
39 KiB
Markdown
Raw Normal View History

# Decision records
Assent to GH-DEC-2026-001 (FLEX-DEC-2026-001), closing FLEX-IN-0001 flex-auth answers gate-house's assent request on the three items ratified in GH-DEC-2026-001, following the estate precedent that a boundary is drawn on review by the other side. Assent to all three, with one conformance debt flex-auth accepts as its own and two conditions on the rename: - Engine framing and sole decision point: assent. flex-auth cannot hold this boundary against zone-engine and decline it as a general rule. But standard section 6 also binds flex-auth: DecisionProvenance carries no registry snapshot digest, so a decision that turned on registry content cannot be replayed from its own provenance. Recorded as a known non-conformance rather than claimed as conformance. - access-engine rename: assent to the name, not to execution. Repository identity and runtime identity must rename in separate revertible steps — since FLEX-WP-0016 the enforcing ops-warden pin binds tokens to the protected-system name, so a single-step rename 401s every warden sign, including the certificate the ops-bridge tunnels depend on. FLEX-WP prefix ownership stays with the repository. - Authoring/evaluation split: assent, with the section 6 test applied symmetrically — a gate-house authority ceiling that determines an outcome reaches the decision as an input claim or as a rule in the versioned policy package, so its application stays reconstructable from the decision record. FLEX-WP-0017-T03 stays wait: the design half re-routes to gate-house, the durable storage half remains unowned and is raised as an engine gap under section 5. Decision id follows the canon scheme {PREFIX}-DEC-YYYY-NNN. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> 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
2026-08-28 21:47:06 +02:00
## FLEX-DEC-2026-001 — Assent to GH-DEC-2026-001: Engine framing, access-engine rename, authoring/evaluation split
```yaml
Assent to GH-DEC-2026-001 (FLEX-DEC-2026-001), closing FLEX-IN-0001 flex-auth answers gate-house's assent request on the three items ratified in GH-DEC-2026-001, following the estate precedent that a boundary is drawn on review by the other side. Assent to all three, with one conformance debt flex-auth accepts as its own and two conditions on the rename: - Engine framing and sole decision point: assent. flex-auth cannot hold this boundary against zone-engine and decline it as a general rule. But standard section 6 also binds flex-auth: DecisionProvenance carries no registry snapshot digest, so a decision that turned on registry content cannot be replayed from its own provenance. Recorded as a known non-conformance rather than claimed as conformance. - access-engine rename: assent to the name, not to execution. Repository identity and runtime identity must rename in separate revertible steps — since FLEX-WP-0016 the enforcing ops-warden pin binds tokens to the protected-system name, so a single-step rename 401s every warden sign, including the certificate the ops-bridge tunnels depend on. FLEX-WP prefix ownership stays with the repository. - Authoring/evaluation split: assent, with the section 6 test applied symmetrically — a gate-house authority ceiling that determines an outcome reaches the decision as an input claim or as a rule in the versioned policy package, so its application stays reconstructable from the decision record. FLEX-WP-0017-T03 stays wait: the design half re-routes to gate-house, the durable storage half remains unowned and is raised as an engine gap under section 5. Decision id follows the canon scheme {PREFIX}-DEC-YYYY-NNN. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> 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
2026-08-28 21:47:06 +02:00
id: FLEX-DEC-2026-001
kind: decision
title: 'Assent to GH-DEC-2026-001: Engine framing, access-engine rename, authoring/evaluation
split'
status: resolved
origin: cross-repo
origin_ref: gate-house GH-DEC-2026-001
standard: net-kingdom/canon/standards/security-layer-model_v0.1.md
intake_ref: FLEX-IN-0001
owner: flex-auth
affects:
- flex-auth
- gate-house
- net-kingdom
- ops-warden
- secrets-engine
- zone-engine
requested_dispositions:
- assent
- revise
- reject
created: '2026-08-28T19:43:38.443788Z'
updated: '2026-08-28T19:44:29.505314Z'
rationale: 'Assent to all three items of GH-DEC-2026-001. Item 1: flex-auth is Engine-layer
and the sole decision point; the INTENT reframe at fe46122 stands. flex-auth accepts
one conformance debt of its own — DecisionProvenance carries no registry snapshot
digest, so a decision turning on registry content is not replayable from its own
provenance (standard section 6). Item 2: access-engine is the right name; execution
is a separate governed migration, conditioned on renaming repository identity and
runtime identity in separate revertible steps (the enforcing ops-warden pin binds
tokens to the protected-system name flex-auth, and a single-step rename would 401
every warden sign) and on FLEX-WP prefix ownership staying with the repository.
Item 3: the authoring/evaluation split is accepted; gate-house authority ceilings
must reach the decision as input claims or as rules in the versioned policy package
so their application is reconstructable from the decision record — the same section
6 test flex-auth applied to zone-engine and now to itself. FLEX-WP-0017-T03/T05
stay wait: the design half is re-routed to gate-house, the durable storage half
remains unowned and is raised as an engine gap.'
decided_by: flex-auth (reviewing side)
decided_at: '2026-08-28T19:44:29.505314Z'
state_hub_decision_id: "c990e442-77c2-4556-a40b-61f65eded10b"
```
## Context
`gate-house` asked flex-auth, via `FLEX-IN-0001`, to assent to the three items
ratified in `GH-DEC-2026-001`. The request follows the estate precedent that a
boundary is drawn on review by the other side rather than asserted — the
precedent flex-auth itself set when it reviewed a `zone-engine` draft and ruled
that flex-auth is the policy decision point and stays the only one.
This record is flex-auth's answer. It is written from the reviewing side: the
questions asked were not "is this flattering to flex-auth" but "does this hold
against what flex-auth actually is, and does flex-auth actually conform".
## Disposition
**Assent to all three items, with one accepted conformance debt and two
conditions on the rename.** Nothing here revises or rejects any part of
`GH-DEC-2026-001`.
### Item 1 — Engine framing and the sole decision point: assent
flex-auth is Engine-layer under §3.3 and §4 of the standard. The defining
property holds: the same authoritative input state yields the same result. The
INTENT reframe is applied at `fe46122` and is correct as written — "control
plane" is dropped as Staff vocabulary, and the exclusive decision-point role of
§6 is stated.
The generalization in §6 is the ruling flex-auth drew against `zone-engine`,
applied to the estate. flex-auth cannot consistently hold that boundary against
another repository and decline it as a general rule.
**Accepted conformance debt — registry provenance.** §6 states that compiled
data determining an outcome is still deciding, and that provenance must remain
reconstructable from the engine's decision. flex-auth does not fully satisfy
this today. `DecisionProvenance` (`pkg/api/canonical.go`) carries the evaluator,
mode, policy package, policy version, and a directory ETag — but no digest of
the registry snapshot that supplied resource, subject, and relationship facts.
A decision that turned on registry content therefore cannot be replayed from
its own provenance.
flex-auth accepts this as its own gap rather than claiming conformance it does
not have. It is the same argument flex-auth made to `zone-engine` — zone
*membership* compiles into the registry snapshot, per-zone *stance* belongs in
the versioned policy package, precisely because registry content is absent from
decision provenance. The clean fix is to make registry content provenanced, and
a workplan will carry it. Until it lands, the flex-auth position that
outcome-determining content belongs in the versioned policy package stands
unchanged, and stands for gate-house's inputs on the same terms (item 3).
### Item 2 — The `flex-auth` → `access-engine` rename: assent to the name, not yet to execution
`access-engine` is the right name. The rejection of `auth-engine` is correct —
`key-cape` owns authentication, and `auth-` preserves exactly the ambiguity the
rename exists to remove. The §8 lane/rule demarcation is an acceptable cost and
flex-auth adopts it: `ops-warden` and `ops-mason` own access lanes, flex-auth
owns access rules.
flex-auth agrees the rename is a separate governed migration and does not treat
`GH-DEC-2026-001` as authorizing it. Two conditions, from the consumer side
rather than the documentary side:
1. **Repository identity and runtime identity must be renamed in separate,
independently revertible steps, repository first.** The name `flex-auth` is
not only a repo slug: it is a deployed service, an in-cluster DNS name, a
chart and image name, and — since `FLEX-WP-0016` moved the ops-warden pin to
`callerAuth.mode: enforce` — a protected-system identifier that inbound
tokens are bound to. A single-step rename would make every `warden sign` a
401, including the certificate the `ops-bridge` tunnels depend on. This is
the same dependency shape that caused `ops-warden`'s `ADR-0006` to defer
`policy.enabled` permanently.
2. **`FLEX-WP` prefix ownership stays with the repository across the rename.**
Existing `FLEX-WP-0001..0018` identifiers are referenced by State Hub
records, by consumer repositories, and by decision provenance in shipped
audit records. They are not renamed retroactively; whether new work takes a
new prefix is a question for the migration record, not for this assent.
The migration also touches State Hub identifiers, `ops-warden`'s routing
tables, `zone-engine`'s boundary text, and `secrets-engine` integrations, as
`GH-DEC-2026-001` states. flex-auth will not begin any of it under this record.
### Item 3 — The authoring/evaluation split: assent, with the §6 test applied symmetrically
The split is correct and flex-auth gives up the authoring half willingly:
gate-house owns the doctrine, the invariants, the authority ceilings, the
operating modes, and the authority context; flex-auth owns evaluation
exclusively plus the policy-as-code mechanism; policy *content* stays with the
protected system's owner.
flex-auth draws one boundary back, which is the §6 test applied to gate-house's
inputs on the same terms flex-auth applies it to `zone-engine` and to itself:
> An authority ceiling that determines an outcome must reach the decision as
> either an input claim on the request or a rule in the versioned policy
> package — and its application must be reconstructable from the decision
> record. A ceiling that resolves an outcome before evaluation runs has decided
> early, and gate-house is Staff, where §6 forbids a decision point.
Concretely: gate-house authors the ceiling; flex-auth renders it. The principal
/actor/runtime triple, the mandate, and the operating mode arrive as input
claims and appear in the decision binding. Where a ceiling constrains an
outcome, it is expressed in the versioned policy package so that the policy
version in provenance identifies the ceiling that applied. This is not a
restriction on gate-house's authorship; it is what keeps the authorship
auditable at decision time.
**Effect on `FLEX-WP-0017`.** The split resolves the overlap and re-routes the
design half of the blocked work: gate-house designs the approval contract,
flex-auth validates approvals at decision time. It does not unblock
`FLEX-WP-0017-T03` — the durable approval object, authenticated approval
entries, and atomic supersession still need an owner for *storage and
lifecycle*, which is neither gate-house's (Staff holds no state another layer
depends on at runtime, §3.4) nor flex-auth's (flex-auth does not own the
organizational approval lifecycle). T03 and T05 stay `wait`, with the design
half now addressed to gate-house. That unowned half is flagged to gate-house as
an engine gap under §5, not solved locally.
## Consequences
- The standard's §11 blocker "flex-auth reframed as an Engine … not yet
assented" is answered for flex-auth. `kings-guard` and `ops-warden` assent
remains outstanding and is not flex-auth's to give.
- `INTENT.md` records the assent and the registry-provenance debt.
- A workplan will carry the registry snapshot digest into `DecisionProvenance`.
- The rename is not started, and must not start before a migration record
exists that satisfies the two conditions above.
## FLEX-DEC-2026-002 — Review of security layer model v0.4: assent with findings, one rule contested
```yaml
id: FLEX-DEC-2026-002
kind: decision
title: 'Review of security layer model v0.4: assent with findings, one rule contested'
status: resolved
origin: cross-repo
origin_ref: net-kingdom security-layer-model_v0.4
standard: net-kingdom/canon/standards/security-layer-model_v0.4.md
intake_ref: FLEX-IN-0002
owner: flex-auth
affects:
- flex-auth
- gate-house
- net-kingdom
- ops-warden
- approval-engine
- maturity-engine
requested_dispositions:
- assent
- revise
- reject
created: '2026-08-29T00:41:21.171665Z'
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'
state_hub_decision_id: "2e96321d-c7bf-4127-9c30-f3def32e5bee"
```
## Context
`FLEX-IN-0002` asked flex-auth to review v0.3. By the time of review the current
text is **v0.4** (plus the in-place §11 amendment at `2aaf46c`), which supersedes
v0.3. This record reviews v0.4 and answers the two questions `FLEX-IN-0002` put.
Three versions have landed since `FLEX-DEC-2026-001`, which answered **v0.1**.
## Disposition
**Assent to v0.4, with one rule contested, two capability assignments not
accepted as assented, and three consistency defects.** Nothing here blocks
adoption; §9.3 needs correction before it is relied on.
### What v0.4 gets right, from flex-auth's side
§6.2 adopts flex-auth's boundary from `FLEX-DEC-2026-001` in substance and binds
gate-house on the same terms. §9.4 upholds the self-dealing objection: the
evaluator does not own the object it evaluates, `access-engine` consumes
approvals as input claims and never mutates them. §13 attributes the
registry-snapshot digest gap to flex-auth as self-declared, which is correct.
§9.5's guardrail — a maturity level MUST NOT gate a decision directly — is §6.1
applied to a new engine, and flex-auth endorses it.
### Contested — §9.3 conflicts with shipped, assented behavior
> *"The deterministic fail to reduced authority default is `access-engine`'s,
> applied when it cannot reach its own inputs."*
Two different failure cases are conflated:
1. **access-engine is reachable but cannot reach its own inputs.** The fallback
is flex-auth's, it is deterministic, and §9.3 is right. flex-auth accepts it.
2. **access-engine is not reachable at all.** The engine applies nothing,
because it is not running. Whatever happens next is the consumer's behavior,
necessarily. flex-auth has held since 2026-08-19 that **fail-open is not
expressible by a PDP at all** — not as a preference, but because there is no
evaluator in the path to express it.
§9.3 as written rules case 2 into the engine, where it cannot live. It also
collides with shipped behavior in a repository that has assented to this
standard: **ops-warden `ADR-0009`** (accepted 2026-08-22, superseding `ADR-0006`)
retires the global `policy.enabled` and `policy.fail_closed` and replaces them
with a **total per-zone map in the consumer PEP** — fails open for
`z0-experimental`, `z1-operational`, `z2-protected`, `z2-continuity` and
`unknown`; fails closed for `z3-critical`. That map is a Staff-layer
degraded-mode fallback, and it is the correct design: the alternative makes
flex-auth a hard dependency of every `warden sign`, including the SSH
certificate the ops-bridge tunnel carrying the policy call depends on.
Proposed correction: §9.3 should rule the **input-degradation** fallback into the
engine and state that **engine-unreachability residue is the consumer's**,
bounded by a requirement that the consumer's stance be declared per zone and
auditable — which `ADR-0009` already satisfies. The sentence *"engine-unavailable
is not grounds for a Staff break-glass path"* is sound and should be kept: a
bypass path around a **reachable** engine is a second decision point. A consumer
choosing its own behavior when there is no engine to ask is not.
### Not accepted as assented — two capability assignments
v0.4's frontmatter lists `flex-auth FLEX-DEC-2026-001` under `assented_by`, and
§14 presents it as adoption of the current text. That record answered v0.1.
Since then §13 names `access-engine` as intended owner of two capabilities
flex-auth has never reviewed:
| Gap row | Position |
| --- | --- |
| Containment surface → `access-engine` + runtime engines | Plausible and consistent with §8's posture asymmetry — rendering reduced authority is what a PDP does. Not yet reviewed, and §9.2 marks it pending anyway. Record it as **proposed owner**, not owner. |
| Authentication / assurance evidence → `user-engine`, `access-engine` | **Declined as stated.** flex-auth consumes assurance claims as input and never re-defines them; `INTENT.md` is explicit that the identity layer owns authentication. Evidence *of authentication* belongs to the identity layer and `audit-core`. flex-auth owns evidence of the **decision**, which it already emits. |
An `intended_owner` in a gap register is a proposal to the named repository, not
an assignment to it — §2 keeps what a repository owns in that repository's own
`INTENT.md`. flex-auth suggests §13 gain a column distinguishing a proposed owner
from an assented one, so the register does not accumulate silent assignments the
way §14's assent row nearly did.
### Consistency defects
1. **Frontmatter `status: proposed` contradicts §14 "Status is accepted."**
Material, because §2 makes this standard the authority for layer assignment.
The change log records v0.2 as accepted and v0.3/v0.4 as proposed, so §14
appears to be a carryover.
2. **§14's adoption count does not add up.** "seven of fifteen" and "the
remaining eight" against a §4 catalog of 17 rows, 16 of them estate-authored
(`OpenBao` excepted by §11's own who-must-declare rule). Seven declared plus
the nine repositories actually listed is sixteen. Should read **seven of
sixteen** and **the remaining nine**.
3. **§14 says "All three repositories whose boundaries moved"** above a table of
four. `audit-core` is the fourth.
## Answers to FLEX-IN-0002
**(1) Is the boundary enough to unblock `FLEX-WP-0017` T03/T05 design?**
Yes for T03. Not quite for T05, which needs two things specified in
`approval-engine`'s contract first:
- **Claim shape must bind to the request digest flex-auth already computes.**
The approval claim should carry the approval id plus the digest of the action
it approves, computed over the same canonical binding as
`NewDecisionBinding` (`FLEX-WP-0017-T01`). Otherwise "approved" and "approved
*for this exact request*" are not distinguishable at decision time, and T05's
wrong-action/lane/stage/targets proofs have nothing to compare against.
- **Single consumption needs an owner and an ordering.** flex-auth never mutates
approvals, so consumption is `approval-engine`'s. But the decision precedes the
action, and the action precedes consumption — so an allow rendered against an
approval that is then never consumed, or consumed twice by a racing caller, is
a gap neither engine closes alone. Naming who marks consumed and at what point
relative to the decision belongs in the contract before T05, not after.
**(2) Is the maturity claim route practical from where flex-auth sits?**
Yes, with one caveat that is flex-auth's own fault rather than
`maturity-engine`'s. A level reaching flex-auth as a **request claim** is
practical today and lands in the decision binding. A level reaching flex-auth as
**registry content** is not reconstructable, because `DecisionProvenance` carries
no registry-snapshot digest — the gap flex-auth self-declared in §13. Until that
closes, maturity levels should arrive as request claims or versioned policy
rules, never compiled into the registry snapshot. This is the same constraint
flex-auth placed on zone stance, for the same reason.
## Consequences
- `FLEX-IN-0002` is closed; the assessment covers v0.4 rather than v0.3.
- flex-auth's assent now attaches to **v0.4** for §§18, §9.19.2, §9.49.6,
and §§1016, and to §9.3 only in its input-degradation half.
- `FLEX-WP-0017` T03/T05 stay `wait` on `approval-engine`, with the two contract
items above raised as prerequisites for T05.
- `SCOPE.md` cites ops-warden `ADR-0006` as current; it is superseded by
`ADR-0009`. Correcting that is flex-auth's own housekeeping.
## FLEX-DEC-2026-003 — Review of security layer model v0.6 and companion v0.1: assent, two answers, five findings
```yaml
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'
state_hub_decision_id: "def6a31b-4cd7-4474-8cfa-339b0fc9fcc6"
```
## Context
gate-house published v0.5 (answering `FLEX-DEC-2026-002`), then v0.6 with a new
two-page companion, and asked flex-auth to review two rules specifically. This
record reviews **v0.6 + companion v0.1** and answers both questions.
Every finding in `FLEX-DEC-2026-002` was honored: §9.3 rewritten along the two-
owner split, §13 given an owner-status column with containment recorded as
*proposed* and authentication/assurance evidence as *declined* with flex-auth's
reasoning, all three consistency defects fixed, and both T05 answers folded into
`approval-engine`'s INTENT and `GH-WP-0002-T06`. gate-house also recorded
plainly that v0.4 had ruled against shipped assented behavior and that neither
it nor ops-warden caught it. That is the loop working.
## Disposition
**Assent to v0.6 and companion v0.1**, with two answers and five findings. None
blocks adoption.
The Engine typing in §3.3 is a real improvement and flex-auth endorses it,
particularly *"a new engine is a PIP unless this standard is amended"* — that
sentence is the structural form of §6, and it forecloses the drift §6 was
written to prevent.
### Answer to question 1 — §6.4.2, no local recaching of the verdict
**The rule is right and flex-auth supports it.** Caching the answer is deciding
early at the consumer; caching an input claim under its own freshness rule is
not. Three refinements, one of which is a genuine collision:
**(a) It contradicts §9.7.1 as written.** §9.7.1 permits an allow bound to *"a
session or obligation that ends."* A session-bound allow is used across later
requests by construction — that is what binding to a session means. §6.4.2
forbids replaying a stored verdict *"for a later request."* Taken together, a
session-bound allow is both permitted and prohibited. The fix is small: scope
§6.4.2 to requests **outside the decision's own stated binding and lifetime**,
which preserves the rule and legalizes exactly the case §9.7.1 already intends.
**(b) "Later request" needs a mechanical test, and flex-auth already ships one.**
A PEP retrying an identical action after a transport failure is not a later
request, but nothing in the standard says how to tell. flex-auth computes a
canonical request digest over the normalized subject, action, resource, and
context (`NewDecisionBinding`, `FLEX-WP-0017-T01`), and it is already in every
decision binding. flex-auth offers it as the normative test: **replay is
permitted iff the request digest matches and the decision's lifetime holds.**
That makes §6.4.2 checkable rather than a matter of implementer judgment, and it
costs the estate nothing new.
**(c) Deny-caching is the one real pattern the rule outlaws without naming it.**
A consumer caching a *deny* under load cannot manufacture authority — §8's
asymmetry holds, and it protects the PDP from retry storms, which is a real
operational need. But it does breach §6.4.4: an action refused with no decision
record naming that request is not reconstructable, and a stale deny is an
availability failure that will be misdiagnosed as a policy one. flex-auth's
recommendation is to **rule on it explicitly** rather than leave implementers to
infer: permit short-lived negative caching only where the refusal is still
recorded, or forbid it in terms. Either is defensible; silence is not, because
this is the pattern an implementer under load reaches for first.
### Answer to question 2 — §9.7.2, does the visibility deadline land on a PDP
**Yes, and it lands harder on the PDP than on the PEP** — but one number is the
wrong shape for a decision point.
A PEP has one boundary and can state one deadline. A PDP's revocation visibility
is **per input class**, because a decision is a join over sources with unrelated
refresh behavior: approval-claim freshness from `approval-engine`, registry
snapshot cadence, policy package activation, and directory ETag. A single
flex-auth number would be either a fiction or the worst case, and the worst case
is the registry snapshot — which is the slowest and the least visible. Proposed:
**§9.7.2 requires a per-input-class deadline at a PDP and a single boundary
deadline at a PEP.**
The consequence for flex-auth is worth stating plainly, because it changes
flex-auth's own priorities. §9.7.2 makes the self-declared registry-provenance
gap **load-bearing rather than untidy**. Without a snapshot digest in
`DecisionProvenance`, nobody can determine after the fact which snapshot a
decision read, so a stated visibility deadline for registry-borne facts is
unfalsifiable — the deadline and the digest are the same gap seen from two
sides. flex-auth accepts that this raises the gap from housekeeping to a
conformance prerequisite and will plan it as one.
### Finding 1 — §6.4's stance-map register does not exist
§6.4 requires every PEP-shaped consumer to publish its stance map and requires
those maps to be *"inventoried — in `maturity-engine` once it exists, in §13
until then."* **§13 contains no stance rows and does not mention stance maps.**
Neither document says where a map is published or in what form.
This matters for precisely the reason §6.4 gives: without the register,
*"`z0``z2` and unknown fail open"* is the estate's effective policy with nobody
having compiled it. `ops-warden` `ADR-0009` is cited three times across the two
documents as the reference shape and appears in no register. flex-auth has a
direct interest here — the aggregate of consumer stances is the estate's real
authorization behavior, and flex-auth is the only repository positioned to
notice when it diverges from what the policy packages say.
### Finding 2 — the companion omits it too, which answers the question asked
gate-house asked what would show the companion is not sufficient on its own.
Companion §5.3 says *"publish your unreachable-engine stance"* but never says
**where**, and omits the inventory obligation entirely. A repository satisfying
the companion faithfully would publish a stance map into its own repo and
believe itself conforming, and no register would learn of it. On every other
point reviewed the companion is faithful to the statute; this is the one gap.
### Finding 3 — §17 puts the decision-record schema in the wrong layer
The four artifacts are the right four, and Taxonomy is right for three of them.
The **request-claim schema** is genuinely cross-engine vocabulary and belongs
there; so do the gap-record and emission-cadence artifacts.
The **decision-record schema** does not. A decision record is the PDP's output
artifact — the one thing in the estate that only `access-engine` produces — and
§2 keeps what a repository owns in that repository's own `INTENT.md`. flex-auth
already publishes its shape: the `binding` in `DecisionEnvelope`, the request
digest, and `schemas/action_authorization.schema.json`. Taxonomy authoring the
schema for an artifact only flex-auth emits inverts the ownership rule the
standard applies everywhere else.
This is the same §2 argument flex-auth used to decline authentication and
assurance evidence in `FLEX-DEC-2026-002`, and gate-house accepted it there.
Symmetry requires flex-auth to apply it against its own interest as well as for
it: proposed split is **claim / gap-record / emission-cadence to Taxonomy,
decision-record schema to `access-engine` as a published contract**, with
Taxonomy holding only the shared field vocabulary the claim schema needs to
reference. flex-auth will publish that contract; it should not receive it.
### Finding 4 — §§1719 are H1, outside the section hierarchy
They use `#` where every other section uses `##`, so they render as siblings of
the document title rather than sections of it. Mechanical, but the standard asks
repositories to cite section numbers, and §§1718 carry normative content.
### Finding 5 — §19 grades the document it lives in
A *"Verdict on fitness"* inside a standard of record makes the assessment
normative by adjacency and will age against the text it grades — v0.7 will
either restate it or leave a stale verdict in force. Suggest it live in the
2026-08-29 assessment history document and be cited from §16, where the open
questions already carry the same content as questions rather than as a grade.
## Consequences
- flex-auth's assent attaches to **v0.6 + companion v0.1**, with §6.4.2 and
§9.7.2 read as refined above pending gate-house's disposition.
- The registry-snapshot digest gap is reclassified from housekeeping to a
conformance prerequisite under §9.7.2, and will be planned as one.
- flex-auth offers the canonical request digest as the §6.4.2 replay test and
offers to publish the decision-record schema as its own contract.
Answer ops-warden and secrets-engine; open FLEX-WP-0021 Two cross-repo questions arrived in the flex-auth inbox and both are answered as decision records rather than as prose in a message. FLEX-DEC-2026-004 answers ops-warden WARDEN-WP-0034-T05. A decision lifetime shorter than the SSH certificate TTL is meaningful, but only as authority to issue, never as authority to use an already-issued certificate. The pre-sign gate is the only consumer of the shorter lifetime: no replay past expires_at, fresh Check per sign. The lever that shortens effective access is the requested TTL as a policy input, which is already deployed as the ttl_out_of_bounds deny. ops-warden's section 9.7.2 window through certificate TTL is correct as written and correctly owned by the PEP; flex-auth does not want that residue moved to the PDP. docs/decision-input-freshness.md gains the same boundary as published contract text, so the ruling is not only in the decision log. FLEX-DEC-2026-005 answers secrets-engine. A real policy package is expected and flex-auth authors it here as it does for every consumer; the reserved coordinate is secrets-engine.catalog-lane.lifecycle v1 and it does not exist yet. Their choice not to default the pin was correct and is endorsed explicitly. POST /v1/check is deployed but has no estate-wide address by design -- per-consumer cluster-local pins with default-deny ingress -- so their 2026-09-06 probe found the design working, not an outage. FLEX-WP-0021 carries that work: obtain the real action vocabulary from secrets-engine, publish the package with fixtures, confirm the digest join against a real decision record, then stand up a flex-auth-secrets-engine pin in warn without moving the other two pins. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JTbVXpEiXA7mNJVpDnEPcB Assistant: claude-code Assistant-Model: opus Assistant-Process: 412054@bnt-lap001 Assistant-Session: 3968fae1-8d59-4209-9bd6-c22594b8ab19
2026-09-06 01:12:50 +02:00
## FLEX-DEC-2026-004 — Decision lifetime does not reach past issuance: answer to ops-warden WARDEN-WP-0034-T05
```yaml
id: FLEX-DEC-2026-004
kind: decision
title: 'Decision lifetime does not reach past issuance: answer to ops-warden WARDEN-WP-0034-T05'
status: resolved
origin: cross-repo
origin_ref: WARDEN-WP-0034-T05
owner: flex-auth
affects:
- flex-auth
- ops-warden
requested_dispositions:
- answer
- decline
decided_by: flex-auth (access-engine / PDP)
rationale: 'Answered, not declined. A decision lifetime shorter than the SSH certificate
TTL is meaningful, but only as an authority-to-issue window, never as an authority-to-use
window over an already-issued certificate. flex-auth lifetime.expires_at bounds
how long that one allow may be relied on to authorise a sign; it cannot bound an
artifact ops-warden issued under it, and flex-auth does not claim it does. The downstream
contract that consumes the shorter lifetime is the pre-sign gate itself: no replay
of an allow past expires_at, and a fresh Check per sign. The lever that actually
shortens effective access is the requested TTL as a policy input, which is already
deployed as the ttl_out_of_bounds deny; ops-warden section 9.7.2 window through
cert TTL is correctly stated and correctly owned by the PEP.'
created: '2026-09-05T23:08:38.877787Z'
updated: '2026-09-06T00:00:00.000000Z'
decided_at: '2026-09-06T00:00:00.000000Z'
state_hub_decision_id: "f6bfb02a-bbf3-4488-acf9-0e1cb57ce19d"
Answer ops-warden and secrets-engine; open FLEX-WP-0021 Two cross-repo questions arrived in the flex-auth inbox and both are answered as decision records rather than as prose in a message. FLEX-DEC-2026-004 answers ops-warden WARDEN-WP-0034-T05. A decision lifetime shorter than the SSH certificate TTL is meaningful, but only as authority to issue, never as authority to use an already-issued certificate. The pre-sign gate is the only consumer of the shorter lifetime: no replay past expires_at, fresh Check per sign. The lever that shortens effective access is the requested TTL as a policy input, which is already deployed as the ttl_out_of_bounds deny. ops-warden's section 9.7.2 window through certificate TTL is correct as written and correctly owned by the PEP; flex-auth does not want that residue moved to the PDP. docs/decision-input-freshness.md gains the same boundary as published contract text, so the ruling is not only in the decision log. FLEX-DEC-2026-005 answers secrets-engine. A real policy package is expected and flex-auth authors it here as it does for every consumer; the reserved coordinate is secrets-engine.catalog-lane.lifecycle v1 and it does not exist yet. Their choice not to default the pin was correct and is endorsed explicitly. POST /v1/check is deployed but has no estate-wide address by design -- per-consumer cluster-local pins with default-deny ingress -- so their 2026-09-06 probe found the design working, not an outage. FLEX-WP-0021 carries that work: obtain the real action vocabulary from secrets-engine, publish the package with fixtures, confirm the digest join against a real decision record, then stand up a flex-auth-secrets-engine pin in warn without moving the other two pins. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JTbVXpEiXA7mNJVpDnEPcB Assistant: claude-code Assistant-Model: opus Assistant-Process: 412054@bnt-lap001 Assistant-Session: 3968fae1-8d59-4209-9bd6-c22594b8ab19
2026-09-06 01:12:50 +02:00
```
## Context
ops-warden now states its §9.7.2 revocation window explicitly: an allow can
remain effective through an already-issued SSH certificate until TTL expiry
(`adm` 48h, `agt` 24h, `atm` 8h), because there is no CRL/KRL recall channel.
It asked flex-auth, as the access engine, whether a decision lifetime shorter
than the certificate TTL is meaningful under that PEP behaviour, and if so what
downstream contract should consume the shorter lifetime.
## Disposition
**Answered, not declined.** The lifetime is meaningful, and the mismatch
ops-warden noticed is not a defect on either side. It is two different objects
with two different revocation stories.
### A decision lifetime is authority to issue, not authority to use
`lifetime.expires_at` (`docs/decision-record-contract.md`, default TTL 15m)
bounds one thing: how long that particular allow may be relied on as the
authority for the action it decided. For ops-warden the action is `sign`, and
the action completes at issuance. Once the certificate exists, the decision has
been consumed; nothing in the decision record reaches the artifact.
flex-auth does not claim otherwise, and a PDP that did would be lying. The
estate already ruled that fail-open is not expressible by a PDP because there
is no evaluator in that path; recall of an issued credential is the same shape
of claim. There is no flex-auth in the path of an SSH session already
authenticated by a valid certificate.
So a 15m decision lifetime against a 48h `adm` certificate is not an
inconsistency to reconcile. It says the gate's answer goes stale in 15 minutes;
it says nothing about the certificate, and was never asked to.
### The downstream contract is the pre-sign gate itself
That is the only consumer of the shorter lifetime, and it consumes it two ways:
1. **No replay past `expires_at`.** An allow may not be reused to authorise a
second `warden sign`, and a cached verdict must not outlive its TTL. This is
already the published rule (`docs/decision-input-freshness.md`, "How to read
this as a consumer") and it is the whole of what the lifetime binds.
2. **Fresh Check per sign.** Because the lifetime is short and claims are not
cached PDP-side, revocation of a subject, actor, or approval claim is visible
to the *next* sign immediately — deadline 0 for the claim class. That is the
real value of the short lifetime: it bounds how long a revoked principal can
keep obtaining *new* certificates, which is the part flex-auth can bound.
### The lever that shortens effective access is already deployed
If the goal is to shrink the window in which a revoked principal retains live
access, the mechanism is not a shorter decision lifetime — it is a shorter
**certificate** TTL, and the requested TTL is already a policy input. The
shipped `ops-warden.ssh-certificate.sign` package denies `ttl_out_of_bounds`
before OpenBao is reached (verified 2026-06-29). Capping the requested TTL per
actor class or per zone in the policy package is a policy change flex-auth can
make and explain; asking the decision lifetime to reach past issuance is not.
flex-auth is not proposing that change here. Certificate TTLs are ops-warden's
to set, and this record does not reopen them.
## Consequences
- ops-warden's §9.7.2 statement — allow effective through certificate TTL,
bounded by 48h/24h/8h, no recall channel — is **correct as written and
correctly owned by the PEP**. flex-auth does not want that residue moved to
the PDP's side of the line.
- No change to `flex-auth.decision-record.v1`. `lifetime` keeps its published
meaning; this record states the boundary it already had rather than adding
one.
- If ops-warden ever wants a certificate TTL bound to policy rather than to
actor class, that is a policy-package change under a new workplan, not a
lifetime semantics change.
## FLEX-DEC-2026-005 — secrets-engine policy package is expected but unpublished; /v1/check has no estate-wide endpoint by design
```yaml
id: FLEX-DEC-2026-005
kind: decision
title: secrets-engine policy package is expected but unpublished; /v1/check has no
estate-wide endpoint by design
status: resolved
origin: cross-repo
origin_ref: secrets-engine SECRETS-WP-0009-T03
owner: flex-auth
affects:
- flex-auth
- secrets-engine
- approval-engine
requested_dispositions:
- answer
decided_by: flex-auth (access-engine / PDP)
rationale: 'Two answers. (1) Yes, a real package is expected, and flex-auth authors
it in this repo as it did for every other consumer; the reserved coordinate is secrets-engine.catalog-lane.lifecycle
at version v1, and it does not exist yet. Until it is published and pinned, secrets-engine
keeping SECRETS_ENGINE_AUTHORIZATION_POLICY_PACKAGE and _VERSION as required configuration
with no fallback is the correct shape and flex-auth endorses it. (2) POST /v1/check
is deployed, but there is no estate-wide PDP address and there is not meant to be
one: each consumer gets its own cluster-local pin whose NetworkPolicy default-denies
ingress except from that one approved workload. The 2026-09-06 probe finding no
reachable PDP is the design working, not an outage. A reachable endpoint for secrets-engine
is a per-consumer pin that follows its policy package.'
created: '2026-09-05T23:08:51.144287Z'
updated: '2026-09-06T00:00:00.000000Z'
decided_at: '2026-09-06T00:00:00.000000Z'
state_hub_decision_id: "f318e4e2-6195-4db8-b8c0-c71eeecad766"
Answer ops-warden and secrets-engine; open FLEX-WP-0021 Two cross-repo questions arrived in the flex-auth inbox and both are answered as decision records rather than as prose in a message. FLEX-DEC-2026-004 answers ops-warden WARDEN-WP-0034-T05. A decision lifetime shorter than the SSH certificate TTL is meaningful, but only as authority to issue, never as authority to use an already-issued certificate. The pre-sign gate is the only consumer of the shorter lifetime: no replay past expires_at, fresh Check per sign. The lever that shortens effective access is the requested TTL as a policy input, which is already deployed as the ttl_out_of_bounds deny. ops-warden's section 9.7.2 window through certificate TTL is correct as written and correctly owned by the PEP; flex-auth does not want that residue moved to the PDP. docs/decision-input-freshness.md gains the same boundary as published contract text, so the ruling is not only in the decision log. FLEX-DEC-2026-005 answers secrets-engine. A real policy package is expected and flex-auth authors it here as it does for every consumer; the reserved coordinate is secrets-engine.catalog-lane.lifecycle v1 and it does not exist yet. Their choice not to default the pin was correct and is endorsed explicitly. POST /v1/check is deployed but has no estate-wide address by design -- per-consumer cluster-local pins with default-deny ingress -- so their 2026-09-06 probe found the design working, not an outage. FLEX-WP-0021 carries that work: obtain the real action vocabulary from secrets-engine, publish the package with fixtures, confirm the digest join against a real decision record, then stand up a flex-auth-secrets-engine pin in warn without moving the other two pins. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JTbVXpEiXA7mNJVpDnEPcB Assistant: claude-code Assistant-Model: opus Assistant-Process: 412054@bnt-lap001 Assistant-Session: 3968fae1-8d59-4209-9bd6-c22594b8ab19
2026-09-06 01:12:50 +02:00
```
## Context
secrets-engine applied flex-auth's earlier PDP answers in commit `627810b`
(`resource.type: secret-catalog-lane`, `resource.system: secrets-engine`,
catalog id as `request.resource.id`, and a join that validates
`ActionAuthorization.decision.binding.request_digest` against
`flex-auth.decision-record.v1`). It then asked two questions, with Glas
real-key execution (`SECRETS-WP-0009-T03`) waiting on the answers.
## Disposition
### Answer 1 — yes, a package is expected; it does not exist yet
Every shipped consumer's policy package is authored **in this repository**, not
in the consumer's, and baked into the image: `ops-warden.ssh-certificate.sign`
(v2), `tenant-engine.write-api.mutate`, `user-engine.portal.authorize`,
`railiance-platform.credential-grant.issue`, `qonto-assistant.finance-read`.
secrets-engine follows the same path. The reserved coordinate is:
| | |
| --- | --- |
| Package | `secrets-engine.catalog-lane.lifecycle` |
| Version | `v1` |
This is a **reservation, not a publication.** No such package exists in
`examples/` today, and until one is published and pinned, a Check naming it
evaluates nothing. `FLEX-WP-0021` carries the work.
secrets-engine's decision not to default the pin was the right call and
flex-auth endorses it explicitly: keeping
`SECRETS_ENGINE_AUTHORIZATION_POLICY_PACKAGE` and `_VERSION` as required
configuration with no fallback is exactly correct. Defaulting them would have
pinned production to example vocabulary nobody publishes, and the failure would
have been a silent evaluation against a package that is not there rather than a
loud missing-configuration error.
### Answer 2 — /v1/check is deployed; there is no estate-wide address, by design
The 2026-09-06 probe finding no reachable PDP found the design working.
flex-auth runs as **per-consumer cluster-local pins**, not as one shared
service. Three are live (`flex-auth-ops-warden`, `flex-auth-tenant-engine`,
`flex-auth-user-engine`). Each is a three-document manifest whose third document
is a default-deny `NetworkPolicy` admitting ingress from exactly one approved
consumer workload, and each is pinned to its own image digest so one consumer's
policy can roll without moving another's (`deploy/README.md`). Caller
authentication is per-pin: on the ops-warden pin it is `enforce`, so an
anonymous `/v1/check` is 401 and a token bound to another protected system is
403 (`FLEX-WP-0016`).
There is therefore no address for secrets-engine to reach *because none has been
created for it*. A reachable endpoint is a per-consumer pin, and a pin follows
its policy package — so both questions resolve to the same piece of work, in
that order: publish the package, then pin it.
## Consequences
- `FLEX-WP-0021` is opened in this repo to publish
`secrets-engine.catalog-lane.lifecycle` v1 and stand up a
`flex-auth-secrets-engine` pin. It needs secrets-engine's real action
vocabulary as input; the example `secrets-engine.lifecycle/v1` vocabulary is
explicitly not it.
- secrets-engine should keep the pin required and unset until the package is
published. Nothing in this record authorises a fallback value.
- No change to `flex-auth.decision-record.v1`. The digest join secrets-engine
built against it is correct as described.