secrets-engine/history/2026-08-23-scope-intent-capability-gap-assessment.md
tegwick 7799716c73
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
docs: align scope with implemented capabilities
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
2026-08-23 10:48:16 +02:00

217 lines
13 KiB
Markdown

# Scope and Intent Capability Gap Assessment
Date: 2026-08-23
Author: codex
Compared: `SCOPE.md` against `INTENT.md` and the current implementation
## Executive assessment
The repository has a credible, tested CLI MVP, but it is not yet the complete
secure interaction and automation layer described by `INTENT.md`.
The strongest implemented slice is:
```text
catalog -> approval-status check -> guarded plan -> policy/AppRole apply
-> bounded presence/capability verification -> CLI-local exec/handoff
-> non-secret route/evidence pointer
```
The weakest slice is lifecycle safety beyond that pilot chain. Rotation is
metadata only, KV provisioning is not merge-safe for multi-field paths, KV
revoke is destructive and incomplete, approval integration is still a pilot
resolver rather than a production authorization contract, and service mode does
not exist.
The intent remains directionally sound. The gap is maturity, not a need to move
the system boundary: OpenBao should remain the backend; flex-auth/State Hub
should authorize; key-cape/user-engine should own identity; ops-warden should
route; and secrets-engine should remain the guarded workflow layer.
## Open-work assessment
The only active repository workplan is `SECRETS-WP-0006`:
- T05 waits on explicit per-lane approval and scoped production OpenBao apply
authority for five reviewed existing production lanes.
- T06 waits on successful native verification before ops-warden proxy retirement.
No approval or production authority was available during this assessment. The
latest ops-warden message also parks the standalone `warden-sign` AppRole for
break-glass use and explicitly requests no credential action. There was no safe,
actionable live task, so documentation reconciliation was the appropriate work.
## Intent scorecard
| Intent area | Assessment | Current evidence | Material gap |
| --- | --- | --- | --- |
| Decision first | Partial | Privileged CLI handlers require a resolved status and reject missing, unapproved, or superseded decisions. | Resolution accepts tracked local fixtures, checks only coarse status, has no freshness/purpose/actor semantics, and does not enforce actual dual control. The five new CCR strings do not resolve through the State Hub UUID endpoint. |
| OpenBao enforces; engine orchestrates | Substantially aligned | Backend operations are isolated in `openbao.py`; catalog, plan, guards, delivery, routing, and evidence are separate modules. | Only the local `bao`/`vault` subprocess adapter exists. Some safety depends on local validation rather than backend-bound per-request authorization. |
| Stage-aware privilege | Partial | Stage names, stage mismatch rejection, build/test prefixes, static stage policies, and consumer policy/AppRole naming exist. | Production has no local owner-prefix invariant (`STAGE_PREFIX["prod"]` is empty). Static bootstrap stage policies and supplied credentials remain wider and longer-lived than per-action identity. |
| No values in coordination surfaces | Partial, with a concrete implementation risk | Catalog validation rejects secret-looking inline values; evidence is scrubbed; CLI output avoids values; exec output is redacted. | `kv_put` passes the raw value in the local `bao` subprocess argument vector. Exec/verification parse the complete KV path response in process memory. These do not put values in Git/State Hub, but they fall short of the strongest custody boundary in the intent. |
| Least friction without broad power | Partial | Human-readable dry-runs, one-command exec, route pointers, and exact consumer policies reduce hand-written OpenBao work. | Routine production still needs manual decision and credential bridging. There is no OIDC/service login, service API, or durable request orchestration. |
| Bootstrap honestly, then harden | Partial | Bootstrap files are mode checked, rejected inside Git worktrees, documented, and tracked in the hardening backlog. | Bootstrap mode is still part of the live CLI; one historical cleanup/revocation record remains incomplete; tracked local approval mirrors remain usable for production commands. |
| Prefer exec-time delivery | Partial | `exec-env` and `npm-config` are implemented, including temporary npm config cleanup and child-output redaction. | `exec-file` and `wrapped` are schema-only. Scoped login tokens are not explicitly revoked after exec/verify, and child delivery is local CLI execution only. |
| Auditable and reversible | Weak/partial | Scrubbed JSONL evidence and best-effort State Hub progress events exist; OpenBao remains the audit source. | Many failure paths produce no evidence, State Hub posting silently drops failures, actors are not cryptographically bound, no audit-report command exists, and KV revoke is destructive rather than reversible. |
| Typed multi-application catalog | Substantially aligned as metadata | Multiple KV lanes, an auth-capability lane, explicit org/repo/stage/consumer/field data, existing-mount semantics, and high-risk ownership checks are implemented. | This is not runtime multi-tenancy. Consumer claims are descriptive and are not authenticated or authorized by this process. |
| CLI and service API | CLI achieved; API absent | The documented CLI surface exists and the pilot flow has integration coverage. | No daemon, API, queue, scheduler, UI, remote protocol, or concurrency model exists. |
| Lifecycle: establish/access/rotate/deactivate | Establish/access partial; lifecycle incomplete | Apply, provision, verify, exec, handoff, and two forms of revoke exist. | No rotate command, compromise/suspend state, provider rotation, lease/accessor lifecycle, rollback orchestration, or safe soft deactivation exists. |
## Highest-priority gaps
### P0 — Provisioning is unsafe for shared multi-field paths
`provision_from_file` reads one declared field and calls `OpenBaoClient.kv_put`.
That adapter invokes:
```text
bao kv put <mount>/<path> <field>=<value>
```
Two consequences matter:
1. `kv put` replaces the data at a KV v2 path rather than merging sibling
fields. Updating one field can remove the others.
2. The raw value is present in the local subprocess argument vector while the
command runs.
This is acceptable only as a constrained pilot/bootstrap primitive on a
single-field or disposable path. It must not be used on the newly admitted
issue-core, reuse-surface, Forgejo, or email paths. A production design needs a
merge-safe update mechanism that does not expose values through argv, plus
tests proving sibling preservation and failure cleanup.
### P0 — KV revoke is destructive, incomplete, and inconsistent with dry-run
For a KV lane, live `revoke` deletes all KV metadata/versions but leaves the
consumer AppRole and ACL policy. The dry-run text says it would delete both KV
metadata and the AppRole, which the live path does not do.
This does not meet the intent's reversible lifecycle principle. The command
needs explicit operations with different approvals, for example:
- suspend delivery by deleting/disabling AppRole credentials;
- deactivate consumer policy/auth;
- revoke leases/tokens by accessor;
- delete current value versions;
- irreversibly destroy KV metadata only under an explicit destruction action.
Until then, KV revoke should be treated as destructive path deletion, not a
general revocation or deactivation workflow.
### P0 — Production approval is not yet a complete authorization contract
The resolver accepts `resolved`, `approved`, or `accepted` status and absence of
`superseded_by`. It does not validate action type, lane/stage binding, actor,
expiry, allowed fields, operation, approval count, or flex-auth result.
`dual-control` is a catalog label but is not enforced as two approvals.
Tracked local decision mirrors are useful for demos and historical pilots but
are too permissive as a steady-state production authority source. Production
should require a canonical decision object bound to the exact action and should
fail closed when State Hub/flex-auth cannot prove that binding.
## Important hardening gaps
### P1 — Steady-state authentication is absent
The CLI accepts a token from an environment variable or a mode-0600 bootstrap
file. It does not perform OpenBao OIDC, workload/service auth, short-lived
issuer exchange, or per-action identity binding. This keeps routine production
work dependent on attended external credential bridging.
### P1 — Verification is narrower than the intent
KV positive verification checks one field, defaulting to the first. Route
readiness also checks only the first field, so a multi-field lane can appear
ready while sibling fields are missing. KV negative verification uses a fixed
garbage token, not a real unrelated workload identity. Provider behavior and
application health are outside the check.
High-risk lane acceptance should verify every declared field, use a real
unrelated identity or capabilities proof, capture OpenBao audit request ids,
and, where appropriate, call a value-safe provider/application smoke.
### P1 — Delivery token lifecycle is implicit
Exec and verification create AppRole Secret IDs and login tokens. Secret ID
use counts and token TTL/use bounds limit them, but the code does not revoke the
issued token explicitly on success, child failure, interruption, or verification
failure. Evidence does not include a safe lease/accessor lifecycle.
### P1 — Evidence is informative, not a durable audit workflow
Local JSONL is append-only by convention, not tamper-evident. State Hub posting
is best-effort and silently ignored on error. Apply/provision/handoff failures
are generally recorded only if a caller adds separate evidence; there is no
transaction id joining State Hub evidence to OpenBao audit entries, and no
`audit` command summarizes a lane.
### P1 — Lifecycle metadata is not lifecycle automation
Catalog entries require rotation/deactivation descriptions and owners, which is
valuable governance metadata. The engine does not execute those plans, manage
provider credential overlap, coordinate consumers, maintain compromised or
deactivated state, or verify rollback.
## Longer-term product gaps
### P2 — Service mode
The intent describes a CLI and service API. Only the CLI exists. A service
should wait until approval binding, auth, delivery token lifecycle, evidence,
and destructive-operation semantics are hardened; exposing today's CLI directly
would amplify its pilot shortcuts.
### P2 — Runtime tenancy and consumer identity
The catalog can describe many applications and logical owners, but the engine
does not authenticate a tenant, resolve claims, isolate concurrent callers, or
enforce per-tenant quotas/policies. The current product is multi-application
metadata and path orchestration, not a multi-tenant service.
### P2 — Backend abstraction
OpenBao-specific behavior is isolated reasonably well, but there is only one
subprocess backend. “OpenBao as the initial enforcement backend” remains an
architectural option, not a demonstrated portable backend contract.
## Current evolution phase
Against the phases in `INTENT.md`:
1. **Bootstrap:** implemented and still active.
2. **MVP:** achieved for the whynot-design npm pilot.
3. **Stage separation:** implemented in catalog/plan/static policy form, but
steady-state stage authentication and stronger production owner binding are
incomplete.
4. **Hardening:** started; existing-mount admission, risk classification,
exact-field guards, bounded AppRoles, and fail-closed decisions exist. The P0
and P1 gaps above remain.
5. **Service mode:** not started.
## Recommended next sequence
1. Keep `SECRETS-WP-0006-T05` limited to approved metadata apply and explicit
per-field, positive/negative verification. Do not provision or revoke the
existing shared paths through the current KV lifecycle commands.
2. Fix provisioning transport and merge semantics, with sibling-preservation
and process-argument tests.
3. Split KV suspend/deactivate/destroy semantics and make dry-run match live
behavior.
4. Replace coarse/local production decision resolution with an action-bound,
expiring, dual-control-capable authorization contract.
5. Add steady-state OIDC/service auth and explicit child-token revocation.
6. Strengthen multi-field/readiness and unrelated-identity verification, then
correlate evidence with OpenBao audit request ids.
7. Implement rotation state and wrapped/file delivery before designing service
mode.
## Conclusion
`INTENT.md` still describes the right destination, but the accurate present
tense is a guarded OpenBao CLI MVP with one proven native workload pilot and
several reviewed-but-not-live production lane plans. The repository should not
yet advertise itself as a service API, complete lifecycle engine, runtime
multi-tenant system, wrapped-delivery broker, or production-safe general KV
updater.