Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02669-87ee-7a31-b111-edc95a16e0fa
5.6 KiB
Ephemeral custody projection lifecycle
scripts/custody-projection.py is the platform-owned interface for future
short-lived, mount-only credential exercises. It replaces copying the dated
audit-core-whitehat-e2-credentials*.py, HCL, and YAML files. Those dated
artifacts are retained only as historical evidence and cleanup references for
their terminal engagement ids.
The lifecycle does not authorize an engagement. A projection contract is
accepted only when it is approved, lasts at most 900 seconds, binds exact
target and runner revisions, names exactly two distinct sender identities, and
uses engagement- and target-bound KV paths. Kubernetes and OpenBao resource
names are derived from the SHA-256 of the engagement id; no previous engagement
name is copied into a new manifest.
Versioned interfaces
| Interface | Schema | Producer | Consumer |
|---|---|---|---|
railiance.custody-projection-contract v1 |
schemas/custody-projection-contract.schema.json |
engagement/platform owners | platform lifecycle and Whitehat review |
railiance.custody-broker-readiness v1 |
schemas/custody-broker-readiness.schema.json |
whitehat-security |
platform pre-projection gate |
railiance.custody-projection-receipt v1 |
schemas/custody-projection-receipt.schema.json |
platform lifecycle | Whitehat broker adapter and operator |
railiance.custody-cleanup-receipt v1 |
schemas/custody-cleanup-receipt.schema.json |
platform lifecycle | engagement owner and evidence record |
Receipts contain no bearer, password, value-derived fingerprint, Secret data, or registry body. The projection receipt carries an opaque lease id, canonical receipt id, exact identity roles and mount paths, contract digests, times, resource names, and Kubernetes UIDs. Changing any canonical field invalidates the receipt id.
Broker gate owned directly by Whitehat
The owner does not need a coding agent to translate the request. Given the final projection contract, it can run:
python3 scripts/wp0025-broker-readiness.py show \
--contract /path/to/final-projection-contract.json
python3 scripts/wp0025-broker-readiness.py verify \
--contract /path/to/final-projection-contract.json \
--consumer-root /path/to/whitehat-security
python3 scripts/wp0025-broker-readiness.py approve \
--contract /path/to/final-projection-contract.json \
--consumer-root /path/to/whitehat-security \
--reviewer whitehat-owner
Verification is closed to
src/whitehat_security/platform_custody.py and
tests/test_platform_custody_adapter.py. It records the full consumer commit,
adapter SHA-256, focused-test result, exact platform schema hashes, mount paths,
and cleanup support. Approval posts the canonical receipt directly to State
Hub as from_agent=whitehat-security. A stale schema, changed projection
contract, wrong engagement, wrong target, absent adapter, or failing focused
test does not count.
The platform lifecycle queries that receipt before token generation and before the first mutation. State Hub coordination identity is not a secret transport; the receipt contains no secret.
Platform commands
The template at docs/custody-projection-contract.example.json intentionally
fails validation until every placeholder and the approval status are replaced
by the owners.
python3 scripts/custody-projection.py validate \
--contract /path/to/final-projection-contract.json
python3 scripts/custody-projection.py render \
--contract /path/to/final-projection-contract.json
python3 scripts/custody-projection.py preflight \
--contract /path/to/final-projection-contract.json
render is value-free and creates no resource. preflight reads only status,
metadata, names, image/readiness, and the broker receipt. It never reads a
Kubernetes Secret value.
Inside the approved projection interval, an attached custody operator may run:
python3 scripts/custody-projection.py project \
--contract /path/to/final-projection-contract.json \
--confirm '<engagement-id>:attended' \
--receipt-out /approved/non-repo/path/projection.json
If any mutation fails, the transaction invokes exact-scope cleanup. The
operator must not release the consumer until the returned projection receipt
passes its adapter. The receipt file is written mode 0600 even though it is
value-safe.
Status and normal attended cleanup require that receipt:
python3 scripts/custody-projection.py status \
--contract /path/to/final-projection-contract.json \
--receipt /approved/non-repo/path/projection.json
python3 scripts/custody-projection.py cleanup \
--contract /path/to/final-projection-contract.json \
--receipt /approved/non-repo/path/projection.json \
--confirm '<engagement-id>:cleanup' \
--receipt-out /approved/non-repo/path/cleanup.json
Status reports active, absent, partial, or mismatched. It compares live
resource UIDs with the receipt and does not interpret a connection error as
absence.
Expired cleanup entry point
cleanup-expired refuses until the contract expiry and still requires the
exact projection receipt and confirmation:
python3 scripts/custody-projection.py cleanup-expired \
--contract /path/to/final-projection-contract.json \
--receipt /approved/non-repo/path/projection.json \
--confirm '<engagement-id>:expired' \
--receipt-out /approved/non-repo/path/cleanup.json
This is an approval-ready reaper entry point, not a deployed scheduler. A
future controller still needs an owner-reviewed credential route, runtime
identity, alerting, and rollout decision. Application expires_at remains the
acceptance backstop; evidenced resource deletion remains the cleanup result.