Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
344 lines
25 KiB
Markdown
344 lines
25 KiB
Markdown
# Task-scoped protected OpenBao execution — owner design
|
||
|
||
Design ID: `SECRETS-EXEC-20261011-v1`. Status: proposed, source preparation only.
|
||
Owning draft: `SECRETS-WP-0013`; adoption remains `SECRETS-WP-0008-T06`.
|
||
No native entitlement, owner assent or founder go is established here.
|
||
|
||
## The concrete gap and immediate correction
|
||
|
||
`cli._require_lane_approval` consumes the approved action before `_open_backend`.
|
||
`engine_auth.select_engine_auth` selects service-jwt for auto whenever its login
|
||
contract is configured. CCR-2026-0038 grants a 300-second, eight-use token with
|
||
exactly `secrets-engine-login-self`. It explicitly denies custody data/metadata,
|
||
policy/auth writes and child-token creation. Thus a valid approval can be
|
||
consumed before an ordinary apply/provision/exec fails for lack of backend power.
|
||
|
||
The staged correction checks **local execution readiness before claim/PDP/CAS**
|
||
for every caller about to access protected OpenBao work. Until a real reviewed
|
||
execution backend ships, service-jwt is rejected for those callers. Explicit
|
||
bootstrap/env providers retain their existing approval/stance gates; no fallback
|
||
or default changes. Service-auth login/self probes remain available. Local-only
|
||
compromise/reactivate records retain their existing path. A file/boolean claiming
|
||
entitlement cannot make the guard pass. Tests exercise the actual apply handler,
|
||
asserting zero claim/PDP/consume/login/apply calls for configured auto and missing
|
||
explicit service-jwt entitlement. This correction fixes early consumption; it
|
||
does **not** implement task-scoped protected work or complete T06.
|
||
|
||
## One initial execution surface
|
||
|
||
Implement `secrets-engine execute-ccr --catalog <id> --stage prod --request <file>`
|
||
as an explicit, metadata-only action. It admits one owner-reviewed Platform
|
||
`workload-kv-read` CCR with already-existing KV and auth mounts, exact generated
|
||
policy and exact Kubernetes or existing netkingdom OIDC role. It does not call
|
||
generic `apply_plan`: that function can manage AppRoles/mounts, which differs
|
||
from Platform's typed CCR applier. Provision, exec delivery, rotation, KV reads,
|
||
SecretIDs, token creation by the caller, mounting and arbitrary CLI commands
|
||
remain outside this first entitlement. Their backend obligations require their
|
||
own later exact action contracts, not reuse of this metadata allowance.
|
||
|
||
CCR38 and its role/policy stay byte-for-byte unchanged. The engine identifies
|
||
itself through that login-only service session; authentication does not create
|
||
authority for the metadata action. A new separately reviewed Platform
|
||
`task-execution-grant` CCR admits the founder-owned execution mediator and its
|
||
temporary exact two-path task token. Existing applied CCRs, including CCR38,
|
||
cannot be replayed to manufacture new native work. A real unexecuted, separately
|
||
approved metadata CCR and native success/negative/cleanup receipts are needed
|
||
for the first proof; none is selected or approved by this design.
|
||
|
||
## Exact request binding
|
||
|
||
Use `authorization.build_action_request(entry, 'execute-ccr', ...)` with the
|
||
existing canonical digest rules. `apply` is a distinct legacy action: its v2
|
||
policy ignores this metadata contract and must never stand in for execute-ccr.
|
||
Add a reviewed `context.execution_contract` object
|
||
before observing the claim or calling Check. Its fields are:
|
||
|
||
| Field | Binding |
|
||
| --- | --- |
|
||
| contract_version | `SECRETS-EXEC-20261011-v1` |
|
||
| operation_id | Owner-issued UUID, part of the request digest |
|
||
| operation | `metadata-apply-ccr`, closed enum |
|
||
| execution_actor | Verified `service:secrets-engine`, separate from initiating subject |
|
||
| ccr_id / ccr_immutable_sha256 | Exact unapplied owner CCR ID and immutable content digest |
|
||
| execution_grant_ccr_id / grant_immutable_sha256 | Exact reviewed task-token materialization and cleanup contract |
|
||
| catalog_sha256 | Exact non-secret approved catalog target specification |
|
||
| source_bundle | Absolute owner-resolved files and immutable SHA256 hashes for engine dispatcher, Platform mediator, typed CCR module and policy/auth source |
|
||
| backend_origin / cluster_uid | Exact reviewed estate OpenBao origin and railiance01 identity |
|
||
| operations | Ordered exact HTTP method, normalized path and canonical JSON/text payload hash for each read/write/readback; no user supplied body |
|
||
| operation_deadline | Upper bound no later than approval, PDP, caller and original/fresh source validity; task-token expiry may be earlier |
|
||
| backend_budget | TTL at most 120s, at most twelve uses, no renew/child/wrap/unlimited uses |
|
||
| expected_before / expected_after | Owner-authenticated revision/state digests, never secret contents |
|
||
|
||
The action subject/type/purpose/tenant are the actual initiating principal's
|
||
verified identity and existing catalog purpose, not inferred from engine service
|
||
identity or arbitrary environment fields. Founder-attended invocation names that
|
||
principal and the representation explicitly. The mediator refuses absent owner
|
||
identity correspondence; a caller-authored subject string is not evidence.
|
||
|
||
The signed/served approval must bind the same approval-free execution context;
|
||
the PDP must return the exact canonical request and matching approval digest.
|
||
Publish and pin the updated access-engine policy package/version that evaluates
|
||
this context; an older package accepting only a catalog ID cannot authorize it.
|
||
No package default or implicit semantic acceptance is assumed. Approval identity,
|
||
decision ID and canonical request digest travel together in a non-secret request
|
||
descriptor. They are provenance until freshly validated and CAS-consumed.
|
||
|
||
CCR immutable digests exclude only explicitly owner-defined append-only review/
|
||
evidence fields; policy bytes, role bytes, typed mutation body, target coordinates,
|
||
grant/deadline and source revisions are included. Approval IDs added after
|
||
issuance do not change their own binding. The mediator independently reconstructs
|
||
all operation bodies from owner sources and compares the resulting digest list.
|
||
It accepts no caller ACL template, path override, shell argv or raw JSON body.
|
||
|
||
## Execution mediation and consume ordering
|
||
|
||
Use the existing attended Platform front door, invoked by the founder, to run a
|
||
new fixed Platform owner mediator. No daemon, broad new service or new bearer
|
||
delivery to agents is required. The caller engine prepares a non-secret request
|
||
and receives a non-secret receipt. The founder-held admin credential exists only
|
||
inside the reviewed Platform child, through the existing attended route. It is
|
||
never exported to the caller. Neither local receipt files nor user-authored
|
||
`approved` labels authorize the child.
|
||
|
||
The Platform child runs the reviewed Secrets Engine authorization/consume code
|
||
at the last responsible execution boundary; the CLI preparation path does not
|
||
consume early and then hand an untrusted receipt to a more privileged process.
|
||
The backend mediation contract must preserve engine ownership of lifecycle
|
||
validation while Platform owns its OpenBao credential and typed native primitive.
|
||
|
||
1. Load pinned sources and exact non-secret request; check typed CCR/grant owner
|
||
approval, catalog/subject/operation correspondence, source/state expectations,
|
||
owner-only execution state directory, supported method/body allowlist and
|
||
local clock/deadline. No protected OpenBao call occurs yet.
|
||
2. Atomically reserve the operation in a dedicated engine execution ledger,
|
||
uniquely keyed by `approval_id + request_digest`; the operation UUID is inside
|
||
the digest. Bind the authenticated caller, sources and deadline. This is
|
||
execution state, not the audit outbox or a local evidence-presence gate.
|
||
Multiworker locking uses SQLite `BEGIN IMMEDIATE` and uniqueness constraints;
|
||
only the reservation creator may proceed.
|
||
3. Use `authorize_action` / approval-claim validation / `check_decision` / exact
|
||
envelope validation, with pinned policy package and trusted application time.
|
||
Recheck freshness after network calls. Require served human control for the
|
||
production grant; missing claims, unreachable PDP or mismatched binding refuse.
|
||
4. Call `require_production_consume` once for that exact binding. Require
|
||
confirmed matching consumption. Persist `consume-confirmed` before any
|
||
protected OpenBao login, custody read, capability check or mutation. A consume
|
||
timeout is `unknown`; never treat it as absence or automatically retry.
|
||
A consumed/idempotent response without the same live local admission is not
|
||
permission to execute again. Retrying a completed operation returns only its
|
||
existing receipt; an incomplete/unknown operation requires owner reconciliation
|
||
and a fresh exact approval for any additional backend action.
|
||
5. After consume, perform the existing attended in-memory client-secret source
|
||
and CCR38 service login without provider fallback. Verify its exact login-self
|
||
policy/TTL/uses and service identity; keep this session self-only. Failure
|
||
consumes the trial without metadata effects; no implicit re-login or retry.
|
||
6. In the Platform child, use the separately authorized founder-held identity
|
||
only to materialize the reviewed exact ephemeral task policy/token. OpenBao's
|
||
token parent remains platform-owned; the CCR38 token cannot mint it. The grant
|
||
binds every policy/token create/revoke/delete step to this operation and its
|
||
cleanup. Do not give a standing `sys/policies/acl/*`, `auth/*/role/*` or
|
||
`auth/token/create` grant to the engine service.
|
||
7. Recheck decision, approval and operation deadlines before **each** protected
|
||
request. The task token is nonrenewable, no default/identity policies and
|
||
grants only read/create/update on the one approved ACL policy path and the
|
||
one approved workload auth-role path, plus revoke-self. No child token, mount
|
||
administration, KV data/metadata, SecretID or sibling role is admitted.
|
||
OpenBao ACL narrows endpoint access; it cannot restrict the HCL/role payload.
|
||
Only the mediator's independently reconstructed body equality enforces that.
|
||
8. Use an injected restricted backend adapter for the exact ordered native
|
||
calls. Compare current expected state; write only the approved generated body,
|
||
read it back, and compare it. No `OpenBaoClient._run_ok` raw failure reaches
|
||
receipts: capture/discard backend output and use closed reason codes. A generic
|
||
403/timeout or “already exists” is not successful verification.
|
||
9. Finally self-revoke the task token and login-self token, then retire only the
|
||
exact per-operation grant policy under founder-owned cleanup authority.
|
||
Record confirmed revocation/absence separately from native apply status.
|
||
Policy retirement alone does not prove issued-token revocation.
|
||
|
||
Task-token use budget is declared before issuance: two before-state reads, two
|
||
writes, two readbacks and one self-revoke (seven uses), plus at most four explicit
|
||
metadata capability checks and one reserved cleanup use (twelve total). Failed
|
||
backend calls count; never retry to evade limits. The immutable
|
||
`backend_budget.ttl_seconds` declares the requested task-token TTL (30–120s).
|
||
The native adapter does not dynamically recompute or shorten that declaration.
|
||
Before issuance, conservatively require at least the declared TTL plus the 5s
|
||
cleanup reserve within the remaining operation/approval/PDP authority, including
|
||
clock uncertainty. The requested and explicit maximum native token TTL equal
|
||
the declared budget; returned native TTL must be positive and at most that budget.
|
||
The token can expire before the operation deadline; failure or expiry stops work
|
||
without remint. The signed operation deadline remains no later than approval,
|
||
PDP, authenticated caller and both original/fresh source validity. Access's
|
||
trusted source path independently requires the same TTL-plus-5s fit after source
|
||
I/O and an operation interval no longer than 120s. Therefore a structural 120s
|
||
backend budget cannot fit inside a 120s authority interval with the reserve and
|
||
refuses. No renewal or remint extends the operation. CCR38 keeps its
|
||
own 300s/eight-use limit; login-self checks and revoke fit its separate budget.
|
||
|
||
## Failure, replay and recovery
|
||
|
||
Ledger states: `reserved → consume-confirmed → authenticating → grant-issued →
|
||
applying → verified`, followed by a separate `cleanup-confirmed` or
|
||
`cleanup-unconfirmed` result. Every external call is durably recorded as about
|
||
to execute before the call, then recorded complete afterward. Crash between
|
||
those steps is `unknown` rather than presumed failure. Concurrent duplicate
|
||
requests never both reach consume or OpenBao.
|
||
|
||
An operation can end refused before consumption, consumed-without-backend-work,
|
||
partially-applied, backend-outcome-unknown, verified-cleanup-unconfirmed or
|
||
verified-cleanup-confirmed. No incomplete state permits blind reapplication.
|
||
Cleanup continues when an action deadline expires, under the original explicit
|
||
cleanup obligation, without new business writes. Read/repair of changed target
|
||
state requires a fresh exact owner-reviewed recovery approval; no old snapshot
|
||
overwrites concurrent owner edits. Hard interruption uses the Platform owner's
|
||
bounded revocation/cleanup procedure with protected accessor custody. Ledger
|
||
and evidence carry only fingerprints/metadata; the raw accessor/token never
|
||
leaves owner memory or the separately admitted owner-private recovery channel.
|
||
|
||
Revocation failure blocks owner acceptance and T06 closure, even if TTL will
|
||
eventually expire. Emit non-secret atomic local lifecycle events/outbox records
|
||
at the ledger boundaries; audit-core outage does not suppress cleanup. Do not
|
||
claim an empty archive proves no effects, or that local ledger state is founder
|
||
approval. Owner acceptance requires native before/after equality, denied sibling
|
||
actions and verified cleanup under the same exact trial.
|
||
|
||
## Implementation hooks and closure tests
|
||
|
||
- `engine_auth.py`: staged readiness guard now. Later separate identity-session
|
||
and execution-adapter objects; generic service-jwt client remains self-only.
|
||
- `cli.py`: staged guard passed through backend-action approval callers now.
|
||
New explicit execute-ccr preparation/mediated dispatch later; ordinary apply
|
||
does not acquire new rights merely because the login contract is configured.
|
||
- `authorization.py`, `approval_consume.py`: execution context participates in
|
||
both approval-free binding and full PDP digest; owner child returns the actual
|
||
`ConsumedApproval`, not only an `AuthorizedAction` ID.
|
||
- New `execution_contract.py`, `execution_ledger.py`, `mediated_backend.py`:
|
||
strict closed schema, deterministic compiler, separate admission state,
|
||
pinned owner-native adapter and non-secret outcome types. Ordinary subprocess
|
||
command execution is not an implementation of this adapter.
|
||
- Platform owns task-execution-grant CCR/typed validator/token materializer/
|
||
fixed execution child/recovery hooks. Approval Engine/PDP owners acknowledge
|
||
the request+claim/package correspondence; secrets-engine cannot assent for them.
|
||
|
||
Counterexamples required before shipping: login-only auto provider consumes
|
||
nothing; stale/forged grant file changes nothing; same name with changed HCL or
|
||
role body refuses; wrong method/sibling policy/role/KV/SecretID/child-token/mount
|
||
write refuses; changed source/caller/tenant/catalog/deadline alters the bound
|
||
request and refuses; consume conflict/unavailability/timeout produces zero
|
||
backend calls; idempotent approval consumption never causes a second execution;
|
||
concurrent calls admit one worker; postconsume login failure never falls back;
|
||
expiry during a multi-call operation stops new mutations but permits scoped
|
||
cleanup; readback mismatch/transport failure is incomplete; revoke failure
|
||
never passes; partial apply and crash after write require explicit recovery.
|
||
|
||
Current staged readiness regression suite: eight new checks plus 28 existing
|
||
auth, consume and privileged-evidence checks pass (36 total). No native action
|
||
was performed. Full entitlement implementation, policy-package publication,
|
||
independent Platform/Secrets Engine/identity/approval/PDP acknowledgement and
|
||
exact founder-scoped native proof remain required. A successful metadata trial
|
||
alone would not cover value provision or exec delivery.
|
||
|
||
## Existing commands and the next contract they actually need
|
||
|
||
| Existing operation | Current login-only outcome | Contract required after the metadata pilot |
|
||
| --- | --- | --- |
|
||
| apply / apply_plan | Readiness refusal before consume; no backend | A separate exact policy/AppRole/mount structure compiler and grant for each admitted PlanAction. The workload-kv-read execute-ccr pilot does not implement these generic AppRole/mount calls. |
|
||
| provision / rotate | Readiness refusal before consume; no KV writes | Exact KV v2 data path, declared field set, expected CAS version, no sibling changes, owner-held opaque input handle, confirmed new version and separately admitted revoke/cleanup. Never expose secret plaintext or guessable secret-value hashes in request/evidence. |
|
||
| verify positive/negative | Readiness refusal before consume; no custody reads | Metadata capability probe plus, only when required, exact declared field-presence read through a restricted owner adapter. Return booleans; malformed/denied/transport error is inconclusive, not absence. Independent negative identity and exact deny paths are separate bindings. |
|
||
| exec / wrap | Readiness refusal before consume; no read/injection/wrap | Exact custody path/field, signed executable/cwd/destination/exec-owner binding and duration, no user arbitrary shell, owner-mediated read and approved process-local delivery, or exact one-use wrap creation/unwrap/expiry; self revoke and injected material cleanup. |
|
||
| handoff | Readiness refusal before consume; no role-id/SecretID mint | Exact existing AppRole and single-use SecretID creation, bounded TTL/uses and owner-admitted delivery; native role-id lookup/SecretID mint/login/revoke budget and no caller standing credential. |
|
||
| revoke / native suspend/deactivate | Readiness refusal before consume; no backend revocation/deletion | Exact policy/role/lease/accessor target and expected state, admitted lifecycle effect, rollback/custody preservation and denial readback. Token/lease handles remain owner-private; receipt fingerprints cannot authorize another handle. |
|
||
| session revoke | Readiness refusal before its backend; legacy stance remains | Exact already-held protected handle, typed token/lease revocation and independent subject/authority binding; no arbitrary accessor or lease-prefix wildcard. |
|
||
| destroy | Existing live destroy refusal remains | Exact destructive dual-control contract and custody/backup/version proof; metadata pilot supplies neither. |
|
||
| compromise / reactivate local state | Existing local state gate remains | No new OpenBao entitlement; these local records do not establish actual native revocation/reactivation. |
|
||
| service-auth probe-openbao | Existing explicit self-only identity trial | CCR38 positive/negative/lifetime/revoke proof, with no implied entitlement to any catalog lane. |
|
||
|
||
Each subsequent contract needs its own owner-reviewed compiler, request/PDP
|
||
package binding, method/body enforcement, consume admission, TTL/use budget,
|
||
failure/recovery rules and native counterexamples. No success in the initial
|
||
metadata pilot unblocks these rows by default. Full T06 adoption requires the
|
||
actual selected engine production workflow to run through the corresponding
|
||
reviewed entitlement, with native effect, denial/no-fallback and cleanup proof.
|
||
|
||
## Source preparation: typed request propagation (2026-10-11)
|
||
|
||
The staged ExecutionContract descriptor is deliberately a closed, immutable
|
||
non-secret value. `parse` validates shape and captures JSON; every `as_dict`
|
||
revalidates even directly constructed instances and returns a detached copy.
|
||
It is not an owner approval, source authenticity attestation, principal
|
||
representation or proof that the requested origin is admitted. Compiler and
|
||
mediator must independently reconstruct and authenticate sources and bodies.
|
||
|
||
The exact proposed descriptor serialization refines the table above:
|
||
|
||
- Digests use `sha256:` plus 64 lowercase hexadecimal characters. UUIDs use
|
||
canonical lowercase hyphenated form. CCR and grant IDs must be distinct.
|
||
- `source_bundle` has exactly engine_dispatcher, platform_mediator, typed_ccr and
|
||
policy_auth; each has exactly an absolute normalized `path` and `sha256`.
|
||
This parses paths without reading files; pin verification remains unimplemented.
|
||
- `operations` is an ordered list of exactly method/path/payload_sha256 objects.
|
||
It accepts only GET/PUT metadata policy and auth-role endpoint shapes, with
|
||
normalized relative paths. This schema does not reconstruct bodies or prove
|
||
that all before/write/readback operations occur or target exactly two paths.
|
||
A later owner compiler must enforce those semantics before any execution.
|
||
- `backend_origin` is an explicit-port HTTP(S) origin without credentials, path,
|
||
query or fragment. Syntactic acceptance creates no approved route or network
|
||
call. Estate pinning and server authentication remain mediator obligations.
|
||
- `backend_budget` has exactly ttl_seconds (integer 30–120) and uses (integer
|
||
1–12); booleans fail. Listed calls must leave at least one cleanup use.
|
||
Renewal, remint and live per-call accounting remain outside this descriptor.
|
||
- expected_before/expected_after are non-secret state digests; operation_deadline
|
||
is timezone-aware. Before claim, refuse expired deadlines using the existing
|
||
trusted-time window. After claim and Check, refuse a deadline later than either
|
||
served approval or decision expiry. A future mediator must recheck before every
|
||
effect and provide separately authorized cleanup after business expiry.
|
||
- catalog_sha256 is a descriptor-local sorted compact JSON fingerprint of the
|
||
complete existing context.catalog_target, distinct from canonical PDP digests.
|
||
Resource catalog ID/stage, subject, tenant and purpose remain bound separately
|
||
through the unchanged canonical request hashing implementation.
|
||
|
||
The same typed descriptor passes through build_action_request, _expected_request,
|
||
resolve_approval_observation and authorize_action. Any contract on apply or another
|
||
legacy action refuses. execute-ccr without a typed valid contract, prod stage or
|
||
platform tenant refuses. execute-ccr always binds human_control=true, even on a
|
||
legacy bootstrap catalog, and cannot use an unserved/demo fallback. Invalid or
|
||
catalog-mismatched descriptors refuse before claim/PDP/CAS. Returned requests are
|
||
independent copies; later caller mutation cannot change the stored descriptor.
|
||
|
||
99 focused source tests pass across context propagation, the existing approval
|
||
join, decision validation, readiness, consume and engine auth. New tests exercise
|
||
all four actual builders, actual consume request formatting and claim-free/full
|
||
joins; mutation of operation, CCR/grant/source/body/method/path/budget/deadline/
|
||
estate/state/principal/purpose/catalog/tenant/stage refuses the original approval.
|
||
Transport/PDP stand-ins deliberately supply hypothetical human control and an
|
||
ALLOW: they prove propagation, not a shipped policy or real principal admission.
|
||
|
||
No execute-ccr CLI, identity-owner verified initiating-principal interface,
|
||
new access policy package, typed owner compiler, task-grant materializer,
|
||
independent authenticated consume-confirmation integration, durable admission
|
||
journal, restricted backend, per-call deadline checks or native entitlement is
|
||
supplied by this stage. Existing cfg subject strings remain legacy provenance;
|
||
this change does not make them verified human representation. Existing generic
|
||
consume receipt validation limitations remain explicitly separate. service-jwt
|
||
readiness guard/default and ordinary actions remain unchanged. T01 stays in
|
||
progress, T03 stays wait, WP13 stays proposed, and WP8-T06 stays open.
|
||
|
||
## D3 wrapped-delivery primitive and its separate entitlement
|
||
|
||
D3 P3=b chooses response-wrapped issuer delivery with one-time unwrap. The
|
||
existing wrapped-handoff primitive now verifies native TTL/accessor/creation-path
|
||
metadata and reserves a private immutable recipient file before minting. Native
|
||
KV-v2 creation paths include the `data/` endpoint; caller-requested CLI paths
|
||
cannot substitute for returned metadata. Failed mint/write or an altered
|
||
reservation retains an uncertain attempt and never silently overwrites/remints.
|
||
|
||
A disposable local OpenBao integration proves the exact source path,60-second
|
||
TTL, successful first unwrap and specific HTTP400 consumed-token replay denial.
|
||
This proves backend semantics, not the estate's first delivered lane, audited
|
||
initiating principal, consumer identity, independent owner acceptance or cleanup
|
||
under crash/revocation. Production CLI wrapping remains fail-closed. Existing
|
||
metadata execute-ccr contracts expressly exclude wrap/KV value delivery and cannot
|
||
be borrowed for this purpose. A separate exact owner-admitted delivery operation
|
||
must bind actor,path,audience,deadline,authorization/consume state,destination,
|
||
audit,replay/crash recovery and rollback before native adoption. The prepared
|
||
D2 repository-issuer lane remains attended until its own reviewed execution and
|
||
independent destination/adoption proofs pass.
|