Answer ops-warden and secrets-engine; open FLEX-WP-0021
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 1s

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
This commit is contained in:
tegwick 2026-09-06 01:12:50 +02:00
parent 56940727bf
commit 6a6464fcc9
4 changed files with 659 additions and 15 deletions

View file

@ -537,3 +537,208 @@ questions already carry the same content as questions rather than as a grade.
conformance prerequisite under §9.7.2, and will be planned as one.
- flex-auth offers the canonical request digest as the §6.4.2 replay test and
offers to publish the decision-record schema as its own contract.
## FLEX-DEC-2026-004 — Decision lifetime does not reach past issuance: answer to ops-warden WARDEN-WP-0034-T05
```yaml
id: FLEX-DEC-2026-004
kind: decision
title: 'Decision lifetime does not reach past issuance: answer to ops-warden WARDEN-WP-0034-T05'
status: resolved
origin: cross-repo
origin_ref: WARDEN-WP-0034-T05
owner: flex-auth
affects:
- flex-auth
- ops-warden
requested_dispositions:
- answer
- decline
decided_by: flex-auth (access-engine / PDP)
rationale: 'Answered, not declined. A decision lifetime shorter than the SSH certificate
TTL is meaningful, but only as an authority-to-issue window, never as an authority-to-use
window over an already-issued certificate. flex-auth lifetime.expires_at bounds
how long that one allow may be relied on to authorise a sign; it cannot bound an
artifact ops-warden issued under it, and flex-auth does not claim it does. The downstream
contract that consumes the shorter lifetime is the pre-sign gate itself: no replay
of an allow past expires_at, and a fresh Check per sign. The lever that actually
shortens effective access is the requested TTL as a policy input, which is already
deployed as the ttl_out_of_bounds deny; ops-warden section 9.7.2 window through
cert TTL is correctly stated and correctly owned by the PEP.'
created: '2026-09-05T23:08:38.877787Z'
updated: '2026-09-06T00:00:00.000000Z'
decided_at: '2026-09-06T00:00:00.000000Z'
```
## Context
ops-warden now states its §9.7.2 revocation window explicitly: an allow can
remain effective through an already-issued SSH certificate until TTL expiry
(`adm` 48h, `agt` 24h, `atm` 8h), because there is no CRL/KRL recall channel.
It asked flex-auth, as the access engine, whether a decision lifetime shorter
than the certificate TTL is meaningful under that PEP behaviour, and if so what
downstream contract should consume the shorter lifetime.
## Disposition
**Answered, not declined.** The lifetime is meaningful, and the mismatch
ops-warden noticed is not a defect on either side. It is two different objects
with two different revocation stories.
### A decision lifetime is authority to issue, not authority to use
`lifetime.expires_at` (`docs/decision-record-contract.md`, default TTL 15m)
bounds one thing: how long that particular allow may be relied on as the
authority for the action it decided. For ops-warden the action is `sign`, and
the action completes at issuance. Once the certificate exists, the decision has
been consumed; nothing in the decision record reaches the artifact.
flex-auth does not claim otherwise, and a PDP that did would be lying. The
estate already ruled that fail-open is not expressible by a PDP because there
is no evaluator in that path; recall of an issued credential is the same shape
of claim. There is no flex-auth in the path of an SSH session already
authenticated by a valid certificate.
So a 15m decision lifetime against a 48h `adm` certificate is not an
inconsistency to reconcile. It says the gate's answer goes stale in 15 minutes;
it says nothing about the certificate, and was never asked to.
### The downstream contract is the pre-sign gate itself
That is the only consumer of the shorter lifetime, and it consumes it two ways:
1. **No replay past `expires_at`.** An allow may not be reused to authorise a
second `warden sign`, and a cached verdict must not outlive its TTL. This is
already the published rule (`docs/decision-input-freshness.md`, "How to read
this as a consumer") and it is the whole of what the lifetime binds.
2. **Fresh Check per sign.** Because the lifetime is short and claims are not
cached PDP-side, revocation of a subject, actor, or approval claim is visible
to the *next* sign immediately — deadline 0 for the claim class. That is the
real value of the short lifetime: it bounds how long a revoked principal can
keep obtaining *new* certificates, which is the part flex-auth can bound.
### The lever that shortens effective access is already deployed
If the goal is to shrink the window in which a revoked principal retains live
access, the mechanism is not a shorter decision lifetime — it is a shorter
**certificate** TTL, and the requested TTL is already a policy input. The
shipped `ops-warden.ssh-certificate.sign` package denies `ttl_out_of_bounds`
before OpenBao is reached (verified 2026-06-29). Capping the requested TTL per
actor class or per zone in the policy package is a policy change flex-auth can
make and explain; asking the decision lifetime to reach past issuance is not.
flex-auth is not proposing that change here. Certificate TTLs are ops-warden's
to set, and this record does not reopen them.
## Consequences
- ops-warden's §9.7.2 statement — allow effective through certificate TTL,
bounded by 48h/24h/8h, no recall channel — is **correct as written and
correctly owned by the PEP**. flex-auth does not want that residue moved to
the PDP's side of the line.
- No change to `flex-auth.decision-record.v1`. `lifetime` keeps its published
meaning; this record states the boundary it already had rather than adding
one.
- If ops-warden ever wants a certificate TTL bound to policy rather than to
actor class, that is a policy-package change under a new workplan, not a
lifetime semantics change.
## FLEX-DEC-2026-005 — secrets-engine policy package is expected but unpublished; /v1/check has no estate-wide endpoint by design
```yaml
id: FLEX-DEC-2026-005
kind: decision
title: secrets-engine policy package is expected but unpublished; /v1/check has no
estate-wide endpoint by design
status: resolved
origin: cross-repo
origin_ref: secrets-engine SECRETS-WP-0009-T03
owner: flex-auth
affects:
- flex-auth
- secrets-engine
- approval-engine
requested_dispositions:
- answer
decided_by: flex-auth (access-engine / PDP)
rationale: 'Two answers. (1) Yes, a real package is expected, and flex-auth authors
it in this repo as it did for every other consumer; the reserved coordinate is secrets-engine.catalog-lane.lifecycle
at version v1, and it does not exist yet. Until it is published and pinned, secrets-engine
keeping SECRETS_ENGINE_AUTHORIZATION_POLICY_PACKAGE and _VERSION as required configuration
with no fallback is the correct shape and flex-auth endorses it. (2) POST /v1/check
is deployed, but there is no estate-wide PDP address and there is not meant to be
one: each consumer gets its own cluster-local pin whose NetworkPolicy default-denies
ingress except from that one approved workload. The 2026-09-06 probe finding no
reachable PDP is the design working, not an outage. A reachable endpoint for secrets-engine
is a per-consumer pin that follows its policy package.'
created: '2026-09-05T23:08:51.144287Z'
updated: '2026-09-06T00:00:00.000000Z'
decided_at: '2026-09-06T00:00:00.000000Z'
```
## Context
secrets-engine applied flex-auth's earlier PDP answers in commit `627810b`
(`resource.type: secret-catalog-lane`, `resource.system: secrets-engine`,
catalog id as `request.resource.id`, and a join that validates
`ActionAuthorization.decision.binding.request_digest` against
`flex-auth.decision-record.v1`). It then asked two questions, with Glas
real-key execution (`SECRETS-WP-0009-T03`) waiting on the answers.
## Disposition
### Answer 1 — yes, a package is expected; it does not exist yet
Every shipped consumer's policy package is authored **in this repository**, not
in the consumer's, and baked into the image: `ops-warden.ssh-certificate.sign`
(v2), `tenant-engine.write-api.mutate`, `user-engine.portal.authorize`,
`railiance-platform.credential-grant.issue`, `qonto-assistant.finance-read`.
secrets-engine follows the same path. The reserved coordinate is:
| | |
| --- | --- |
| Package | `secrets-engine.catalog-lane.lifecycle` |
| Version | `v1` |
This is a **reservation, not a publication.** No such package exists in
`examples/` today, and until one is published and pinned, a Check naming it
evaluates nothing. `FLEX-WP-0021` carries the work.
secrets-engine's decision not to default the pin was the right call and
flex-auth endorses it explicitly: keeping
`SECRETS_ENGINE_AUTHORIZATION_POLICY_PACKAGE` and `_VERSION` as required
configuration with no fallback is exactly correct. Defaulting them would have
pinned production to example vocabulary nobody publishes, and the failure would
have been a silent evaluation against a package that is not there rather than a
loud missing-configuration error.
### Answer 2 — /v1/check is deployed; there is no estate-wide address, by design
The 2026-09-06 probe finding no reachable PDP found the design working.
flex-auth runs as **per-consumer cluster-local pins**, not as one shared
service. Three are live (`flex-auth-ops-warden`, `flex-auth-tenant-engine`,
`flex-auth-user-engine`). Each is a three-document manifest whose third document
is a default-deny `NetworkPolicy` admitting ingress from exactly one approved
consumer workload, and each is pinned to its own image digest so one consumer's
policy can roll without moving another's (`deploy/README.md`). Caller
authentication is per-pin: on the ops-warden pin it is `enforce`, so an
anonymous `/v1/check` is 401 and a token bound to another protected system is
403 (`FLEX-WP-0016`).
There is therefore no address for secrets-engine to reach *because none has been
created for it*. A reachable endpoint is a per-consumer pin, and a pin follows
its policy package — so both questions resolve to the same piece of work, in
that order: publish the package, then pin it.
## Consequences
- `FLEX-WP-0021` is opened in this repo to publish
`secrets-engine.catalog-lane.lifecycle` v1 and stand up a
`flex-auth-secrets-engine` pin. It needs secrets-engine's real action
vocabulary as input; the example `secrets-engine.lifecycle/v1` vocabulary is
explicitly not it.
- secrets-engine should keep the pin required and unset until the package is
published. Nothing in this record authorises a fallback value.
- No change to `flex-auth.decision-record.v1`. The digest join secrets-engine
built against it is correct as described.