92 lines
4.1 KiB
Markdown
92 lines
4.1 KiB
Markdown
|
|
# secrets-engine action vocabulary
|
||
|
|
|
||
|
|
Status: published
|
||
|
|
Date: 2026-09-06
|
||
|
|
Workplan: `FLEX-WP-0021-T01`
|
||
|
|
Source: secrets-engine `docs/gated-actions.md`, read out of their `cli.py`
|
||
|
|
Package: `secrets-engine.catalog-lane.lifecycle` v1
|
||
|
|
(`examples/secrets-engine/policy_package.md`)
|
||
|
|
|
||
|
|
`FLEX-WP-0021-T01` required secrets-engine to **name** this list rather than
|
||
|
|
letting flex-auth derive it, and made an inferred action a blocker rather than
|
||
|
|
a default. That was not ceremony: four of the twelve would have been inferred
|
||
|
|
wrongly, and one action that looks obvious does not exist at all.
|
||
|
|
|
||
|
|
The example vocabulary `secrets-engine.lifecycle/v1` this replaces is **not**
|
||
|
|
this list and was never published (`FLEX-DEC-2026-005`).
|
||
|
|
|
||
|
|
## Request shape
|
||
|
|
|
||
|
|
| Field | Value |
|
||
|
|
| --- | --- |
|
||
|
|
| `resource.type` | `secret-catalog-lane` |
|
||
|
|
| `resource.system` | `secrets-engine` |
|
||
|
|
| `resource.id` | the catalog id |
|
||
|
|
| `resource.attributes.stage` | deployment stage |
|
||
|
|
| `resource.attributes.fields` | sorted; populated for `provision`, `rotate`, `verify`, `exec`; empty list otherwise |
|
||
|
|
| `resource.attributes.policy_targets` / `auth_targets` | sorted |
|
||
|
|
| `context.approval` | approval claim; required for `destroy` only |
|
||
|
|
|
||
|
|
Empty lists are sent rather than omitted or guessed.
|
||
|
|
|
||
|
|
## The twelve actions
|
||
|
|
|
||
|
|
| Action | Reaches OpenBao | Notes |
|
||
|
|
| --- | --- | --- |
|
||
|
|
| `apply` | yes | `apply --dry-run` does not reach the gate |
|
||
|
|
| `provision` | yes | carries `fields` |
|
||
|
|
| `rotate` | yes | carries `fields` |
|
||
|
|
| `verify` | yes | carries `fields` |
|
||
|
|
| `handoff` | yes | |
|
||
|
|
| `wrap` | yes | |
|
||
|
|
| `exec` | yes | carries `fields` |
|
||
|
|
| `deactivate` | yes | also the gate for the CLI verb `revoke` |
|
||
|
|
| `suspend` | yes | |
|
||
|
|
| `destroy` | yes | dual control; not reachable live yet |
|
||
|
|
| `compromise` | no | local delivery overlay only |
|
||
|
|
| `reactivate` | no | local delivery overlay only |
|
||
|
|
|
||
|
|
## Four things an inferred list gets wrong
|
||
|
|
|
||
|
|
**1. `revoke` is not an action.** The CLI verb `revoke` gates as `deactivate`,
|
||
|
|
which is also reached from `lifecycle deactivate`. There is no `revoke` value
|
||
|
|
and one must not be added. The package asserts this
|
||
|
|
(`test_revoke_is_not_an_action` → `unknown_action`).
|
||
|
|
|
||
|
|
**2. `destroy` is defined but not reachable live.** Its handler raises before
|
||
|
|
the gate, so only `--dry-run` renders. It stays in the vocabulary as the
|
||
|
|
dual-control case; no live `destroy` Check arrives until
|
||
|
|
`SECRETS-WP-0007-T04` lands. Keeping it in the package now means the rule is
|
||
|
|
reviewed and fixtured *before* the path opens rather than in the same change
|
||
|
|
that opens it.
|
||
|
|
|
||
|
|
**3. `compromise` and `reactivate` touch no OpenBao object.** They mutate local
|
||
|
|
delivery-overlay state only. They are gated because they change delivery
|
||
|
|
posture, not because they write to the backend — a distinction worth recording,
|
||
|
|
because a reviewer looking for backend writes would otherwise read their
|
||
|
|
presence as a mistake.
|
||
|
|
|
||
|
|
**4. Seven CLI surfaces never reach the gate** and must not appear in the
|
||
|
|
package: `plan`, `apply --dry-run`, `route`, `audit`, `catalog`,
|
||
|
|
`decision inspect`, and `evidence`.
|
||
|
|
|
||
|
|
`apply --dry-run` is the one flex-auth cannot enforce. `apply` is an action and
|
||
|
|
`apply --dry-run` is not, but both would arrive as `apply` — the distinction is
|
||
|
|
invisible to the PDP. What keeps them apart is that the PEP does not call the
|
||
|
|
gate for a dry run. flex-auth records the limit rather than implying a control
|
||
|
|
it does not have.
|
||
|
|
|
||
|
|
## Dual control on `destroy`
|
||
|
|
|
||
|
|
`destroy` requires `context.approval` marked `approved` with at least two
|
||
|
|
distinct approver subject ids. Repeated entries from one subject count once.
|
||
|
|
|
||
|
|
flex-auth consumes that claim as an input claim and never mutates it
|
||
|
|
(`security-layer-model_v0.7` §9.4). Per `FLEX-DEC-2026-006` the approval fact is
|
||
|
|
`approval-engine`'s step-1 artifact and the decision is step 2; the PEP
|
||
|
|
validates across both. This package checks that the claim *says* approved with
|
||
|
|
distinct approvers. It does **not** re-derive the claim's temporal validity,
|
||
|
|
signature, or supersession state — those belong to `approval-engine` to assert
|
||
|
|
and to the PEP to verify against the live claim. A PDP re-deriving them from a
|
||
|
|
caller-supplied blob would be inventing an authority it does not have.
|