secrets-engine/workplans/SECRETS-WP-0008-layer-model-lifecycle-conformance.md
tegwick 2278cefbb3
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Ship secret-use snapshot surface for kings-guard
Add secrets-engine secret-use snapshot: catalog plus local evidence only,
contracted non-secret fields, declared 1d heartbeat cadence. Never contacts
OpenBao. Completeness is not claimed. owner_status stays proposed until
kings-guard admits the snapshot as an observation input.

Assistant: grok
Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
2026-09-02 01:31:23 +02:00

272 lines
11 KiB
Markdown

---
id: SECRETS-WP-0008
type: workplan
title: "Evolve the Lifecycle engine to the accepted security layer model"
domain: infotech
repo: secrets-engine
status: active
owner: grok
topic_slug: custodian
created: "2026-08-29"
updated: "2026-09-02"
state_hub_workstream_id: "9c9e5164-b2f5-5ea2-a557-5368d65e9fe0"
---
# SECRETS-WP-0008 - Evolve the Lifecycle engine to the accepted security layer model
## Goal
Close the gaps between the 2026-08-29 layer declaration (Engine / Lifecycle)
and the obligations the accepted NetKingdom Security Layer Model v0.7 places on
a PEP-shaped Lifecycle engine.
Source review: `history/2026-08-29-layer-model-scope-intent-review.md`.
Declaration surface already landed this session: `INTENT.md` frontmatter,
`layer.yaml`, `pep-stance.yaml`, and `tests/test_layer_conformance.py`.
This workplan does **not** restart CLI hardening owned by `SECRETS-WP-0007`.
It adds the layer-model remainder: stance binding, decision-record consumption,
`audit-core` evidence, and the two proposed engine surfaces.
## Non-goals
- Taking ops-warden's SSH access lane or becoming a credential broker.
- Rendering or caching authorization decisions.
- Observation in production or automatic containment (companion §10: both are
at zero estate-wide).
- Service mode, runtime tenancy, or a second custody backend.
- Reopening live production on State Hub status or local fixtures.
## Coordination
- `SECRETS-WP-0007-T04` owns the consumer validator and the fail-closed
production gate until a durable ActionAuthorization object is served.
- `SECRETS-WP-0007-T05` owns delivery-session self-revocation and the KeyCape
scaffold; this workplan owns the layer-model "no standing credential" bar.
- railiance-platform owns production OpenBao and the JWT role.
- `access-engine` / State Hub own serving the decision record.
- `audit-core` owns evidence custody.
- ops-warden owns the SSH Staff PEP and must assent any SSH-CA API contract.
- kings-guard owns secret-abuse posture and must assent the evidence surface.
- gate-house owns statute §13.1 inventory of PEP stance maps.
## Tasks
## Publish stance application as shipped behaviour
```task
id: SECRETS-WP-0008-T01
status: done
priority: high
state_hub_task_id: "945735c6-2d3d-5f72-baf0-692e6b210889"
```
Completed 2026-08-29. `_require_lane_approval` loads `pep-stance.yaml`.
`SHIPPED_STANCE` is pinned equal to the published map. Fail-closed production
evidence records `stance_stage`, `stance_failure_mode`, and omits
`stance_decision_id` unless a decision was rendered.
`pep-stance.yaml` is published and tests pin `prod: fail_closed` to
`_require_lane_approval`. The gate is still hardcoded, and a refusal is
recorded as a `DecisionError` rather than named stance fields.
Load the published map as the source of shipped defaults. When the
unreachable-engine residue is applied, record at least stage, failure mode, and
decision id present only where a decision was rendered. Keep the three-factor
unsafe-demo exception out of the stance table. Assert map equality in
`tests/test_layer_conformance.py` the way ops-warden's stance test does.
Acceptance:
- `_require_lane_approval` (or its successor) reads `pep-stance.yaml`.
- Changing the YAML without changing the code fails the test, and the reverse.
- Fail-closed production evidence includes named stance fields, never a secret.
- Build/test remain fail-open *relative to access-engine* and still require
the existing lane-approval check; that residue stays documented as a gap
until T02.
## Consume access-engine decision records
```task
id: SECRETS-WP-0008-T02
status: wait
priority: high
state_hub_task_id: "3eb9cff8-1441-5437-9e92-a2b655c82d04"
```
Progress 2026-09-02. Gate House notice `632bdad9` (`GH-DEC-2026-003`): this
engine is the PEP for FLEX-WP-0017-T05 / OpenBao writes. The shared consume
function now lives in `src/secrets_engine/approval_consume.py` and every live
privileged production handler reaches it through `_require_lane_approval`
before `OpenBaoClient.resolve`. Conflict, unavailability, or a missing
binding fail closed with no OpenBao call. Production still also fail-closes
on the unpublished durable ActionAuthorization / consume-binding serving
path, so this task remains `wait`.
Blocked on the durable ActionAuthorization serving path owned with
`SECRETS-WP-0007-T04` / State Hub / `access-engine`.
Once that object is served, a protected side effect holds a decision record
identifying the request it was rendered for. Replay is permitted iff the
canonical request digest matches and the lifetime holds. Do not cache the
verdict. Do not treat State Hub status, a local fixture, or catalog admission
as the decision.
Acceptance:
- Every live privileged production handler passes one shared consume function.
- Wrong digest, expired lifetime, superseded status, or missing record fail
closed before OpenBao.
- Evidence carries the decision id; a fail-closed residue still records the
stance instead.
- Local fixtures cannot authorize steady-state production.
## Emit evidence under the §9.6 bound
```task
id: SECRETS-WP-0008-T03
status: done
priority: high
state_hub_task_id: "0b54cedf-9335-5fa0-bdcb-1efa9a9cd98d"
```
Completed 2026-08-29. `evidence-classification.yaml` is the declaration.
Load-bearing records are queued in `evidence_dir/outbox/` before JSONL.
`secrets-engine evidence heartbeat` emits a positive claim. Drain never sits
on a mutation path and an audit-core outage leaves the queue in place.
Classify each privileged action as load-bearing or attributive. Today's trail
is attributive local JSONL plus best-effort State Hub notes; completeness is
already not claimed and must stay that way until the destination is
`audit-core`.
Load-bearing classes (at least live destroy, production revoke/suspend of a
control, and dual-control provision once those are enabled) require atomic
emission via a **local** outbox and a declared cadence or heartbeat. Do not
place a synchronous `audit-core` dependency inside the state-change
transaction. Attributive classes may keep the current non-blocking trade if
that trade stays declared.
Acceptance:
- A machine-readable classification exists next to the declaration.
- No control in this repository branches on presence or absence of a local
evidence record.
- Load-bearing emission is queued locally; an `audit-core` outage does not
block a revocation.
- Documentation uses the sound claim: the archive proves records it holds were
not altered or truncated after arrival.
## Accept the SSH-CA signing engine surface
```task
id: SECRETS-WP-0008-T04
status: done
priority: medium
state_hub_task_id: "581aeee3-3d99-5034-992b-7d42e6542a19"
```
Completed 2026-08-29. Contract published at `docs/ssh-ca-signing-contract.md`.
No OpenBao SSH-CA write was added. `layer.yaml` owner_status remains proposed.
ops-warden's `ssh-ca-signing-write` gap names this engine as intended owner.
Accept the **engine API**, decline the Staff lane. Publish a contract
ops-warden can call after an `access-engine` decision: sign a short-lived
certificate for an `adm`/`agt`/`atm` actor under the caller's request, present
no additional authority, reconstruct as the caller's action.
Do not implement a replacement client until ops-warden assents the contract.
Do not unpark the standalone `warden-sign` AppRole as break-glass.
Acceptance:
- A non-secret contract document in `docs/` states request, decision binding,
lifetime, evidence, and non-responsibilities.
- ops-warden has a recorded chance to assent or contest.
- No OpenBao SSH-CA write is added to this CLI ahead of that assent.
- `layer.yaml` owner_status remains `proposed` until the surface ships.
## Accept the secret-use evidence surface
```task
id: SECRETS-WP-0008-T05
status: done
priority: medium
state_hub_task_id: "3100d28c-9494-5b21-9d5a-af583cd8aa55"
```
Completed 2026-08-29. Contract published at
`docs/secret-use-evidence-contract.md`. `layer.yaml` owner_status remains
proposed. `route`/`audit` are still not this surface.
**2026-09-02:** kings-guard assented in its own voice (message
`75ebd2cc-a166-4676-94aa-deef2791c0c9`). It will not consume the surface as
an observation input until the surface ships and publishes its cadence
declaration. No Tooling contact will be opened to fill that wait.
**2026-09-02 later:** shipped `secrets-engine secret-use snapshot`. Cadence is
the declared 1d heartbeat on the snapshot envelope. `owner_status` stays
`proposed` until kings-guard admits the snapshot as an observation input.
`route`/`audit` remain distinct operator summaries.
kings-guard's secret-abuse posture is fixture-driven because no engine exposes
lease, revocation, mount, rotation, and delivery-session metadata. `route` and
`audit` are operator summaries over local JSONL and are not that surface.
Publish a read-only engine contract of non-secret metadata, bounded by §9.6:
it cannot prove omission at source. Coordinate destination with `audit-core`.
Do not give kings-guard a vault client, and do not return values.
Acceptance:
- Contract lists fields, freshness, and the evidence bound.
- kings-guard has a recorded chance to assent or contest.
- No secret material, token, or accessor is in the surface.
- `layer.yaml` owner_status remains `proposed` until the surface ships.
## No standing engine credential
```task
id: SECRETS-WP-0008-T06
status: wait
priority: medium
state_hub_task_id: "d7bc8bdc-a0f8-5058-a640-374ef9859148"
```
Progress 2026-09-02. Named engine auth is shipped: `service-jwt`, `bootstrap`,
and `env` are exclusive providers. `--auth service-jwt` exchanges the KeyCape
service JWT and logs in through `OpenBaoClient.login_jwt` (JSON file, never
argv), then self-revokes. A JWT selection refuses `--bootstrap-token-file` and
does not read `BAO_TOKEN` on failure. `--bootstrap-token-file` is a named
break-glass provider with `auth_break_glass` evidence.
The platform-owned OpenBao JWT mount/role is still unpublished, so auto keeps
bootstrap/env and this task remains `wait`. Companion §7 / statute §3.4: an
agent holds no long-lived credential of its own. Authority is per task,
time-bounded, and attributable to the principal it acts for.
Bootstrap token files remain break-glass, heavily evidenced, never an implicit
fallback from service identity.
Acceptance:
- Steady-state CLI/API auth is the reviewed service identity, not
`--bootstrap-token-file`.
- Bootstrap is a named break-glass provider with distinct evidence.
- No fallback from JWT/service-auth failure into bootstrap or AppRole.
- Issued delivery sessions continue to self-revoke as in WP-0007-T05.
## Exit criteria
- The published PEP stance is the shipped default and stance application is
recorded by name.
- Production live side effects require an access-engine decision record, or
remain fail-closed with a recorded stance.
- Evidence classification and destination match §9.6; completeness is not
claimed.
- SSH-CA and secret-use surfaces are either contracted with the declaring
repositories or explicitly declined in this repository's own voice.
- Bootstrap is not the steady-state engine credential.
- `layer.yaml` / `pep-stance.yaml` / INTENT frontmatter stay in one voice.
- No raw secret values in Git, State Hub, chat, prompts, workplans, evidence,
or argv.