ops-mason/docs/keycape-client-custody-attended-verification.md
tegwick 969fe218df Prepare independently reviewed native custody capability verification
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 14:16:58 +02:00

10 KiB
Raw Blame History

MASON-WP-0009-T05: founder-run native capability proof

Preparation only. No credential was read, native session started, TokenRequest issued, policy widened, KV GET performed, or route activated during preparation. The existing T05 task stays in progress. Formal ops-mason owner source review, founder attendance approval for the exact staged digest, and live native receipts remain necessary. An --approval-reference argument records the founder's prior disposition; it cannot approve itself or establish runtime isolation.

Exact reuse and authority

The existing verifier scripts/verify-fluid-telegram-lane.sh demonstrates attended OIDC and value-free capabilities, but accepts every failed login as a nonmember refusal. It is not reused as proof. secrets-engine's OpenBaoOidcClientSecretSource demonstrates bao login -no-store -format=json, but its next operation reads CLIENT_SECRET, so that provider is not invoked here. Existing scripts/bao-session.sh grant MASON-WP-0009-T05 provides query authority: 45m ops-mason-survey, founder minting, explicit data denies, no policy/role/token write. The grant may also carry default; all other token/identity policies are refused. No new policy or credential owner is proposed.

Source inspection found neither CCR-0033's ESO reader nor CCR-0034's operator reader grants sys/capabilities-self (both omit default). Calling capabilities under their own tokens would prove endpoint denial, not the required path matrix. The verifier uses the survey grant's existing sys/capabilities to query the native lane token in memory. The lane token never enters argv, environment, receipts or a token helper. All HTTP calls use a fixed loopback endpoint, reject redirects/proxies and allow only login, self lookup, capability query and self revocation endpoints. No secret or metadata GET exists in the HTTP interface.

Credential routing was read first: WARDEN_MEMORY=0 warden route show openbao-structure-survey --json. That entry routes to ops-mason and is not a credential vending surface. The initial default route lookup printed routing metadata but failed to append its memory event outside this sandbox; no escalation was requested. There is no active operator route in current ops-warden source; the previously reported draft proposal must be accepted there separately.

Concrete attended proposal

Proposal ID: MASON-WP-0009-T05-native-capability-proof-20261011. Target: existing OpenBao on the railiance01 loopback tunnel and, for ESO only, railiance01's external-secrets/external-secrets ServiceAccount TokenRequest. Expected result: two exact native read-only matrices, confirmed lane/query self-revokes, and a separately accepted nonmember refusal. Estimated software cost EUR 0; bounded founder browser/session time around 10–20 minutes. Risk: short-lived read-capable session tokens exist in founder process memory, and ESO TokenRequest briefly mints a 10m JWT for the existing ServiceAccount. No secret value is requested. No policy or deployment changes occur. Actual runtime isolation remains unverified; exact source review and founder attendance are procedural gates, not an autopilot grant.

After ops-mason accepts the source and the supervisor records the exact digest and scoped founder go, the founder runs from their terminal. For each mode, obtain a fresh existing survey grant through the established session helper. Do not hand an agent an operator token or inspect either session file in chat. Use a new output path each time and a private browser session for the chosen human. The verifier requires TTY attendance, rejects ambient BAO_TOKEN/VAULT_TOKEN and namespaces, and refuses a source digest different from the reviewed digest.

python3 scripts/verify-keycape-client-custody.py operator \
  --attended --approval-reference '<recorded founder disposition>' \
  --approved-source-sha256 '<reviewed script digest>' \
  --survey-token-fd 3 --receipt '<new absolute 0600 receipt path>' \
  3< "$HOME/.claude-bao-token"

Repeat with eso and then nonmember, fresh grant and receipt for each. Opening the existing grant FD is performed only in the founder terminal. The verifier closes it and self-revokes the query token. Existing bao-session.sh revoke is then used by the founder to remove the stale grant file; its printed success is not accepted as revocation evidence because that helper suppresses revoke errors. The verifier's native revoke receipt is the relevant evidence.

For ESO the runner first captures node metadata via fixed ssh -oBatchMode=yes railiance01 'kubectl get nodes -o json' and refuses any estate other than one node with address 92.205.62.239. It then captures only the exact audience openbao, 10m ServiceAccount TokenRequest JWT in memory and logs in at auth/kubernetes/login as external-secrets-keycape-secrets-engine-openbao. No Secret object, pod or controller token is read. The independently minted lane token is revoked; the running ESO controller's token is never touched. If the node metadata does not expose the documented IP, stop for platform owner source review of the estate pin; do not loosen the check at runtime.

Receipt acceptance and remaining proof gates

Schema mason-client-custody-native/v1 includes exact script digest, proposal reference, mode, timestamp, accessor SHA256, matrix and cleanup booleans. Files are reserved with O_EXCL/O_NOFOLLOW and mode 0600. Raw CLI/HTTP errors and auth responses are never copied into receipts. Capability queries must return every named path; missing rows, unexpected default/identity policies, wrong role, wrong ESO namespace/name, TTL over 5m/15m, any data write or parent/sibling/metadata capability fail closed. capability_matrix_observed means observations passed; owner_acceptance: pending is retained even when exit status is zero.

Operator and ESO acceptance requires ops-mason's actual owner review against the existing CCRs/source receipts, native role/policy/TTL observations, exact read-only matrix and both lane/query revocation confirmations. Operator role/claims remain grounded in the existing approved CCR-0034 binding, not a CLI role label chosen by the human. ESO metadata must show the fixed service account and role.

Nonmember never auto-passes. A semantic groups mismatch from the OIDC callback is only group_refusal_candidate; transport failure, cancellation, HTTP 403 alone, wrong issuer, malformed JSON, or MFA failure is unproved_login_failure. An unexpected successful nonmember login is revoked and fails the run.

Before ops-mason accepts check 3, key-cape supplies a value-free owner-authenticated identity record proving the same attempted subject is human, outside net-kingdom-admins and aal2, with validated issuer/signature/expiry; platform supplies a correlated native OpenBao callback refusal specifically for groups. The records must bind the same attempt using an owner-verified request/flow correlation digest, not merely nearby timestamps, a user-selected nonmember mode or decoded JWT claims. Include owner record ids/digests, observed time, subject fingerprint, issuer validation boolean, group_member=false, aal2=true, callback endpoint, refusal class and no token issued. The identity/flow digest must be produced and verified inside the owners' attended/audit surface; the runner does not possess the upstream OIDC JWT and cannot manufacture this evidence. If owners cannot produce this correlation, check 3 stays unproved. Never publish raw JWTs, callback URLs/codes/states, cookies, session ids or authentication errors.

This is separate from KEY-WP-0039 human/coding-agent JWT boundary evidence and secrets-engine's service identity denial matrix. Those do not prove this reader's nonmember binding. The existing successful service-jwt exchange and self-revokes remain accepted prior evidence; do not discard or regenerate them merely to complete these different gates. ops-warden may activate the pointer only after all T05 required owner-native receipts are actually accepted.

Cleanup and failure recovery

On every obtained lane token, including policy mismatch/unexpected nonmember success, attempt lane revoke-self, then query revoke-self. A failed revoke changes result to unproved. A query token that fails its identity check is not trusted for further calls; record no cleanup confirmation and use the established founder revocation path. Callback timeout/malformed native auth may leave a token whose handle was not recovered: no cleanup success is claimed; the role's 5m/15m TTL bounds it and the platform owner decides recovery. No policy is widened to retry.

The Kubernetes TokenRequest JWT cannot be individually revoked by this procedure; it expires after 10m and is never persisted. Do not delete the shared ServiceAccount or revoke the controller's token as cleanup. No KV rollback exists because no KV request occurred. Session cleanup is the rollback; any unconfirmed cleanup is explicit open evidence and blocks acceptance until owner resolution.

Corrected source revision — 2026-10-11

The prior immutable stage is retained at /tmp/mason-jwt-capability-next-stage (manifest e5d4781c6280c3bc8a3930c63cfb563f59752465720d6c2c3e95eb0551f8e949). Independent source review requested changes: its HTTP202/text response could falsely confirm revocation and missing native accessors could hash the empty string. This revised source rejects every unsupported status/content/body/error shape. Lookup/capability/Kubernetes-login require HTTP200 application/json and endpoint-specific object data/auth, no errors member, with bounded headers/body. Revoke succeeds only on HTTP204 with an empty body. Error envelopes never imply cleanup; a recovered token from a mixed login envelope is retained solely for finally cleanup. Nonempty bounded typed actual accessors are required before fingerprinting; empty/malformed query accessors still revoke a scope-validated query token, and empty/malformed lane accessors still revoke recovered lane tokens.

TTL is a positive integer within the declared bound, with booleans refused. CLI stdout/stderr are captured while running with a 64KiB limit per stream; excess or timeout kills the local process group and leaves native outcome unproved. No unlimited capture is described as bounded. The exact approved source digest is checked before receipt/token/native IO. 29 offline tests cover these corrections, actual relative redirect refusal with hostile proxy settings, synthetic oversized subprocess output, valid native204 cleanup versus bad202, missing-accessor cleanup, and mixed-error session recovery. Source corrections are not a new native success, owner acceptance, founder go or adoption proof.