Add companion lanes to catalog-bound exec owners (SECRETS-WP-0011 T01-T03)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

A configured exec owner may receive fields from other consenting kv lanes.
Each lane is gated, consumed and read through its own AppRole; any refusal
starts no child. Companions are part of the owner digest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 226514@bnt-lap001
Assistant-Session: 26ba103d-05fe-45a1-9cd7-9475bf239df6
This commit is contained in:
tegwick 2026-09-23 17:27:09 +02:00
parent 452203b19b
commit bc58184d71
10 changed files with 449 additions and 29 deletions

View file

@ -91,3 +91,36 @@ uv run --extra dev --with 'PyJWT[crypto]>=2.7,<3' python tools/exercise_approval
Receipt: `docs/evidence/2026-09-10-exec-owner-approval-exercise.json`.
Native custody, exact operator group/file delivery, real human/audit/service path,
accepted factory configuration and paid execution remain open.
## Companion lanes (SECRETS-WP-0011)
A configured owner may list extra lanes to deliver alongside the primary field:
```yaml
exec_owner:
status: configured
# command, cwd, environment, files as above
companions:
- {catalog: <kv-lane-id>, field: <declared-field>, env: <ENV_NAME>}
```
The companion lane must consent in its own catalog entry with
`delivery_config.companion_of: [<primary-lane-id>]`. It must be a `kv` lane in
the same stage, declare the field and `exec-env`, and bind no exec owner of its
own. Env names must be unique, must not match the fixed environment or the
primary field's name, and must not use loader or engine credential prefixes.
Pending owners cannot list companions.
Companions are part of the owner binding, so changing a companion's lane, field
or env name changes the owner digest and invalidates earlier decisions.
At exec time, companions are resolved before any approval is consumed. Each
lane then gets its own privileged evidence record, stance, approval and
consume for action `exec`. No lane's decision covers another lane. After every
gate passes, each value is read through its own lane's AppRole session. A
failure on any lane starts no child. The binding is checked again after the
reads, and all values are injected together and redacted from the output.
Proof: `tests/test_exec_owner_companions.py`, plus
`tests/test_integration_companions.py` on a throwaway OpenBao (two lanes, one
owner, and the primary AppRole denied the companion path).