2026-06-28 09:03:37 +00:00
# SCOPE
2026-08-23 10:48:16 +02:00
> Implemented capability boundary for agents and contributors. Aspirational
> direction belongs in `INTENT.md`; current work and operational gates belong in
> `workplans/`.
2026-08-29 11:57:47 +02:00
>
> **Layer declaration (accepted model v0.7):** Engine / Lifecycle. Machine-readable
> form: `layer.yaml`. PEP stance: `pep-stance.yaml`. This file states what the
> implementation currently does inside that layer; it does not restate doctrine.
2026-06-28 09:03:37 +00:00
## One-liner
2026-08-29 11:57:47 +02:00
secrets-engine is the Lifecycle engine for cataloged credential work: a
decision-gated Python CLI that validates non-secret lane metadata and
orchestrates narrowly scoped OpenBao policy, AppRole, provisioning,
verification, delivery, routing, handoff, and revocation actions.
2026-08-23 10:48:16 +02:00
2026-08-29 11:57:47 +02:00
It is a deterministic API over OpenBao, not a vault, not a policy decision
point, not an identity provider, and not a general secrets API.
2026-08-23 10:48:16 +02:00
## Implemented Capabilities
### Catalog and validation
- Loads YAML catalog entries for `kv` and `auth-capability` lanes.
- Models `build` , `test` , and `prod` stages, exact mounts/paths/fields,
consumers, approvals, delivery modes, verification, lifecycle ownership,
workload-delivery metadata, and `standard` /`high` risk.
- Distinguishes engine-managed and existing KV mounts, and engine-managed,
existing, or absent native delivery auth.
- Rejects malformed entries, inline secret-looking values, wildcard paths,
unsupported modes, bootstrap-only approval for high-risk lanes, and high-risk
entries without rotation/deactivation owners.
- Records existing ESO/Kubernetes/OIDC workload delivery as metadata only; it
does not operate those delivery systems.
### Decision-gated planning and OpenBao metadata apply
- Resolves a catalog lane by catalog id or `approval.decision_ref` .
2026-08-23 14:15:42 +02:00
- Resolves legacy lane decisions from State Hub by id, with tracked local YAML
2026-08-29 11:57:47 +02:00
mirrors retained only for non-production and explicit throwaway demos. This is
not an access-engine decision record.
2026-09-07 13:46:31 +02:00
- Fails every production live command closed while the two-artifact chain
cannot be completed — today because approval-engine does not serve the
approval-claim endpoint. That fail-closed row is the published
unreachable-engine stance for `prod` in `pep-stance.yaml` . A local
2026-08-29 11:57:47 +02:00
mirror can unlock a prod-labeled lane only when an explicit unsafe-demo
switch, disabled Hub URL, and loopback OpenBao target are all present. Plans
and `apply --dry-run` remain usable.
2026-09-07 13:46:31 +02:00
- Validates the two-artifact authorization chain, split by owning layer per
`GH-DEC-2026-005` . The approval-engine **approval-claim** supplies the approval
fact: issuer, `valid_now` , consumption state, freshness, `reason_code` , a
required `binding.pdp_path` declaration, and the `pdp_digest` tie to this exact
action. The flex-auth **DecisionEnvelope** supplies the decision: effect,
structured binding correspondence to the proposed action, canonical request
digest, lifetime, and the accepted policy package/version pin. Neither layer
republishes the other's data, and validation is consume-only; this process does
not evaluate policy.
- Compares the decision binding by structured correspondence rather than
byte-equality, per flex-auth's published normalization rule: everything the
engine proposed must survive unchanged, registry enrichment may add only
`type` /`tenant` /`attributes` , and an enriched tenant must be the request
tenant. The request digest is verified against the tuple the binding carries.
The CheckRequest carries the package's `known_tenant` ; an absent tenant is a
`wrong_tenant` denial, not an ignored field.
- The `ActionAuthorization` object is **deferred and never ratified**
(`FLEX-DEC-2026-006` ); nothing validates it. There is no State Hub authority
constant — State Hub is a read model and holds no runtime approval authority.
The distinct-approver threshold is folded into `valid_now` by the issuer and is
no longer an independent consumer-side check, which is correct on layering and
a real reduction in what this engine verifies alone.
2026-08-23 10:48:16 +02:00
- Renders guarded OpenBao plans for exact consumer ACL policies and AppRoles.
- Applies policy and AppRole metadata idempotently. Existing mounts render a
non-mutating check and are never created by apply.
- Rejects stage mismatches, wildcard paths, broad-admin names, forbidden
`sys/` , token, identity, root, or sudo semantics, and auth-capability paths
outside an exact allowlist.
### KV provisioning
- Imports one declared field from a mode-0600 file outside Git worktrees.
- Generates random values only for non-production lanes.
- Keeps values out of CLI output and evidence records.
2026-08-23 12:05:58 +02:00
- Uses a CAS-aware create/patch primitive. Secret data is passed through a
mode-0600 temporary JSON input reference that is removed in a `finally` path,
never as a raw subprocess argument. Existing-path updates preserve unmentioned
sibling fields and stale-version writes fail closed.
2026-08-23 10:48:16 +02:00
### Verification
2026-08-23 12:05:58 +02:00
- KV positive verification logs in through the lane AppRole and reports field
presence without printing values. With no `--field` , it checks every declared
field; an explicit `--field` narrows the check.
2026-08-23 12:33:38 +02:00
- KV negative verification requires a real unrelated token supplied through a
mode-0600 file outside Git and checks that it cannot read the path. Missing
unrelated identity material fails closed without a backend probe.
2026-08-23 10:48:16 +02:00
- Auth-capability verification checks the AppRole token's capabilities on exact
allowlisted and denial-probe paths.
2026-08-23 12:33:38 +02:00
These are bounded policy/presence probes. A throwaway OpenBao integration test
proves an overlapping unrelated policy is detected. Production still needs a
reviewed owner/source for each unrelated identity; the engine does not mint or
select that identity itself. Verification does not yet exercise a provider
operation or produce OpenBao audit-request correlation.
2026-08-23 10:48:16 +02:00
### Exec-time delivery
- `exec-env` fetches one declared field through the lane AppRole and injects it
into a child process environment.
- `npm-config` creates a temporary mode-0600 npm config containing an environment
reference rather than the token, injects the value only into the child, and
deletes the config afterward.
- Child stdout and stderr are combined and streamed through token-pattern and
exact-value redaction.
- Undeclared fields and undeclared/unsupported exec modes are rejected before
value fetch.
2026-09-23 17:27:09 +02:00
- A configured exec owner may also receive companion lanes. Each companion lane
must consent with `companion_of` , is gated through its own approval and
consume, and is read through its own AppRole. A refusal on any lane starts no
child (`docs/exec-owner-binding.md` ).
2026-08-23 10:48:16 +02:00
The OpenBao KV response is parsed in the parent process, so every field stored
at the path crosses that process boundary even though only the selected field is
2026-08-23 12:05:58 +02:00
injected. Exec and verification use scoped AppRole sessions that explicitly
self-revoke in a `finally` path. Non-secret evidence contains only an accessor
fingerprint and establishment/revocation outcome; TTL/use limits remain cleanup
backstops.
2026-08-23 10:48:16 +02:00
### Auth-capability handoff
- Models narrow non-KV AppRole grants such as exact `ssh/sign/<role>` update
capability.
- Mints `role_id` /`secret_id` material only after approval and writes it to
distinct mode-0600 files outside Git worktrees without printing the
`secret_id` .
The cataloged standalone `warden-sign` AppRole is currently parked by governance
and must not be applied as a break-glass or recovery path. OpenBao being sealed
cannot be recovered through that AppRole.
### Routing and evidence
- `route` returns a non-secret pointer containing lane ownership, decision
2026-08-23 12:05:58 +02:00
status, metadata/value-presence booleans, missing declared field names,
readiness, and a safe next command. Every declared field must be present.
2026-08-23 10:48:16 +02:00
- Records scrubbed local JSONL evidence and posts a minimal State Hub progress
2026-08-23 14:15:42 +02:00
event on a best-effort basis. Posts carry stable idempotency/source headers.
Each requested State Hub delivery receives an append-only local `delivered` ,
`queued` , `failed` , or `skipped-no-topic` companion record; edge-relay queued
2026-08-29 11:57:47 +02:00
receipts retain only the non-secret outbox id. This trail is attributive:
completeness is not claimed, and it is not `audit-core` .
2026-08-23 12:58:12 +02:00
- Every live privileged CLI handler records an attempt before lane-approval
resolution and a terminal success, verification failure, rejection,
interruption, or typed backend/input failure. Failure evidence contains the
exception class and approval state, never exception prose.
2026-08-23 12:33:38 +02:00
- `audit` summarizes allowlisted local lane evidence: action/result counts,
canonical decision references, session cleanup outcomes, and State Hub
delivery outcomes. It never re-emits arbitrary evidence detail.
2026-08-23 10:48:16 +02:00
- Keeps OpenBao audit logs as the backend source of truth.
2026-08-23 14:15:42 +02:00
The engine recognizes edge-relay queued receipts but does not own or initiate
outbox replay. Direct State Hub failure remains locally visible. Route and audit
do not replace exact-action authorization or OpenBao audit logs.
### Steady-state service-auth scaffold
- Implements an explicit KeyCape `client_credentials` exchange for the
accepted `secrets-engine-openbao` service identity.
- Reads the confidential-client secret only from a mode-0600 file outside Git,
sends it through HTTP Basic authentication, and rejects ID/refresh tokens.
- Preflights the exact issuer, subject, audience, principal type, tenant, role,
scope, assurance, RS256 algorithm, and 15-minute lifetime. The in-memory JWT
is excluded from object representations and is renewed at the three-minute
boundary.
- Never retries into or falls back to bootstrap, operator, or AppRole auth.
2026-09-02 01:24:08 +02:00
The CLI now has a named `service-jwt` provider that logs in through that
scaffold when the platform JWT mount/role contract is present, then
self-revokes the issued OpenBao token. Signature verification and token
issuance remain with the platform-owned exact-bound OpenBao JWT role, whose
mount/role contract is still outstanding. Until it is published, `--auth auto`
keeps named bootstrap/env providers and never treats them as a fallback from
service-jwt failure.
2026-08-23 12:33:38 +02:00
Treat their output as operational guidance, not complete attestation for
high-risk lanes.
2026-08-23 10:48:16 +02:00
### Revocation currently available
2026-08-23 12:05:58 +02:00
- For engine-managed native auth, live `revoke` deletes the AppRole and policy.
- For KV lanes, ordinary revoke explicitly preserves all KV metadata/versions.
- Externally managed delivery auth and workload delivery are reported as
preserved and are not mutated.
- `lifecycle suspend` removes only the managed AppRole and preserves its policy
for reviewed re-apply.
- `lifecycle deactivate` removes managed native AppRole/policy objects while
preserving KV custody.
- `lifecycle destroy --dry-run` renders managed-auth removal plus irreversible
KV metadata deletion. Live destroy is currently disabled even with exact
catalog-id confirmation; it will remain closed until the canonical
exact-action approval contract in `SECRETS-WP-0007-T04` is enforced.
2026-09-02 10:08:33 +02:00
These operations do not manage external workload delivery. `session revoke`
revokes an already-issued token accessor or lease id the operator already
2026-09-02 13:09:10 +02:00
holds; evidence is fingerprint-only. `rotate` replaces one declared KV field
through the merge-safe write path. Persistent overlay states
(`active` /`suspended` /`deactivated` /`compromised` ) live under the evidence
directory; they do not recreate OpenBao objects and do not rotate provider
credentials.
2026-08-23 10:48:16 +02:00
## CLI Surface
```text
secrets-engine catalog list|show
secrets-engine decision inspect
secrets-engine plan
secrets-engine apply [--dry-run]
secrets-engine provision
secrets-engine verify
secrets-engine handoff
secrets-engine exec
secrets-engine policy publication
secrets-engine route
secrets-engine revoke [--dry-run]
2026-08-23 12:05:58 +02:00
secrets-engine lifecycle suspend|deactivate|destroy
2026-08-23 12:33:38 +02:00
secrets-engine audit < catalog-id > [--json]
2026-08-29 12:52:55 +02:00
secrets-engine evidence heartbeat|drain|classify
2026-08-23 10:48:16 +02:00
```
2026-09-02 08:59:56 +02:00
The implemented exec adapters are `exec-env` , `npm-config` , and `exec-file` .
`read-check` is verification and `approle-login` is auth-capability handoff
metadata. `secrets-engine wrap` implements response-wrapped operator handoff.
2026-08-23 10:48:16 +02:00
## Proven Operationally
- The whynot-design npm pilot completed a real publish through native
`secrets-engine exec` without placing the token in the parent shell.
- Throwaway OpenBao integration tests exercise plan, apply, provisioning,
positive/negative verification, exec delivery, routing, revocation, and
idempotent metadata apply.
- Five existing high-risk `platform` mount lanes have reviewed catalog metadata
and guarded dry-run plans. Their native AppRoles are not live: workplan
`SECRETS-WP-0006` waits on per-lane approval, production authority, live
positive/negative verification, and routing cutover.
## Not Implemented
- A service API, daemon, UI, queue, scheduler, or remote multi-user service.
2026-09-02 08:59:56 +02:00
- Platform JWT mount/role materialization for the implemented KeyCape
service-auth / `service-jwt` provider.
2026-09-02 13:09:10 +02:00
- Provider-side / workload consumer rotation. Overlay lane state is local and
non-secret only; it is not an OpenBao-side state machine.
2026-09-07 13:46:31 +02:00
- Protocol step 1 in production: approval-engine does not yet serve the
approval-claim endpoint (`APPROVAL-WP-0002-T03` ), so `resolve_consume_binding`
returns no binding and live production remains fail-closed. Step 2 is served
and proven — a real CheckRequest against the deployed `flex-auth-secrets-engine`
pin returns a validated v2 decision over the owner-documented access path
(`docs/pdp-access-path.md` ).
- Responder authentication for the decision channel. `flex-auth.decision-record.v1`
carries no signature and pins serve plain HTTP, so a responder knowing the
published package and version could return a well-formed allow. Fail-closed
protects against a PDP that is absent, not one that lies (`FLEX-DEC-2026-010` ).
The enforced loopback address shape stands in for this until `FLEX-WP-0024`
ships detached signatures.
2026-08-29 11:57:47 +02:00
- Direct access-engine evaluation, JWT signature verification, or identity
2026-08-23 14:15:42 +02:00
authentication. KeyCape claims receive only a consumer preflight; OpenBao is
responsible for cryptographic JWT validation.
2026-08-23 10:48:16 +02:00
- Runtime tenancy isolation; `org` , `repo` , consumers, and stages are catalog
metadata plus local path/policy guards, not a tenant control plane.
- Management or health verification of ESO, Kubernetes Secrets, deployments,
provider accounts, SSH issuance, tunnels, or remote transport.
- Any backend other than the local `bao` /`vault` CLI speaking to OpenBao.
2026-08-29 12:52:55 +02:00
- An SSH-CA signing engine API. The contract is
`docs/ssh-ca-signing-contract.md` ; ops-warden still signs through its
declared OpenBao gap.
- A secret-use evidence engine API for kings-guard. The contract is
`docs/secret-use-evidence-contract.md` . `route` and `audit` are operator
summaries over local JSONL, not that observation surface.
- Emission to `audit-core` . Load-bearing records are queued locally; drain
requires a sender binding that does not exist yet. Completeness is not
claimed.
2026-08-29 11:57:47 +02:00
- Security-zone membership as a request claim. PEP scope is catalog stage.
2026-08-29 12:52:55 +02:00
- Drain of the load-bearing outbox into a live `audit-core` sender binding.
2026-08-23 10:48:16 +02:00
## System Boundary
2026-08-29 11:57:47 +02:00
- **OpenBao / railiance-platform** (Tooling) owns custody, policy enforcement,
leases, and backend audit. secrets-engine is the Lifecycle API over it.
- **access-engine (`flex-auth` )** owns authorization decisions. secrets-engine
consumes a decision record or applies its published unreachable-engine stance;
it does not evaluate policy.
- **approval-engine** owns the durable approval object. secrets-engine may
consume an approval as an input claim and must not store or mutate one.
- **gate-house** owns security doctrine. Doctrine reaches this engine only as it
already reached the decision, never as a side channel.
- **audit-core** owns evidence custody and integrity. Local JSONL and State Hub
notes are not that archive.
2026-08-23 10:48:16 +02:00
- **user-engine / key-cape** own identity, OIDC, MFA, and claims.
2026-08-29 11:57:47 +02:00
- **ops-warden** issues SSH certificates (Staff PEP) and routes non-SSH
credential needs here; it does not vend their values. The SSH-CA write
surface is a proposed engine API, not a transferred lane.
2026-08-23 10:48:16 +02:00
- **ops-bridge** owns tunnels and remote execution transport and may consume a
scoped delivery path.
- **workload/platform repositories** own ESO/Kubernetes delivery, provider
rotation, and application health.
- **info-tech-canon / net-kingdom** own canonical terminology and the
2026-08-29 11:57:47 +02:00
cross-system security boundary. **gate-house** owns the layer model.
2026-08-23 10:48:16 +02:00
Canonical boundary:
`net-kingdom/docs/secrets-engine-security-infrastructure-boundary.md` .
2026-08-29 11:57:47 +02:00
Layer model: `net-kingdom/canon/standards/security-layer-model_v0.7.md` .
Working companion: `net-kingdom/SECURITY-COMPANION.md` .
2026-08-23 10:48:16 +02:00
## Security Rules
- Never place raw secret values in Git, State Hub, chat, prompts, workplans,
normal logs, or evidence.
2026-08-29 11:57:47 +02:00
- Never render or cache an authorization decision. Catalog admission, a dry-run,
an old workload CCR, or a local fixture is not an access-engine allow.
- Never treat silence from `access-engine` as permission. Production live
actions fail closed; any unreachable-engine residue must be the published
stance, recorded, never implicit.
2026-08-23 10:48:16 +02:00
- Never mutate an existing shared mount or replace workload delivery by
implication.
2026-08-23 12:05:58 +02:00
- Never add KV destruction back to ordinary `revoke` ; irreversible custody
destruction requires `lifecycle destroy` , exact confirmation, and a distinct
approved action. The live path remains disabled until that approval contract
exists.
2026-08-23 10:48:16 +02:00
- Keep bootstrap and handoff material outside repositories with mode 0600 and
2026-08-29 11:57:47 +02:00
explicit expiry/revocation handling. Bootstrap is not an implicit fallback
from service identity.
- Never claim that local evidence or a missing record proves occurrence or
non-occurrence. Completeness is not claimed.
## Layer-model obligations (current vs intended)
| Obligation | Current | Intended |
| --- | --- | --- |
| Layer declaration | `INTENT.md` frontmatter + `layer.yaml` | Keep in this repository's own voice |
2026-09-07 13:46:31 +02:00
| One decision point | Two-artifact consumer validation (approval-claim + DecisionEnvelope); step 2 proven against the deployed pin, step 1 unserved so production live stays fail-closed from `pep-stance.yaml` | Consume an access-engine decision record before every protected side effect |
2026-08-29 12:52:55 +02:00
| PEP stance | Runtime loads `pep-stance.yaml` ; named stance fields on privileged evidence | Unchanged map; T02 replaces fail-open residue with a decision record |
| Evidence bound | `evidence-classification.yaml` ; load-bearing local outbox; heartbeat command | Drain to `audit-core` once that sender is admitted |
| SSH-CA surface | Contract at `docs/ssh-ca-signing-contract.md` ; not shipped | Engine API after ops-warden assent |
| Secret-use evidence | Contract at `docs/secret-use-evidence-contract.md` ; not shipped | Engine surface after kings-guard assent |
| Agent credential | Bootstrap token file still accepted; KeyCape scaffold unwired to OpenBao | Per-task, time-bounded service identity; no standing engine credential |
2026-08-23 10:48:16 +02:00
## Where Current Work Lives
- `workplans/` is the source of truth for open work and operational gates.
- `docs/hardening-backlog.md` tracks exit from bootstrap mode.
- `history/` contains dated capability and intent assessments.
- `INTENT.md` remains the stable aspirational direction.
2026-08-29 12:52:55 +02:00
- `layer.yaml` , `pep-stance.yaml` , and `evidence-classification.yaml` are the
layer-model declaration surface.
2026-06-29 12:55:58 +02:00
## Provided Capabilities
```capability
type: security
2026-08-23 10:48:16 +02:00
title: Guarded OpenBao lane planning and metadata apply
description: Validates cataloged KV and auth-capability lanes, renders exact policy/AppRole plans,
and applies approved metadata while rejecting missing decisions, stage mismatches, wildcards,
broad-admin names, and forbidden capability paths. Existing mounts are check-only.
keywords: [secrets, openbao, policy, approle, catalog, decision, stage, least-privilege]
2026-06-29 12:55:58 +02:00
```
```capability
type: security
2026-08-23 10:48:16 +02:00
title: AppRole-scoped exec delivery
description: Fetches one declared KV field through a lane AppRole and injects it into a child
2026-08-23 12:05:58 +02:00
process using exec-env or a temporary npm config, with output redaction, explicit scoped-token
self-revocation, and non-secret cleanup evidence. This is CLI-local delivery; exec-file, wrapping,
and service delivery are absent.
2026-08-23 10:48:16 +02:00
keywords: [secrets, delivery, exec, npm, injection, redaction, openbao]
2026-06-29 12:55:58 +02:00
```
2026-06-29 17:06:20 +02:00
```capability
type: security
title: Scoped OpenBao auth-capability handoff
2026-08-23 10:48:16 +02:00
description: Generates an exact-path policy/AppRole plan and writes role-id/secret-id material to
strict out-of-repo files after approval. Operational use remains subject to lane-specific governance.
keywords: [openbao, approle, auth-capability, handoff, least-privilege]
2026-06-29 17:06:20 +02:00
```
2026-06-30 00:52:05 +02:00
```capability
2026-08-23 10:48:16 +02:00
type: governance
title: Non-secret routing and evidence pointers
description: Reports decision/readiness metadata and records scrubbed local and best-effort State Hub
2026-08-23 12:33:38 +02:00
evidence without returning secret values. Append-only delivery receipts expose State Hub failures,
and an allowlisted audit command summarizes lane operations and cleanup. KV verification can attest a
2026-08-29 11:57:47 +02:00
supplied real unrelated identity, but the engine does not own identity selection. This is attributive
local evidence, not audit-core and not a secret-use observation API.
2026-08-23 10:48:16 +02:00
keywords: [routing, evidence, state-hub, audit, secrets]
2026-06-30 00:52:05 +02:00
```