--- id: SECRETS-WP-0011 type: workplan title: "Multi-lane exec-owner delivery" domain: infotech repo: secrets-engine status: active flavor: implementation owner: claude-code topic_slug: netkingdom created: "2026-09-23" updated: "2026-09-23" related_workplans: - SECRETS-WP-0009 - HFACT-WP-0001 state_hub_workstream_id: "ecb0643d-bad7-5d63-b340-e77ab9579b0a" --- Demand: SECRETS-WP-0009-T03. A configured `exec_owner` child today receives only the catalog's fixed non-secret environment plus one delivered field (`exec_delivery.py:233`). The Glas metered owner (`rein-aharness metered-once`) also needs the Activity Core worker token to claim its queue row. Operator decision 2026-09-23: generalize secrets-engine delivery rather than add a rein-side token-file reader. Operator decisions 2026-09-23: one decision chain per lane (no flex-auth package change), and the worker-token lane uses ordinary lane approval, not human control, since it only lets a worker claim jobs. Scope: one exec may deliver fields from more than one cataloged lane into one catalog-bound owner. The primary lane stays the one named by `--catalog`; the others are companions declared on the primary's `exec_owner`. Invariants carried over unchanged: - Every delivered value comes from a cataloged lane through that lane's own AppRole, in memory only, with self-revoking sessions and redaction. - Each lane's read is its own protected action. No lane's approval, decision or consume covers another lane. - The binding stays exact. Companion lanes, fields and env names are part of the `exec_owner` digest, and the command is refused if any differ. - Fail closed as a set. If any lane is unapproved, missing, denied or fails, no child is started and no value is kept. ## Define the companion contract ```task id: SECRETS-WP-0011-T01 status: done priority: high state_hub_task_id: "820514c9-f54d-530e-a45c-9a04486831c2" ``` Extend `exec_owner` with `companions: [{catalog, field, env}]`. Validate at catalog load: each companion is an existing `kv` lane with the same stage, a declared field, an env name that doesn't collide with the fixed environment or another delivery, and `exec-env` in its delivery modes. Companions are refused on pending owners. Document the contract in `docs/` and the SCOPE exec-time delivery section. ## Gate and fetch each lane separately ```task id: SECRETS-WP-0011-T02 status: done priority: high state_hub_task_id: "33d0fc37-5f23-53a6-9456-f8bd1cc984f9" ``` Run the existing approval-claim / CheckRequest / consume chain once per lane, with each lane's own action and resource, before any value is fetched. Then fetch every value, re-check the binding digest, and spawn once with all values injected and redacted. Evidence records each lane's decision reference and session cleanup. Any failure starts no child and reports which lane refused. ## Tests and throwaway OpenBao proof ```task id: SECRETS-WP-0011-T03 status: done priority: high state_hub_task_id: "52a0ff40-81d6-589a-b05f-3e5f55f81a81" ``` Unit tests: validation, digest coverage, collision, stage mismatch, partial approval, one lane denied after another is fetched, redaction of every value, cleanup of every session. Integration: two KV lanes delivered to one owner on a throwaway OpenBao dev server. ## Catalog the Activity Core worker token lane ```task id: SECRETS-WP-0011-T04 status: wait priority: high blocking_reason: "Needs activity-core owner assent to source ACTIVITY_CORE_WORKER_TOKEN from OpenBao (ESO) instead of a hand-generated Kubernetes Secret." state_hub_task_id: "cf465065-de7a-5d9c-bc80-fee16ffef70d" ``` Request activity-core to move the worker token into OpenBao custody, synced to `actcore-runtime-secret` by their existing `openbao-activity-core` store. Then catalog a read lane for the metered owner and add it as the Glas lane's companion. This coordinates with activity-core's multi-worker request (hub message `914853d9`): a dedicated metered worker token would use the same lane shape. ## Implementation return — 2026-09-23 T01–T03 done. `exec_owner.companions` and `delivery_config.companion_of` are validated at catalog load. `resolve_companions` runs before any gate, and `cmd_exec` gates each lane separately (its own evidence, stance, approval and consume) before opening the backend. `exec_with_secret` reads each lane through its own AppRole, starts no child if any lane fails, re-checks the binding, and redacts every value. Suite: 465 passed. Integration on a throwaway OpenBao: 2 passed (two lanes to one owner; the primary AppRole is denied the companion path). Contract: `docs/exec-owner-binding.md` § Companion lanes. T04 waits on activity-core (hub message `6e694682`).