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
|
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: active
|
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"
|
2026-09-06 01:14:52 +02:00
|
|
|
state_hub_workstream_id: "b01f655e-f71a-50ae-b110-178557f07c63"
|
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
|
2026-09-06 01:12:50 +02:00
|
|
|
priority: high
|
2026-09-06 01:14:52 +02:00
|
|
|
state_hub_task_id: "3c191808-6fe5-5a9b-9a68-a4ce4a672dbd"
|
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.
|
|
|
|
|
|
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
|
2026-09-06 01:12:50 +02:00
|
|
|
priority: high
|
2026-09-06 01:14:52 +02:00
|
|
|
state_hub_task_id: "38770813-dfcc-5711-bba3-0db50d3af135"
|
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.
|
|
|
|
|
|
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
|
2026-09-06 01:12:50 +02:00
|
|
|
priority: high
|
2026-09-06 01:14:52 +02:00
|
|
|
state_hub_task_id: "8f7e5cdd-777e-5f54-9b92-c72b79f65672"
|
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.
|
2026-09-06 08:14:14 +02:00
|
|
|
`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.
|
|
|
|
|
|
2026-09-06 01:12:50 +02:00
|
|
|
## 4. Stand up the `flex-auth-secrets-engine` pin
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
id: FLEX-WP-0021-T04
|
|
|
|
|
status: wait
|
|
|
|
|
priority: high
|
2026-09-06 01:14:52 +02:00
|
|
|
state_hub_task_id: "f4e8709a-65dd-5172-97ae-e7c3432afb22"
|
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.
|
|
|
|
|
|
2026-09-06 01:12:50 +02:00
|
|
|
## 5. Hand the pin coordinates back and close
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
id: FLEX-WP-0021-T05
|
|
|
|
|
status: wait
|
|
|
|
|
priority: medium
|
2026-09-06 01:14:52 +02:00
|
|
|
state_hub_task_id: "f0828871-fd65-5d8c-adfd-28b13fedd2b0"
|
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.
|