flex-auth/workplans/FLEX-WP-0021-secrets-engine-consumer-policy-gate.md

325 lines
15 KiB
Markdown
Raw Normal View History

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
---
id: FLEX-WP-0021
type: workplan
title: "secrets-engine consumer policy package and cluster-local pin"
domain: infotech
repo: flex-auth
fix(secrets-engine): v2 adds the tenant rule v1 never had secrets-engine.catalog-lane.lifecycle v1 contained no reference to input.tenant — not in well_formed, not in the denial ladder, not in a test. A rotate on lane:glas-primary under tenant:coulomb returned allow against the deployed package (decision:066e629bbf0c0924). Found while answering glas-harness's tenant-alignment request, which had asked for wrong-tenant denial evidence. There was none to return. Three covers failed the same way: every one of the 29 fixtures carried tenant:platform, so the suite could not report on the field; T02's own gate named "wrong-tenant deny" and was recorded done unmet; and the engine hashes tenant into request_digest but never compares it. Four other published packages carry the branch — this one was the outlier. v2 adds wrong_tenant above wrong_system, three Rego tests and three fixtures (28/28, 32/32). The absent-tenant test caught a second defect in the first draft: a bare input.tenant != comparison is undefined on a missing key, so the branch dropped and the ladder reported the wrong rung. request_tenant := object.get(input, "tenant", "") fixes it. v2 supersedes rather than amends v1 because the defect failed open: a consumer pinned to _VERSION=v1 would keep receiving allows with no signal the rule beneath the version string had changed. The earlier dual-control correction stayed at v1 because it denied everything. Replay envelopes regenerated at v2; both request_digest values are byte-identical, so secrets-engine's digest join needs no re-pinning. The sweep this prompted found tenant-engine unscoped on tenant as well — deployed, and verified allowing tenant:coulomb. Not the same fix: its request tenant names the target rather than the caller, so a constant would break it. Recorded and carried by FLEX-WP-0022 rather than patched unilaterally. FLEX-WP-0021 closes at T05; the pin still serves v1 until a redeploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aQMM1dPXaPiXVn6DwwtLd Assistant: claude-code Assistant-Model: opus Assistant-Process: 715613@bnt-lap001 Assistant-Session: fabd95c1-4c9e-4080-8849-8707ae025f80
2026-09-06 20:38:45 +02:00
status: finished
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
owner: claude
topic_slug: netkingdom
planning_priority: P1
planning_order: 210
depends_on_workplans:
- FLEX-WP-0012
- FLEX-WP-0016
related_workplans:
- SECRETS-WP-0009
created: "2026-09-06"
updated: "2026-09-06"
state_hub_workstream_id: "b01f655e-f71a-50ae-b110-178557f07c63"
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-WP-0021 — secrets-engine consumer policy package and cluster-local pin
Opened by `FLEX-DEC-2026-005`, answering secrets-engine's two questions of
2026-09-05. Both resolve to one piece of work in one order: **publish the
package, then pin it.**
## Why this exists
secrets-engine has already built its side. Commit `627810b` applies flex-auth's
PDP answers — `resource.type: secret-catalog-lane`, `resource.system:
secrets-engine`, catalog id as `request.resource.id` — and validates
`ActionAuthorization.decision.binding.request_digest` against
`flex-auth.decision-record.v1` with `status: approved` inside validity and
distinct approvers.
Two things it cannot supply itself:
1. `SECRETS_ENGINE_AUTHORIZATION_POLICY_PACKAGE` / `_VERSION` are required
configuration with **no fallback**, deliberately. `secrets-engine.lifecycle/v1`
is example vocabulary, not a published package. Until flex-auth publishes a
real one, that configuration cannot be set to anything true.
2. There is no address for secrets-engine to call. flex-auth runs as
per-consumer cluster-local pins with default-deny ingress admitting one
approved workload each (`deploy/README.md`); no pin exists for
secrets-engine. Its 2026-09-06 probe finding no reachable PDP found the
design working, not an outage.
Glas real-key execution (`SECRETS-WP-0009-T03`) is the workload waiting.
## Boundary
flex-auth authors the policy package; secrets-engine owns the action vocabulary
it encodes and the enforcement around the verdict. flex-auth never mutates the
approval object — that is `approval-engine`'s (`security-layer-model_v0.7` §9.4)
— and validates approvals only as input claims. This plan does not default,
weaken, or supply a fallback for the consumer's package pin.
## 1. Obtain the real action vocabulary from secrets-engine
```task
id: FLEX-WP-0021-T01
Publish secrets-engine.catalog-lane.lifecycle v1 (FLEX-WP-0021 T01, T02) secrets-engine delivered the action vocabulary T01 asked for: twelve actions read out of cli.py, not the example vocabulary. The gate earned its keep -- four would have been inferred wrongly, and revoke, the obvious thirteenth, does not exist as an action at all. It gates as deactivate, which is also reached from lifecycle deactivate. docs/secrets-engine-action-vocabulary.md records the list and the four traps. examples/secrets-engine/ carries the package, both manifests, a loadable registry snapshot, 26 fixtures, five check requests, and a README. validate -kind policy is valid with 22/22 Rego tests and 26/26 fixtures. allow_ttl is 15m, stated in the package rather than inherited from the engine default: these decisions authorize live secret operations, so the reliance window belongs where a reviewer sees it. Per FLEX-DEC-2026-004 it is authority to issue, not authority to keep using what the operation produced. destroy is the dual-control case, gated on a context.approval claim marked approved with two distinct approvers, repeated entries counting once. flex-auth checks what the claim says and deliberately does not re-derive its temporal validity, signature, or supersession -- those are approval-engine's to assert and the PEP's to verify against the live claim, per the split accepted in FLEX-DEC-2026-006. It stays in the package though its handler raises before the gate, so the rule is reviewed and fixtured before SECRETS-WP-0007-T04 opens the path. Following the FLEX-WP-0010-T02 precedent the denial ladder has no action_not_granted branch: one subject holding all twelve actions could never reach it, and a rule that cannot fail reads as control that is not there. Registering a second calling identity is the revisit trigger. Not deployed. No flex-auth-secrets-engine pin exists yet (T04), so their policy pin stays unset and fail-closed until T05. 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 08:02:52 +02:00
status: done
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
priority: high
state_hub_task_id: "3c191808-6fe5-5a9b-9a68-a4ce4a672dbd"
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
```
Owner: `flex-auth` to request and document; `secrets-engine` owns the answer.
- Ask secrets-engine for the authoritative action list over
`secret-catalog-lane` — the operations it will actually gate, not the
`secrets-engine.lifecycle/v1` example set.
- Record it in `docs/secrets-engine-action-vocabulary.md`, following
`docs/tenant-engine-action-vocabulary.md`.
- Record the subject classes that may appear, and which of them are service
accounts versus humans behind an approval.
Gate: every action in the vocabulary is named by secrets-engine, not inferred
here. An inferred action is a blocker, not a default.
Publish secrets-engine.catalog-lane.lifecycle v1 (FLEX-WP-0021 T01, T02) secrets-engine delivered the action vocabulary T01 asked for: twelve actions read out of cli.py, not the example vocabulary. The gate earned its keep -- four would have been inferred wrongly, and revoke, the obvious thirteenth, does not exist as an action at all. It gates as deactivate, which is also reached from lifecycle deactivate. docs/secrets-engine-action-vocabulary.md records the list and the four traps. examples/secrets-engine/ carries the package, both manifests, a loadable registry snapshot, 26 fixtures, five check requests, and a README. validate -kind policy is valid with 22/22 Rego tests and 26/26 fixtures. allow_ttl is 15m, stated in the package rather than inherited from the engine default: these decisions authorize live secret operations, so the reliance window belongs where a reviewer sees it. Per FLEX-DEC-2026-004 it is authority to issue, not authority to keep using what the operation produced. destroy is the dual-control case, gated on a context.approval claim marked approved with two distinct approvers, repeated entries counting once. flex-auth checks what the claim says and deliberately does not re-derive its temporal validity, signature, or supersession -- those are approval-engine's to assert and the PEP's to verify against the live claim, per the split accepted in FLEX-DEC-2026-006. It stays in the package though its handler raises before the gate, so the rule is reviewed and fixtured before SECRETS-WP-0007-T04 opens the path. Following the FLEX-WP-0010-T02 precedent the denial ladder has no action_not_granted branch: one subject holding all twelve actions could never reach it, and a rule that cannot fail reads as control that is not there. Registering a second calling identity is the revisit trigger. Not deployed. No flex-auth-secrets-engine pin exists yet (T04), so their policy pin stays unset and fail-closed until T05. 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 08:02:52 +02:00
**Done 2026-09-06.** secrets-engine delivered twelve actions in their
`docs/gated-actions.md`, read out of `cli.py`: `apply`, `provision`, `rotate`,
`verify`, `handoff`, `wrap`, `exec`, `deactivate`, `suspend`, `destroy`,
`compromise`, `reactivate`. Recorded in
`docs/secrets-engine-action-vocabulary.md`. The gate earned its keep: four
would have been inferred wrongly, and `revoke` — the obvious thirteenth — does
not exist as an action at all.
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
## 2. Publish `secrets-engine.catalog-lane.lifecycle` v1
```task
id: FLEX-WP-0021-T02
Publish secrets-engine.catalog-lane.lifecycle v1 (FLEX-WP-0021 T01, T02) secrets-engine delivered the action vocabulary T01 asked for: twelve actions read out of cli.py, not the example vocabulary. The gate earned its keep -- four would have been inferred wrongly, and revoke, the obvious thirteenth, does not exist as an action at all. It gates as deactivate, which is also reached from lifecycle deactivate. docs/secrets-engine-action-vocabulary.md records the list and the four traps. examples/secrets-engine/ carries the package, both manifests, a loadable registry snapshot, 26 fixtures, five check requests, and a README. validate -kind policy is valid with 22/22 Rego tests and 26/26 fixtures. allow_ttl is 15m, stated in the package rather than inherited from the engine default: these decisions authorize live secret operations, so the reliance window belongs where a reviewer sees it. Per FLEX-DEC-2026-004 it is authority to issue, not authority to keep using what the operation produced. destroy is the dual-control case, gated on a context.approval claim marked approved with two distinct approvers, repeated entries counting once. flex-auth checks what the claim says and deliberately does not re-derive its temporal validity, signature, or supersession -- those are approval-engine's to assert and the PEP's to verify against the live claim, per the split accepted in FLEX-DEC-2026-006. It stays in the package though its handler raises before the gate, so the rule is reviewed and fixtured before SECRETS-WP-0007-T04 opens the path. Following the FLEX-WP-0010-T02 precedent the denial ladder has no action_not_granted branch: one subject holding all twelve actions could never reach it, and a rule that cannot fail reads as control that is not there. Registering a second calling identity is the revisit trigger. Not deployed. No flex-auth-secrets-engine pin exists yet (T04), so their policy pin stays unset and fail-closed until T05. 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 08:02:52 +02:00
status: done
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
priority: high
state_hub_task_id: "38770813-dfcc-5711-bba3-0db50d3af135"
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
```
Owner: `flex-auth`.
- Write `examples/secrets-engine/policy_package.md` with
`id: secrets-engine.catalog-lane.lifecycle`, `version: v1`, and an explicit
`allow_ttl`. An omitted TTL takes the 15m engine default; `none`/`0s` denies
with `allow_lifetime_unstated` rather than granting standing access.
- Add `protected_system_manifest.yaml`, `subject_manifest.yaml`,
`resource_manifest.yaml`, and `registry_snapshot.json` for the namespace.
- Add allow and deny fixtures in `policy_fixtures.yaml` plus
`check_request_*.json`, covering at minimum: an allow per vocabulary action,
wrong-tenant deny, unknown-subject deny, and an approval-claim-absent deny.
- Add `examples/secrets-engine/README.md`.
Gate: `flex-auth validate` passes, the fixture tests pass, and the package
digest is stable across two runs.
Publish secrets-engine.catalog-lane.lifecycle v1 (FLEX-WP-0021 T01, T02) secrets-engine delivered the action vocabulary T01 asked for: twelve actions read out of cli.py, not the example vocabulary. The gate earned its keep -- four would have been inferred wrongly, and revoke, the obvious thirteenth, does not exist as an action at all. It gates as deactivate, which is also reached from lifecycle deactivate. docs/secrets-engine-action-vocabulary.md records the list and the four traps. examples/secrets-engine/ carries the package, both manifests, a loadable registry snapshot, 26 fixtures, five check requests, and a README. validate -kind policy is valid with 22/22 Rego tests and 26/26 fixtures. allow_ttl is 15m, stated in the package rather than inherited from the engine default: these decisions authorize live secret operations, so the reliance window belongs where a reviewer sees it. Per FLEX-DEC-2026-004 it is authority to issue, not authority to keep using what the operation produced. destroy is the dual-control case, gated on a context.approval claim marked approved with two distinct approvers, repeated entries counting once. flex-auth checks what the claim says and deliberately does not re-derive its temporal validity, signature, or supersession -- those are approval-engine's to assert and the PEP's to verify against the live claim, per the split accepted in FLEX-DEC-2026-006. It stays in the package though its handler raises before the gate, so the rule is reviewed and fixtured before SECRETS-WP-0007-T04 opens the path. Following the FLEX-WP-0010-T02 precedent the denial ladder has no action_not_granted branch: one subject holding all twelve actions could never reach it, and a rule that cannot fail reads as control that is not there. Registering a second calling identity is the revisit trigger. Not deployed. No flex-auth-secrets-engine pin exists yet (T04), so their policy pin stays unset and fail-closed until T05. 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 08:02:52 +02:00
**Done 2026-09-06.** `examples/secrets-engine/` carries the package
(`allow_ttl: 15m` stated explicitly), both manifests, a loadable
`registry_snapshot.json`, 26 fixtures, five standalone check requests, and a
README. `validate -kind policy` reports valid with 22/22 Rego tests and 26/26
fixtures passing. `destroy` is the dual-control case, gated on a
`context.approval` claim marked approved with two distinct approvers; flex-auth
checks what the claim says and does not re-derive its validity, signature, or
supersession (`FLEX-DEC-2026-006`). Per the `FLEX-WP-0010-T02` precedent the
denial ladder has no `action_not_granted` branch, because one subject holding
all twelve actions could never reach it.
fix(secrets-engine): v2 adds the tenant rule v1 never had secrets-engine.catalog-lane.lifecycle v1 contained no reference to input.tenant — not in well_formed, not in the denial ladder, not in a test. A rotate on lane:glas-primary under tenant:coulomb returned allow against the deployed package (decision:066e629bbf0c0924). Found while answering glas-harness's tenant-alignment request, which had asked for wrong-tenant denial evidence. There was none to return. Three covers failed the same way: every one of the 29 fixtures carried tenant:platform, so the suite could not report on the field; T02's own gate named "wrong-tenant deny" and was recorded done unmet; and the engine hashes tenant into request_digest but never compares it. Four other published packages carry the branch — this one was the outlier. v2 adds wrong_tenant above wrong_system, three Rego tests and three fixtures (28/28, 32/32). The absent-tenant test caught a second defect in the first draft: a bare input.tenant != comparison is undefined on a missing key, so the branch dropped and the ladder reported the wrong rung. request_tenant := object.get(input, "tenant", "") fixes it. v2 supersedes rather than amends v1 because the defect failed open: a consumer pinned to _VERSION=v1 would keep receiving allows with no signal the rule beneath the version string had changed. The earlier dual-control correction stayed at v1 because it denied everything. Replay envelopes regenerated at v2; both request_digest values are byte-identical, so secrets-engine's digest join needs no re-pinning. The sweep this prompted found tenant-engine unscoped on tenant as well — deployed, and verified allowing tenant:coulomb. Not the same fix: its request tenant names the target rather than the caller, so a constant would break it. Recorded and carried by FLEX-WP-0022 rather than patched unilaterally. FLEX-WP-0021 closes at T05; the pin still serves v1 until a redeploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aQMM1dPXaPiXVn6DwwtLd Assistant: claude-code Assistant-Model: opus Assistant-Process: 715613@bnt-lap001 Assistant-Session: fabd95c1-4c9e-4080-8849-8707ae025f80
2026-09-06 20:38:45 +02:00
**Reopened and re-closed 2026-09-06 at v2 — the stated gate had not been met.**
This task's gate named "wrong-tenant deny" among the required fixtures and the
package shipped without one, because it had no tenant rule at all: a `rotate`
under `tenant: tenant:coulomb` returned `allow` against the deployed v1
(`decision:066e629bbf0c0924`). Found while answering `glas-harness`'s
tenant-alignment request, which had asked for exactly that evidence. v2 adds
`wrong_tenant` above `wrong_system`, three Rego tests and three fixtures
(28/28 and 32/32 passing), and supersedes v1 rather than amending it because
the defect failed **open** — see `FLEX-DEC-2026-008`. The `T03` replay digests
are unchanged at v2; only `policy_version` and `policy_package_digest` moved.
The sweep this prompted found the same shape in `tenant-engine`
(`FLEX-WP-0022`).
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
## 3. Confirm the digest join against a real decision record
```task
id: FLEX-WP-0021-T03
Publish approval_binding_digest: a claim cannot name the request carrying it secrets-engine confirmed T03, and re-verifying against the regenerated destroy fixture found something neither repository can fix alone: a pdp_digest recorded at issue time can never equal the request_digest of a request that carries the claim in its context, because the claim is part of the context that is hashed. Embedding the claim changes the very digest the claim would need to name. Not fixture staleness. It holds for every dual-control request whose claim travels in context -- the shape GH-DEC-2026-008 had just ruled mandatory. Left unresolved that ruling was unimplementable for exactly the case it was written for, and destroy would have been permanently un-allowable in production, failing closed forever on a check that could never pass. flex-auth owns the canonical request digest, so the fix is ours. binding.approval_binding_digest is the same material with context.approval removed, emitted only when a claim was carried. An approval issued against a claim-free Check records that Check's request_digest; the claim-bearing request reproduces it here. DELIBERATELY ADDITIVE, and the reason matters. The tempting fix is to drop context.approval from request_digest entirely. That is wrong: request_digest is the replay identity, and two requests differing only in which approval was presented must not share one, because their decisions differ -- one allows, the other denies dual_control_required. Collapsing them would let an allow obtained with a valid claim be replayed against a request carrying none. So request_digest still covers the claim and still moves; approval_binding_digest deliberately does not, and is documented as not a replay identity. The tests assert the two functions DISAGREE on a claim-bearing request, which is approval-engine's formulation of how to defend a distinction that looks like duplication. The fixture now demonstrates the property rather than asserting it: its claim's pdp_digest equals the envelope's approval_binding_digest with pdp_path true, and changing the claim's contents moved request_digest while leaving approval_binding_digest untouched. Two files a consumer can diff. Also picked up approval-engine's new required binding.pdp_path via the cross-repo schema test added yesterday -- which is the test doing exactly what it was built for, one day later. T03 is done. secrets-engine's own digest-material defect, which our two real envelopes caught, is recorded in the workplan. 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 14:52:33 +02:00
status: done
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
priority: high
state_hub_task_id: "8f7e5cdd-777e-5f54-9b92-c72b79f65672"
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
```
Owner: `flex-auth` to emit; `secrets-engine` to verify.
- Emit a real `DecisionEnvelope` from the package above and hand it to
secrets-engine as the replay fixture for the join it built in `627810b`.
- Verify `binding.request_digest` reproduces as SHA-256 over canonical JSON of
tenant/subject/action/resource/context per `docs/canonical-request-digest.md`.
- Verify `provenance.registry_snapshot_digest` is present — §9.7.2 promotes it
to a conformance prerequisite and `FLEX-WP-0019` closed that gap.
Gate: secrets-engine confirms its validator accepts the real record unchanged.
A validator change on their side is their work-record, not closed from here.
Publish approval_binding_digest: a claim cannot name the request carrying it secrets-engine confirmed T03, and re-verifying against the regenerated destroy fixture found something neither repository can fix alone: a pdp_digest recorded at issue time can never equal the request_digest of a request that carries the claim in its context, because the claim is part of the context that is hashed. Embedding the claim changes the very digest the claim would need to name. Not fixture staleness. It holds for every dual-control request whose claim travels in context -- the shape GH-DEC-2026-008 had just ruled mandatory. Left unresolved that ruling was unimplementable for exactly the case it was written for, and destroy would have been permanently un-allowable in production, failing closed forever on a check that could never pass. flex-auth owns the canonical request digest, so the fix is ours. binding.approval_binding_digest is the same material with context.approval removed, emitted only when a claim was carried. An approval issued against a claim-free Check records that Check's request_digest; the claim-bearing request reproduces it here. DELIBERATELY ADDITIVE, and the reason matters. The tempting fix is to drop context.approval from request_digest entirely. That is wrong: request_digest is the replay identity, and two requests differing only in which approval was presented must not share one, because their decisions differ -- one allows, the other denies dual_control_required. Collapsing them would let an allow obtained with a valid claim be replayed against a request carrying none. So request_digest still covers the claim and still moves; approval_binding_digest deliberately does not, and is documented as not a replay identity. The tests assert the two functions DISAGREE on a claim-bearing request, which is approval-engine's formulation of how to defend a distinction that looks like duplication. The fixture now demonstrates the property rather than asserting it: its claim's pdp_digest equals the envelope's approval_binding_digest with pdp_path true, and changing the claim's contents moved request_digest while leaving approval_binding_digest untouched. Two files a consumer can diff. Also picked up approval-engine's new required binding.pdp_path via the cross-repo schema test added yesterday -- which is the test doing exactly what it was built for, one day later. T03 is done. secrets-engine's own digest-material defect, which our two real envelopes caught, is recorded in the workplan. 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 14:52:33 +02:00
**Done 2026-09-06.** `secrets-engine` confirmed both envelopes reproduce, and
running them found a defect in *their* digest join: they were hashing `id`,
`policy_version`, and `caring_context`, which the canonical digest excludes.
Their previously pinned constant had been computed with `id` in the material, so
it was wrong and its passing proved nothing — two real envelopes were what caught
it. That is the value of handing over a real record rather than a described one.
Re-verification then surfaced a structural finding resolved in
`FLEX-DEC-2026-007`: a `pdp_digest` recorded at issue cannot equal the
`request_digest` of a request carrying the claim in its hashed context.
`binding.approval_binding_digest` is now published for that comparison.
Original emission note follows.
`examples/secrets-engine/replay/` carries two real envelopes — a plain allow
(`rotate`, empty context) and the dual-control allow (`destroy` with a valid
approval-claim). All three digests plus `input_claim_digests.context` verified
identical across two runs; `id`, `decision_time`, and the `lifetime` bounds move
with the clock and are documented as unpinnable. Both fixtures are included
because `input_claim_digests.context` appears only for a non-empty context, so a
consumer asserting it is always present would pass on one and fail on the other.
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
## 4. Stand up the `flex-auth-secrets-engine` pin
```task
id: FLEX-WP-0021-T04
status: done
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
priority: high
state_hub_task_id: "f4e8709a-65dd-5172-97ae-e7c3432afb22"
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
```
Owner: `flex-auth`; deployment approval required.
- Add `deploy/flex-auth-secrets-engine.yaml` as a three-document manifest —
`Deployment`, `Service`, default-deny `NetworkPolicy` admitting ingress from
the secrets-engine workload only — following the two existing pins.
- Promote through the Railiance overlay (`railiance/app.toml`,
`charts/flex-auth`, `values/`), not by editing the flattened emergency files.
- Build in CI and deploy **by immutable digest**, never by tag. Do not roll the
other pins: they are pinned to their own digests precisely so one policy
change does not move another consumer.
- Start `callerAuth.mode` in `warn`. Flip to `enforce` only after secrets-engine
adopts a calling identity and its warn logs are clean, as `FLEX-WP-0016` did
for ops-warden.
Gate: the pin answers `POST /v1/check` for the secrets-engine workload and
default-denies every other source; the other two pins are unchanged.
Review the stance register's second row; record why T04 cannot proceed TWO FINDINGS, BOTH FROM READING OTHER REPOSITORIES RATHER THAN OURS. The §13.1 stance register has a second row. secrets-engine published pep-stance.yaml -- total over catalog stage plus unknown, runtime-read and pinned to SHIPPED_STANCE by test. SCOPE.md claimed ops-warden's was the estate's only published map and that the register's single row was itself the finding; that is no longer true and is corrected. docs/stance-register-review.md is the first exercise of the aggregate-divergence capability flex-auth claimed on 2026-08-29 and then recorded as unexercised because one row cannot diverge from anything. Three findings: The two maps take opposite stances on unknown -- ops-warden fail_open by versioned build profile under ADR-0009, secrets-engine fail_closed. Both conformant, neither a defect, and they disagree about the one case nobody planned for. Reported as an observation for gate-house's register, not as a request that either repository change: a PDP does not set a consumer's stance, and §9.3's two-owner split is our own finding. The rows are not comparable. ops-warden scopes by security-zone, secrets-engine by catalog-stage. §6.4 permits both, but the register cannot then answer what the estate's stance is for a z2 workload. Worth recording before a third row arrives. secrets-engine's map defines fail_closed in terms of a durable ActionAuthorization record, which GH-DEC-2026-005 shelved. The stance is unaffected -- only the artifact name is stale -- but the file is read at runtime and pinned by test, so the stale name outlives a comment. SEPARATELY, T04 IS BLOCKED AND THE REASON IS STRUCTURAL. The task assumed the ops-warden/tenant-engine/user-engine pattern, where the pin's default-deny NetworkPolicy admits one approved consumer workload. secrets-engine has no Kubernetes deployment at all -- it is a CLI. There is no pod selector to write, and inventing one would repeat the error corrected in T02. Three possible shapes recorded in the workplan and raised with them; callerAuth, not the NetworkPolicy, becomes the real boundary if an operator CLI is the caller, so the FLEX-WP-0016 precedent does not transfer unexamined. 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 09:31:40 +02:00
**Blocked 2026-09-06 — the consumer is not a workload.** This task was written
from the `ops-warden` / `tenant-engine` / `user-engine` pattern, where the pin's
default-deny `NetworkPolicy` admits ingress from exactly one approved consumer
*workload*. `secrets-engine` has no Kubernetes deployment at all: it is a CLI
(`src/`, `cli.py`, `pyproject.toml`) with no manifests, no namespace, and no pod
labels. There is no selector to write, and inventing one would be the same error
corrected in `FLEX-WP-0021-T02` — authoring against a shape that does not exist.
Three shapes are possible and the choice is not flex-auth's alone:
1. **Operator-run CLI reaching an in-cluster pin.** Needs a decided ingress
path (tunnel or port-forward), and `callerAuth` becomes the real boundary
rather than the `NetworkPolicy`, because the source address is an operator's
machine rather than a pod.
2. **A `secrets-engine` workload that does not exist yet.** Then this task is
correct as written but waits on `secrets-engine` to have one.
3. **No pin at all**, if the gate is only ever consulted from an operator
context that already holds a decision.
Raised with `secrets-engine`; T04 does not proceed until the shape is decided.
Note this also changes what `callerAuth.mode: warn` is warning about, so the
`FLEX-WP-0016` precedent does not transfer unexamined.
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
## 5. Hand the pin coordinates back and close
```task
id: FLEX-WP-0021-T05
fix(secrets-engine): v2 adds the tenant rule v1 never had secrets-engine.catalog-lane.lifecycle v1 contained no reference to input.tenant — not in well_formed, not in the denial ladder, not in a test. A rotate on lane:glas-primary under tenant:coulomb returned allow against the deployed package (decision:066e629bbf0c0924). Found while answering glas-harness's tenant-alignment request, which had asked for wrong-tenant denial evidence. There was none to return. Three covers failed the same way: every one of the 29 fixtures carried tenant:platform, so the suite could not report on the field; T02's own gate named "wrong-tenant deny" and was recorded done unmet; and the engine hashes tenant into request_digest but never compares it. Four other published packages carry the branch — this one was the outlier. v2 adds wrong_tenant above wrong_system, three Rego tests and three fixtures (28/28, 32/32). The absent-tenant test caught a second defect in the first draft: a bare input.tenant != comparison is undefined on a missing key, so the branch dropped and the ladder reported the wrong rung. request_tenant := object.get(input, "tenant", "") fixes it. v2 supersedes rather than amends v1 because the defect failed open: a consumer pinned to _VERSION=v1 would keep receiving allows with no signal the rule beneath the version string had changed. The earlier dual-control correction stayed at v1 because it denied everything. Replay envelopes regenerated at v2; both request_digest values are byte-identical, so secrets-engine's digest join needs no re-pinning. The sweep this prompted found tenant-engine unscoped on tenant as well — deployed, and verified allowing tenant:coulomb. Not the same fix: its request tenant names the target rather than the caller, so a constant would break it. Recorded and carried by FLEX-WP-0022 rather than patched unilaterally. FLEX-WP-0021 closes at T05; the pin still serves v1 until a redeploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aQMM1dPXaPiXVn6DwwtLd Assistant: claude-code Assistant-Model: opus Assistant-Process: 715613@bnt-lap001 Assistant-Session: fabd95c1-4c9e-4080-8849-8707ae025f80
2026-09-06 20:38:45 +02:00
status: done
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
priority: medium
state_hub_task_id: "f0828871-fd65-5d8c-adfd-28b13fedd2b0"
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
```
Owner: `flex-auth`.
- Reply to secrets-engine with the Service DNS, the package id and version to
set in `SECRETS_ENGINE_AUTHORIZATION_POLICY_PACKAGE` / `_VERSION`, and the
`callerAuth` mode currently in force.
- Update `SCOPE.md` to list secrets-engine among the shipped consumers, and log
a progress event.
Gate: secrets-engine can set its required configuration to published values and
reach a pin; nothing about the fallback-free shape of that configuration changed.
fix(secrets-engine): v2 adds the tenant rule v1 never had secrets-engine.catalog-lane.lifecycle v1 contained no reference to input.tenant — not in well_formed, not in the denial ladder, not in a test. A rotate on lane:glas-primary under tenant:coulomb returned allow against the deployed package (decision:066e629bbf0c0924). Found while answering glas-harness's tenant-alignment request, which had asked for wrong-tenant denial evidence. There was none to return. Three covers failed the same way: every one of the 29 fixtures carried tenant:platform, so the suite could not report on the field; T02's own gate named "wrong-tenant deny" and was recorded done unmet; and the engine hashes tenant into request_digest but never compares it. Four other published packages carry the branch — this one was the outlier. v2 adds wrong_tenant above wrong_system, three Rego tests and three fixtures (28/28, 32/32). The absent-tenant test caught a second defect in the first draft: a bare input.tenant != comparison is undefined on a missing key, so the branch dropped and the ladder reported the wrong rung. request_tenant := object.get(input, "tenant", "") fixes it. v2 supersedes rather than amends v1 because the defect failed open: a consumer pinned to _VERSION=v1 would keep receiving allows with no signal the rule beneath the version string had changed. The earlier dual-control correction stayed at v1 because it denied everything. Replay envelopes regenerated at v2; both request_digest values are byte-identical, so secrets-engine's digest join needs no re-pinning. The sweep this prompted found tenant-engine unscoped on tenant as well — deployed, and verified allowing tenant:coulomb. Not the same fix: its request tenant names the target rather than the caller, so a constant would break it. Recorded and carried by FLEX-WP-0022 rather than patched unilaterally. FLEX-WP-0021 closes at T05; the pin still serves v1 until a redeploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aQMM1dPXaPiXVn6DwwtLd Assistant: claude-code Assistant-Model: opus Assistant-Process: 715613@bnt-lap001 Assistant-Session: fabd95c1-4c9e-4080-8849-8707ae025f80
2026-09-06 20:38:45 +02:00
**Done 2026-09-06, with one coordinate that is not flex-auth's to supply.**
Handed back to `secrets-engine` and `glas-harness`:
```text
fix: the address we published was a misdirection, and the channel is unauthenticated secrets-engine probed the Service DNS name handed over in FLEX-WP-0021-T05 and found it resolves, from the workstation, to an unrelated public host. Reproduced here: search ad.binect.de answers wildcard, so flex-auth-secrets-engine.flex-auth.svc.cluster.local and this-service-does-not-exist.flex-auth.svc.cluster.local both resolve to 80.158.43.29, while the trailing-dot FQDN correctly fails. A bare Service name in a handover is not merely unreachable from there, it is a live misdirection, and the handover was ours. Had a deployment pointed at it, the CheckRequest body would have gone to that host: subject, tenant, lane and resource ids, stage, field names, purpose, plus the caller's bearer token. Trailing-dot FQDN and "in-cluster only" now replace the bare name in the example README, SCOPE.md, and the T05 note. Their real question was how the response channel is authenticated, and they declined to answer it locally because choosing a transport control for our service is not a consumer's call. Right boundary, so the answer is recorded here as FLEX-DEC-2026-010: it is not authenticated. Pins serve plain HTTP, the envelope carries no signature, and a responder that knows the package id and version can return a well-formed allow that passes every check a consumer performs. The part worth stating in the contract is that the digests do not help and look like they do. Every input to request_digest, policy_package_digest and registry_snapshot_digest is either sent by the caller or published in this repo, so a forger reproduces all three exactly. They establish integrity of the binding, never authenticity of the source — and publishing more digests makes a forged envelope look more authenticated, not less. For secrets-engine specifically: fail-closed protects against a PDP that is absent, not against one that lies. An unreachable PDP denies; a lying PDP allows. Third instance of one seam in three decisions. 008: a tenant carried into the digest and never compared — visible, not enforced. 009: a caller authenticated and never recorded — enforced, not visible. 010: a record verifiable and unauthentic — checkable, but not evidence. One nuance that changes the operator recommendation: kubectl port-forward does authenticate the responder, transitively — no DNS name, one named pod, API-server TLS. That is the exact reverse of the caller direction, where it bypasses the NetworkPolicy. Independent properties pointing opposite ways, so neither can be summarised as "the network protects it". FLEX-WP-0024 carries signing; key custody routes through warden/OpenBao rather than minting a key here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aQMM1dPXaPiXVn6DwwtLd Assistant: claude-code Assistant-Model: opus Assistant-Process: 715613@bnt-lap001 Assistant-Session: fabd95c1-4c9e-4080-8849-8707ae025f80
2026-09-06 22:44:45 +02:00
Service: http://flex-auth-secrets-engine.flex-auth.svc.cluster.local.:8080
fix(secrets-engine): v2 adds the tenant rule v1 never had secrets-engine.catalog-lane.lifecycle v1 contained no reference to input.tenant — not in well_formed, not in the denial ladder, not in a test. A rotate on lane:glas-primary under tenant:coulomb returned allow against the deployed package (decision:066e629bbf0c0924). Found while answering glas-harness's tenant-alignment request, which had asked for wrong-tenant denial evidence. There was none to return. Three covers failed the same way: every one of the 29 fixtures carried tenant:platform, so the suite could not report on the field; T02's own gate named "wrong-tenant deny" and was recorded done unmet; and the engine hashes tenant into request_digest but never compares it. Four other published packages carry the branch — this one was the outlier. v2 adds wrong_tenant above wrong_system, three Rego tests and three fixtures (28/28, 32/32). The absent-tenant test caught a second defect in the first draft: a bare input.tenant != comparison is undefined on a missing key, so the branch dropped and the ladder reported the wrong rung. request_tenant := object.get(input, "tenant", "") fixes it. v2 supersedes rather than amends v1 because the defect failed open: a consumer pinned to _VERSION=v1 would keep receiving allows with no signal the rule beneath the version string had changed. The earlier dual-control correction stayed at v1 because it denied everything. Replay envelopes regenerated at v2; both request_digest values are byte-identical, so secrets-engine's digest join needs no re-pinning. The sweep this prompted found tenant-engine unscoped on tenant as well — deployed, and verified allowing tenant:coulomb. Not the same fix: its request tenant names the target rather than the caller, so a constant would break it. Recorded and carried by FLEX-WP-0022 rather than patched unilaterally. FLEX-WP-0021 closes at T05; the pin still serves v1 until a redeploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aQMM1dPXaPiXVn6DwwtLd Assistant: claude-code Assistant-Model: opus Assistant-Process: 715613@bnt-lap001 Assistant-Session: fabd95c1-4c9e-4080-8849-8707ae025f80
2026-09-06 20:38:45 +02:00
Package: secrets-engine.catalog-lane.lifecycle
Version: v2 <- not v1; v1 is deployed and superseded
callerAuth.mode: warn (not enforced caller authentication)
```
fix: the address we published was a misdirection, and the channel is unauthenticated secrets-engine probed the Service DNS name handed over in FLEX-WP-0021-T05 and found it resolves, from the workstation, to an unrelated public host. Reproduced here: search ad.binect.de answers wildcard, so flex-auth-secrets-engine.flex-auth.svc.cluster.local and this-service-does-not-exist.flex-auth.svc.cluster.local both resolve to 80.158.43.29, while the trailing-dot FQDN correctly fails. A bare Service name in a handover is not merely unreachable from there, it is a live misdirection, and the handover was ours. Had a deployment pointed at it, the CheckRequest body would have gone to that host: subject, tenant, lane and resource ids, stage, field names, purpose, plus the caller's bearer token. Trailing-dot FQDN and "in-cluster only" now replace the bare name in the example README, SCOPE.md, and the T05 note. Their real question was how the response channel is authenticated, and they declined to answer it locally because choosing a transport control for our service is not a consumer's call. Right boundary, so the answer is recorded here as FLEX-DEC-2026-010: it is not authenticated. Pins serve plain HTTP, the envelope carries no signature, and a responder that knows the package id and version can return a well-formed allow that passes every check a consumer performs. The part worth stating in the contract is that the digests do not help and look like they do. Every input to request_digest, policy_package_digest and registry_snapshot_digest is either sent by the caller or published in this repo, so a forger reproduces all three exactly. They establish integrity of the binding, never authenticity of the source — and publishing more digests makes a forged envelope look more authenticated, not less. For secrets-engine specifically: fail-closed protects against a PDP that is absent, not against one that lies. An unreachable PDP denies; a lying PDP allows. Third instance of one seam in three decisions. 008: a tenant carried into the digest and never compared — visible, not enforced. 009: a caller authenticated and never recorded — enforced, not visible. 010: a record verifiable and unauthentic — checkable, but not evidence. One nuance that changes the operator recommendation: kubectl port-forward does authenticate the responder, transitively — no DNS name, one named pod, API-server TLS. That is the exact reverse of the caller direction, where it bypasses the NetworkPolicy. Independent properties pointing opposite ways, so neither can be summarised as "the network protects it". FLEX-WP-0024 carries signing; key custody routes through warden/OpenBao rather than minting a key here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aQMM1dPXaPiXVn6DwwtLd Assistant: claude-code Assistant-Model: opus Assistant-Process: 715613@bnt-lap001 Assistant-Session: fabd95c1-4c9e-4080-8849-8707ae025f80
2026-09-06 22:44:45 +02:00
**Corrected 2026-09-06: the address originally handed over was a bare Service
name, which is a live misdirection from a workstation.** `secrets-engine` probed
it and flex-auth reproduced: `search ad.binect.de` expands any
`*.svc.cluster.local` name to one unrelated public host, including names for
services that do not exist. The trailing dot is required and the address is
in-cluster only. `FLEX-WP-0024` carries the consequence — a published address a
consumer can be misdirected from, reaching an unsigned decision envelope over
plain HTTP.
fix(secrets-engine): v2 adds the tenant rule v1 never had secrets-engine.catalog-lane.lifecycle v1 contained no reference to input.tenant — not in well_formed, not in the denial ladder, not in a test. A rotate on lane:glas-primary under tenant:coulomb returned allow against the deployed package (decision:066e629bbf0c0924). Found while answering glas-harness's tenant-alignment request, which had asked for wrong-tenant denial evidence. There was none to return. Three covers failed the same way: every one of the 29 fixtures carried tenant:platform, so the suite could not report on the field; T02's own gate named "wrong-tenant deny" and was recorded done unmet; and the engine hashes tenant into request_digest but never compares it. Four other published packages carry the branch — this one was the outlier. v2 adds wrong_tenant above wrong_system, three Rego tests and three fixtures (28/28, 32/32). The absent-tenant test caught a second defect in the first draft: a bare input.tenant != comparison is undefined on a missing key, so the branch dropped and the ladder reported the wrong rung. request_tenant := object.get(input, "tenant", "") fixes it. v2 supersedes rather than amends v1 because the defect failed open: a consumer pinned to _VERSION=v1 would keep receiving allows with no signal the rule beneath the version string had changed. The earlier dual-control correction stayed at v1 because it denied everything. Replay envelopes regenerated at v2; both request_digest values are byte-identical, so secrets-engine's digest join needs no re-pinning. The sweep this prompted found tenant-engine unscoped on tenant as well — deployed, and verified allowing tenant:coulomb. Not the same fix: its request tenant names the target rather than the caller, so a constant would break it. Recorded and carried by FLEX-WP-0022 rather than patched unilaterally. FLEX-WP-0021 closes at T05; the pin still serves v1 until a redeploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aQMM1dPXaPiXVn6DwwtLd Assistant: claude-code Assistant-Model: opus Assistant-Process: 715613@bnt-lap001 Assistant-Session: fabd95c1-4c9e-4080-8849-8707ae025f80
2026-09-06 20:38:45 +02:00
`SCOPE.md` lists secrets-engine among the shipped consumers and
`examples/secrets-engine/README.md` carries the coordinates at source.
Two things are stated rather than closed, because closing them is not ours:
1. **The pin serves v1 until a redeploy lands.** The over-permissive package is
live now. Deployment approval is the user's; `FLEX-DEC-2026-008` records the
defect and does not grant it.
2. **There is still no verified access path for a workstation CLI.** Ingress
admits namespace `secrets-engine` with pod label
`app.kubernetes.io/name=secrets-engine`; a CLI on an operator's machine is
not that, and Service DNS is not workstation connectivity. `T04`'s three
shapes remain undecided, and `glas-harness` named the same gap independently.
`secrets-engine` cannot complete adoption on coordinates alone.
The fallback-free, fail-closed shape of
`SECRETS_ENGINE_AUTHORIZATION_POLICY_PACKAGE` / `_VERSION` is unchanged.
## Production deployment — 2026-09-06
User authorized production deployment in the Glas session. Installed dedicated
Helm release `flex-auth-secrets-engine`, namespace `flex-auth`, revision 1,
using values/secrets-engine.yaml. CI tag main-dd3ce4c resolves to immutable
digest sha256:89086c02c74a931068423e70937d03df7850fa0db9c63e70be56b3558f1756af.
Chart lint and server dry-run passed; deployment available 1/1. Existing three
consumer deployment specifications were compared before/after and unchanged.
Five published requests returned two allows and three expected denies via
local port-forward. Network probes: namespace secrets-engine with pod label
app.kubernetes.io/name=secrets-engine succeeded after initial propagation
retry; wrong pod label and wrong namespace remained denied across retries.
All six temporary probes and the port-forward were removed. Created the
secrets-engine namespace for the intended consumer identity; no workload or
credential was installed there.
Service: http://flex-auth-secrets-engine.flex-auth.svc.cluster.local:8080.
Policy: secrets-engine.catalog-lane.lifecycle / v1. Caller authentication is
warn, per this task's rollout plan; this is not enforced caller authentication.
Network ingress admits only the specified namespace/pod selector. T05 remains
waiting on consumer configuration/adoption and the owner handoff. Approval
service, KeyCape clients and real credential-lane activation are not supplied
by this deployment. First-install rollback is removal of this dedicated Helm
release, leaving the three existing consumers untouched.
## v2 production correction applied — 2026-09-06
Glas applied the existing operator production authorization to correct the
reported v1 tenant fail-open. Helm revision 2 uses CI main-d98323b digest
sha256:db1c4f7e621c7ea119489a321d7db0e05da09afc17be5f69d873b2b3c7f60cfc.
Lint/server dry-run/rollout passed. Six published Check requests on the live
service returned expected two allows/four denies, including wrong_tenant;
all matched_policy_version values are v2. Other consumer Deployment specs
are unchanged. Values pin updated. Caller authentication remains warn;
workstation consumer access/adoption remains outstanding despite T05 closure.
Glas has sent a follow-up requesting an explicit live owner work record for
that gate. Do not roll back to known over-permissive v1; stop this dedicated
release if v2 cannot be served.