Prepare independently reviewed native custody capability verification

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
This commit is contained in:
tegwick 2026-10-11 14:16:58 +02:00
parent c52be782d0
commit 969fe218df
7 changed files with 1000 additions and 1 deletions

View file

@ -0,0 +1,65 @@
{
"at": "2026-10-11",
"facts": [
"Neither exact custody lane policy grants sys/capabilities-self; no default policy. Existing survey sys/capabilities queries lane tokens in memory.",
"Existing survey source receipt display_name is token-claude-MASON-WP-0009-T04; required T05 grant is token-claude-MASON-WP-0009-T05.",
"ESO lifecycle uses separately issued ServiceAccount TokenRequest JWT, not controller or Secret token.",
"Nonmember CLI failure never accepted; independently trusted identity and same-flow owner-correlated groups refusal still required.",
"Previously accepted service JWT positive/login/revoke proofs retained; new preparation is distinct custody-reader evidence."
],
"founder_native_disposition": "not-yet-recorded",
"native_result": "not-run",
"owner_source_review": "not-yet-performed",
"preparation_result": "corrected-source-preparation",
"prepared_by": "custodian-codex delegated preparation for ops-mason; no owner authority assumed",
"previous_manifest_sha256": "e5d4781c6280c3bc8a3930c63cfb563f59752465720d6c2c3e95eb0551f8e949",
"prior_outcome": "independent source changes_requested; not native success",
"proposal_id": "MASON-WP-0009-T05-native-capability-proof-20261011",
"refinements": [
"endpoint-specific HTTP status/content/body/object/error handling; revoke only204empty",
"nonempty typed bounded native accessors; recovered-token cleanup",
"positive-integer TTL",
"incrementally bounded subprocess capture; no unbounded PIPE claim",
"approved digest guard before token/receipt/native IO"
],
"remaining_gates": [
"ops-mason owner formal exact-source review",
"supervisor exact source disposition and founder scoped native attendance",
"operator native capability matrix with cleanup",
"ESO native capability matrix with cleanup; platform TokenRequest approval",
"key-cape validated same-attempt human/nonmember/aal2 evidence and platform correlated bound-group refusal",
"ops-mason native owner acceptance and ops-warden pointer activation"
],
"routing": {
"initial_default_memory_append_failed_outside_sandbox": true,
"memory_disabled_read_succeeded": true,
"mode": "pointer",
"route": "openbao-structure-survey",
"warden_executes": false
},
"safety": {
"commits": 0,
"credential_reads": 0,
"credential_requests": 0,
"foreign_owner_reviews": 0,
"native_actions": 0,
"owner_checkout_writes": 0,
"policy_widening": 0,
"pushes": 0,
"secret_gets": 0
},
"schema": "mason-native-preparation/v1",
"source_scope": "existing MASON-WP-0009-T05 only; source preparation, no live acceptance; unrelated WORK-RECORDS.md and ADHOC-2026-10-11.md preserved",
"source_sha256": {
"docs/keycape-client-custody-attended-verification.md": "554d20f722c13742eb86460991df420035cec11a469fef09e09567a64e07b247",
"scripts/verify-keycape-client-custody.py": "f7685646dca48db5874174940c1826db186e1ed73e167814d8c0f37af3ad619c",
"tests/test_keycape_client_custody_verifier.py": "ad981635eb97f48ca2e092cdbcb6e47007d89eb272a85578485dbb299bb7c479",
"workplans/MASON-WP-0009-keycape-secrets-engine-openbao-client-custody.md": "9bea9cb4f2619c42a6cf3a7cf1fb14edc8539d702c437939b397ad9ebe1ce7d6"
},
"task": "MASON-WP-0009-T05",
"verification": {
"command": "python3 -m unittest discover -s /tmp/mason-jwt-capability-next-stage-v2/ops-mason/tests -q",
"result": "passed-offline",
"tests": 29
}
}

View file

@ -0,0 +1,168 @@
# 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.
```bash
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.

View file

@ -0,0 +1,81 @@
{
"reviewer": "ops-mason-owner (agent ops-mason, codex)",
"decision": "approved",
"comment": "INTENT Mission four-phase source-grounded construction consistency and Responsibility boundary permit this capability-only verification preparation with no secret-value retrieval or access widening. Exact reviewed revision2 manifest SHA256 4dbf808e2d79c2e6600a163e147db7d15b075e59a8edf8cd236f31fdce9b5155, script f7685646dca48db5874174940c1826db186e1ed73e167814d8c0f37af3ad619c. Own completed work item ADHOC-2026-10-11-T03 refined preparation recorded before this formal entry; first changes-requested review remains historical. Both blocking source defects corrected and independently verified. Approval is source review only: no founder authorization, runtime isolation attestation, native acceptance, route activation or whole-plan closure.",
"own_task": "ADHOC-2026-10-11-T03",
"own_completed_record_sha256": "7fadf4475ff765999b71659495b786737a9641c8ad875afc97bd264a8c9f0aec",
"reviewed_manifest_sha256": "4dbf808e2d79c2e6600a163e147db7d15b075e59a8edf8cd236f31fdce9b5155",
"reviewed_files": [
{
"original_sha256": null,
"path": "scripts/verify-keycape-client-custody.py",
"repo": "ops-mason",
"staged_path": "/tmp/mason-jwt-capability-next-stage-v2/ops-mason/scripts/verify-keycape-client-custody.py",
"staged_sha256": "f7685646dca48db5874174940c1826db186e1ed73e167814d8c0f37af3ad619c"
},
{
"original_sha256": null,
"path": "tests/test_keycape_client_custody_verifier.py",
"repo": "ops-mason",
"staged_path": "/tmp/mason-jwt-capability-next-stage-v2/ops-mason/tests/test_keycape_client_custody_verifier.py",
"staged_sha256": "ad981635eb97f48ca2e092cdbcb6e47007d89eb272a85578485dbb299bb7c479"
},
{
"original_sha256": null,
"path": "docs/keycape-client-custody-attended-verification.md",
"repo": "ops-mason",
"staged_path": "/tmp/mason-jwt-capability-next-stage-v2/ops-mason/docs/keycape-client-custody-attended-verification.md",
"staged_sha256": "554d20f722c13742eb86460991df420035cec11a469fef09e09567a64e07b247"
},
{
"original_sha256": "857458214d94f7852f841bad25887625ee4d90575a79b5153465c661a2e0089f",
"path": "workplans/MASON-WP-0009-keycape-secrets-engine-openbao-client-custody.md",
"repo": "ops-mason",
"staged_path": "/tmp/mason-jwt-capability-next-stage-v2/ops-mason/workplans/MASON-WP-0009-keycape-secrets-engine-openbao-client-custody.md",
"staged_sha256": "9bea9cb4f2619c42a6cf3a7cf1fb14edc8539d702c437939b397ad9ebe1ce7d6"
},
{
"original_sha256": null,
"path": "docs/evidence/2026-10-11-keycape-client-custody-native-preparation.json",
"repo": "ops-mason",
"staged_path": "/tmp/mason-jwt-capability-next-stage-v2/ops-mason/docs/evidence/2026-10-11-keycape-client-custody-native-preparation.json",
"staged_sha256": "8c6b15ce589f322d7f01e8ce6415f3726f34cb301b29b2436618ce4806a12494"
}
],
"prior_review": "docs/reviews/2026-10-11-keycape-client-custody-independent-source-review.json",
"prior_outcome_preserved": "changes_requested on revision1",
"corrections_verified": [
"lookup/login/capability exact200JSON/endpoint-shape/errors absence",
"revoke204emptybody only; failures leave confirmation false and result unproved",
"typed nonempty bounded native accessors; safe recovered-token cleanup on missing correlation handle",
"mixed auth+errors preserves recovered token solely for cleanup, never acceptance",
"typed exact positiveTTLlimits; expired/unbounded refused",
"early source digest mismatch before credentialFD or nativeAPI access",
"HTTP preparse response bound; native subprocess incremental per-stream64KiB capture with process cleanup",
"fixed origin, proxy disabled, all redirectfollowing refused"
],
"tests": {
"maker_suite": "29 passed independently",
"additional_independent_suite": "4 passed",
"additional_tests_sha256": "ce4dd7ebd2f88f031de9cf8aba89f6c761f80f67d56c56adc3717c5d3a902176",
"additional_counterexamples": [
"actual cross-origin302target receives no request/token",
"absent revoke response and cleanup failure remain unproved",
"expiredTTL and missing accessor refuse and attempt recovered-token cleanup",
"oversized native stderr refused before full capture"
]
},
"remaining_live_gates": [
"founder scoped exact-source/native attendance go",
"operator and ESO actual native matrices+metadata+both revokes accepted by owner",
"issuer-authenticated nonmember human/aal2 plus same-attempt platform groups-refusal correlation",
"unknown/native timeout or failed cleanup owner resolution",
"ops-warden pointer acceptance and activation"
],
"native_actions": false,
"credential_requests": false,
"secret_values_accessed": false,
"owner_checkout_writes": false,
"approval_or_admission_fields_changed": false,
"whole_plan_closed": false
}

View file

@ -0,0 +1,325 @@
#!/usr/bin/env python3
"""MASON-WP-0009-T05 attended, value-free lane verifier; no KV GETs.
The existing operator/ESO policies do NOT grant capabilities-self. An existing
read-only ops-mason-survey grant queries their tokens via sys/capabilities.
This file prepares a founder-run procedure, not a new authorization surface.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import subprocess
import selectors
import signal
import sys
import time
import urllib.request
import urllib.error
DATA = 'platform/data/workloads/keycape/secrets-engine-openbao'
OP_ROLE = 'keycape-secrets-engine-openbao-operator-workload-kv-read'
ESO_ROLE = 'external-secrets-keycape-secrets-engine-openbao'
POLICY = 'workload-kv-read-keycape-secrets-engine-openbao-'
PATHS = [DATA, DATA.replace('/data/', '/metadata/'),
'platform/data/workloads/keycape', 'platform/data/workloads/keycape/',
DATA + '-other', 'platform/data/workloads/keycape/other',
'platform/data/workloads/keycape/*', 'sys/policies/acl/ops-mason-survey',
'auth/token/create', 'auth/token/revoke-self']
BASE = 'http://127.0.0.1:18200'
class Refused(Exception):
pass
class RecoveredLoginRefused(Refused):
"""Retain only an already recovered session for finally cleanup, never proof."""
def __init__(self, code, auth):
super().__init__(code)
token = auth.get('client_token') if isinstance(auth, dict) else None
self.token = token if isinstance(token, str) and 0 < len(token) <= 4096 and not any(c.isspace() for c in token) else ''
class Transport(Exception):
pass
class Api:
"""Hard-coded endpoint allowlist prevents secret GETs, role/policy writes."""
def __init__(self):
self.opener = urllib.request.build_opener(urllib.request.ProxyHandler({}),
NoRedirect())
def call(self, endpoint, token='', body=None):
allowed = {'auth/token/lookup-self', 'auth/token/revoke-self',
'sys/capabilities', 'sys/capabilities-self',
'auth/kubernetes/login'}
if endpoint not in allowed:
raise Refused('endpoint_not_allowlisted')
req = urllib.request.Request(BASE + '/v1/' + endpoint,
data=None if body is None else json.dumps(body).encode(),
headers={'Content-Type': 'application/json', 'X-Vault-Token': token},
method='GET' if endpoint == 'auth/token/lookup-self' else 'POST')
try:
with self.opener.open(req, timeout=20) as response:
if endpoint == 'auth/token/revoke-self':
# OpenBao's native self-revoke success is 204 with no body.
if response.status != 204 or response.read(1):
raise Refused('unconfirmed_revoke_response')
return {}
if response.status != 200 or response.headers.get_content_type() != 'application/json':
raise Refused('invalid_status_or_content_type')
length = response.headers.get('Content-Length')
if length is not None and (not length.isdecimal() or int(length) > 65536):
raise Refused('oversized_or_invalid_content_length')
raw = response.read(65537)
if len(raw) > 65536:
raise Refused('oversized_response')
result = json.loads(raw)
if not isinstance(result, dict):
raise Refused('invalid_response_envelope')
if 'errors' in result:
if endpoint == 'auth/kubernetes/login':
raise RecoveredLoginRefused('native_error_envelope', result.get('auth'))
raise Refused('native_error_envelope')
key = 'auth' if endpoint == 'auth/kubernetes/login' else 'data'
if not isinstance(result.get(key), dict):
raise Refused('invalid_endpoint_response_shape')
return result
except urllib.error.HTTPError as error:
error.close() # never read/print server errors
raise Refused('http_' + str(error.code)) from None
except (OSError, ValueError, urllib.error.URLError):
raise Transport('transport_or_invalid_response') from None
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, *args, **kwargs):
raise Refused('redirect_refused')
def digest(value):
return hashlib.sha256(value.encode()).hexdigest()
def accessor_digest(value):
if not isinstance(value, str) or not 1 <= len(value) <= 256 or any(c.isspace() for c in value):
raise Refused('missing_or_invalid_native_accessor')
return digest(value)
def checked_caps(response, expected):
data = response.get('data', response)
# Only batch-shaped named results accepted, never substitute a default.
if not isinstance(data, dict) or not all(path in data for path in expected):
raise Refused('incomplete_capability_matrix')
result = {}
for path, caps in expected.items():
actual = data[path]
if not isinstance(actual, list) or sorted(actual) != sorted(caps):
raise Refused('capability_mismatch')
result[path] = actual
return result
def survey(api, token):
info = api.call('auth/token/lookup-self', token)['data']
if info.get('identity_policies'):
raise Refused('query_identity_policy_additions')
policies = set(info.get('policies', []))
if policies not in ({'ops-mason-survey'}, {'ops-mason-survey', 'default'}):
raise Refused('query_policy_mismatch')
if info.get('display_name') != 'token-claude-MASON-WP-0009-T05' or (type(info.get('ttl')) is not int or not 0 < info['ttl'] <= 2700):
raise Refused('query_identity_or_ttl_mismatch')
expected = {DATA: ['deny'], 'sys/policies/acl/ops-mason-survey': ['deny'],
'auth/token/create': ['deny'], 'sys/capabilities': ['update']}
checked_caps(api.call('sys/capabilities-self', token, {'paths': list(expected)}), expected)
return info
def safe_env(home):
# Never inherit credential variables, token helpers, proxies, CLI plugins.
return {'PATH': '/home/worsch/.local/bin:/usr/local/bin:/usr/bin:/bin', 'HOME': home,
'BAO_ADDR': BASE, 'VAULT_ADDR': BASE,
'DISPLAY': os.environ.get('DISPLAY', ''),
'WSL_INTEROP': os.environ.get('WSL_INTEROP', ''),
'WSL_DISTRO_NAME': os.environ.get('WSL_DISTRO_NAME', '')}
def run(argv, env):
# Capture each stream with a real memory bound while the child runs; never
# retain an unbounded PIPE response and only check its length afterwards.
process = None
try:
process = subprocess.Popen(argv, env=env, stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, start_new_session=True)
buffers = {process.stdout: bytearray(), process.stderr: bytearray()}
deadline = time.monotonic() + 300
with selectors.DefaultSelector() as selector:
for stream in buffers:
selector.register(stream, selectors.EVENT_READ)
while selector.get_map():
remaining = deadline - time.monotonic()
if remaining <= 0:
raise Transport('native_transport_or_timeout')
for key, _ in selector.select(min(remaining, 1)):
chunk = os.read(key.fileobj.fileno(), 8192)
if not chunk:
selector.unregister(key.fileobj)
continue
target = buffers[key.fileobj]
if len(target) + len(chunk) > 65536:
raise Refused('oversized_native_response')
target.extend(chunk)
remaining = deadline - time.monotonic()
if remaining <= 0:
raise Transport('native_transport_or_timeout')
code = process.wait(timeout=remaining)
return code, bytes(buffers[process.stdout]), bytes(buffers[process.stderr])
except (OSError, subprocess.TimeoutExpired):
raise Transport('native_transport_or_timeout') from None
finally:
if process is not None:
if process.poll() is None:
try:
os.killpg(process.pid, signal.SIGKILL)
except ProcessLookupError:
pass
process.wait()
process.stdout.close()
process.stderr.close()
def oidc(env, runner=run):
# -no-store is mandatory; output/errors stay inside the founder's process.
code, raw, err = runner(['/home/worsch/.local/bin/bao', 'login', '-no-store', '-format=json',
'-method=oidc', '-path=netkingdom', 'role=' + OP_ROLE], env)
if code:
# A candidate refusal still needs independent issuer + correlated
# OpenBao owner evidence. HTTP 403, cancellation or timeout never pass.
claim_refusal = b'claim "groups" does not match' in err or b'claim "groups" not found' in err
return None, 'group_refusal_candidate' if claim_refusal else 'unproved_login_failure'
try:
result = json.loads(raw)
if not isinstance(result, dict) or not isinstance(result.get('auth'), dict):
raise Refused('malformed_native_login')
if 'errors' in result:
raise RecoveredLoginRefused('native_error_envelope', result['auth'])
return result['auth'], 'login_succeeded'
except (ValueError, KeyError, TypeError):
raise Refused('malformed_native_login') from None
def eso(api, runner=run):
# Existing owner SSH certificate, no kubeconfig/Secret reads. Separate
# TokenRequest (10m, openbao audience); no ESO controller session revoked.
env = {'PATH': '/usr/local/bin:/usr/bin:/bin', 'HOME': os.path.expanduser('~')}
rc, out, _ = runner(['ssh', '-oBatchMode=yes', 'railiance01',
'kubectl get nodes -o json'], env)
if rc:
raise Transport('cluster_identity_unproved')
try:
nodes = json.loads(out)['items']
addresses = {a['address'] for n in nodes for a in n['status']['addresses']}
if len(nodes) != 1 or '92.205.62.239' not in addresses:
raise Refused('wrong_cluster')
except (KeyError, TypeError, ValueError):
raise Refused('invalid_cluster_identity') from None
rc, jwt, _ = runner(['ssh', '-oBatchMode=yes', 'railiance01',
'kubectl -n external-secrets create token external-secrets --audience=openbao --duration=10m'], env)
if rc or not jwt.strip():
raise Transport('tokenrequest_failed')
return api.call('auth/kubernetes/login', body={'role': ESO_ROLE,
'jwt': jwt.decode().strip()})['auth']
def lane_matrix(api, query, auth, mode):
token = auth.get('client_token')
if not isinstance(token, str) or not token or any(c.isspace() for c in token):
raise Refused('malformed_lane_token')
metadata = auth.get('metadata', {})
expected_role = OP_ROLE if mode == 'operator' else ESO_ROLE
if metadata.get('role') != expected_role:
raise Refused('lane_role_mismatch')
if mode == 'eso' and (metadata.get('service_account_name') != 'external-secrets' or
metadata.get('service_account_namespace') != 'external-secrets'):
raise Refused('eso_identity_mismatch')
if auth.get('identity_policies'):
raise Refused('lane_identity_policy_additions')
expected_policy = POLICY + ('operator' if mode == 'operator' else 'eso')
if set(auth.get('policies', [])) != {expected_policy}:
raise Refused('lane_policy_mismatch')
if (type(auth.get('lease_duration')) is not int or not 0 < auth['lease_duration'] <= (300 if mode == 'operator' else 900)):
raise Refused('lane_ttl_mismatch')
expected = {p: ['deny'] for p in PATHS}
expected[DATA] = ['read']
expected['auth/token/revoke-self'] = ['update']
return checked_caps(api.call('sys/capabilities', query,
{'token': token, 'paths': PATHS}), expected)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('mode', choices=['operator', 'eso', 'nonmember'])
parser.add_argument('--survey-token-fd', type=int, required=True)
parser.add_argument('--approval-reference', required=True)
parser.add_argument('--approved-source-sha256', required=True)
parser.add_argument('--receipt', type=Path, required=True)
parser.add_argument('--attended', action='store_true')
args = parser.parse_args()
if not args.attended or not sys.stdin.isatty() or args.survey_token_fd < 3:
parser.error('founder terminal attendance and separate survey token FD required')
if any(os.environ.get(k) for k in ('BAO_TOKEN', 'VAULT_TOKEN', 'BAO_NAMESPACE', 'VAULT_NAMESPACE')):
parser.error('ambient authority refused')
source_sha256 = hashlib.sha256(Path(__file__).read_bytes()).hexdigest()
if args.approved_source_sha256 != source_sha256:
parser.error('approved source digest differs; no session requested')
# Reservation prevents overwrites/symlink substitution before credentials.
fd = os.open(args.receipt, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
receipt = {'schema': 'mason-client-custody-native/v1', 'task': 'MASON-WP-0009-T05',
'mode': args.mode, 'source_sha256': source_sha256,
'approval_reference': args.approval_reference, 'at': int(time.time()),
'secret_reads': 0, 'result': 'unproved', 'owner_acceptance': 'pending',
'query_revoke': False, 'lane_revoke': False}
api = Api()
query = ''
query_valid = False
lane = ''
try:
with os.fdopen(args.survey_token_fd, 'r') as handle:
query = handle.read(4097).strip()
if not query or len(query) > 4096 or any(c.isspace() for c in query):
raise Refused('malformed_query_token')
query_info = survey(api, query)
query_valid = True
receipt['query_accessor_sha256'] = accessor_digest(query_info.get('accessor'))
if args.mode == 'eso':
auth = eso(api)
else:
import tempfile
with tempfile.TemporaryDirectory(prefix='mason-oidc-') as home:
auth, result = oidc(safe_env(home))
receipt['native_login_result'] = result
if auth is None:
# Never pronounce the nonmember check passed from CLI text.
raise Refused(result)
lane = auth.get('client_token', '')
receipt['lane_accessor_sha256'] = accessor_digest(auth.get('accessor'))
if args.mode == 'nonmember':
raise Refused('nonmember_obtained_token')
receipt['capabilities'] = lane_matrix(api, query, auth, args.mode)
receipt['result'] = 'capability_matrix_observed'
except RecoveredLoginRefused as error:
lane = error.token
receipt['failure_class'] = str(error)
except (Refused, Transport) as error:
receipt['failure_class'] = str(error)
except Exception:
receipt['failure_class'] = 'invalid_or_unexpected_response'
finally:
if lane:
try:
api.call('auth/token/revoke-self', lane, {})
receipt['lane_revoke'] = True
except Exception:
receipt['result'] = 'unproved'
if query_valid:
try:
api.call('auth/token/revoke-self', query, {})
receipt['query_revoke'] = True
except Exception:
receipt['result'] = 'unproved'
query = lane = ''
with os.fdopen(fd, 'w') as handle:
json.dump(receipt, handle, indent=2, sort_keys=True)
handle.write('\n')
return 0 if receipt['result'] == 'capability_matrix_observed' else 1
if __name__ == '__main__':
sys.exit(main())

View file

@ -0,0 +1,298 @@
import importlib.util
from pathlib import Path
import unittest
SCRIPT = Path(__file__).resolve().parents[1] / 'scripts/verify-keycape-client-custody.py'
spec = importlib.util.spec_from_file_location('custody', SCRIPT)
m = importlib.util.module_from_spec(spec)
spec.loader.exec_module(m)
NATIVE_API = m.Api
class Fake:
def __init__(self, info=None, changes=None):
self.calls = []
self.info = info or {'policies': ['ops-mason-survey', 'default'],
'display_name': 'token-claude-MASON-WP-0009-T05', 'ttl': 2700,
'accessor': 'test-query-accessor'}
self.changes = changes or {}
def call(self, endpoint, token='', body=None):
self.calls.append((endpoint, token, body))
if endpoint == 'auth/token/lookup-self':
return {'data': self.info}
if endpoint == 'sys/capabilities-self':
result = {m.DATA: ['deny'], 'sys/policies/acl/ops-mason-survey': ['deny'],
'auth/token/create': ['deny'], 'sys/capabilities': ['update']}
else:
result = {p: ['deny'] for p in m.PATHS}
result[m.DATA] = ['read']
result['auth/token/revoke-self'] = ['update']
result.update(self.changes)
return {'data': result}
class Tests(unittest.TestCase):
def auth(self, mode='operator'):
return {'client_token': 'test-only-not-a-credential', 'accessor': 'test-accessor',
'policies': [m.POLICY + mode], 'lease_duration': 300,
'metadata': {'role': m.OP_ROLE if mode == 'operator' else m.ESO_ROLE,
'service_account_name': 'external-secrets',
'service_account_namespace': 'external-secrets'}}
def test_query_identity_and_data_deny(self):
self.assertEqual(m.survey(Fake(), 'test-query')['accessor'], 'test-query-accessor')
def test_broad_ambient_query_refused(self):
for policies in [['root'], ['ops-mason-survey', 'platform-admin']]:
with self.subTest(policies=policies), self.assertRaises(m.Refused):
m.survey(Fake(info={'policies': policies}), 'test-query')
def test_wrong_task_and_unbounded_grant_refused(self):
for field, value in [('display_name', 'claude-another-task'), ('ttl', 2701), ('ttl', 0)]:
fake = Fake()
fake.info[field] = value
with self.subTest(field=field, value=value), self.assertRaises(m.Refused):
m.survey(fake, 'test-query')
def test_query_cannot_read_value_or_create_token(self):
for path in [m.DATA, 'auth/token/create', 'sys/policies/acl/ops-mason-survey']:
with self.subTest(path=path), self.assertRaises(m.Refused):
m.survey(Fake(changes={path: ['read']}), 'test-query')
def test_operator_and_eso_native_matrix(self):
for mode in ['operator', 'eso']:
fake = Fake()
result = m.lane_matrix(fake, 'test-query', self.auth(mode), mode)
self.assertEqual(result[m.DATA], ['read'])
self.assertEqual(fake.calls[0][0], 'sys/capabilities')
self.assertEqual(fake.calls[0][1], 'test-query')
def test_data_write_and_missing_read_refused(self):
for caps in [['read', 'update'], ['deny'], ['sudo', 'read']]:
with self.subTest(caps=caps), self.assertRaises(m.Refused):
m.lane_matrix(Fake(changes={m.DATA: caps}), 'test-query', self.auth(), 'operator')
def test_parent_sibling_metadata_and_policy_escalation_refused(self):
for path in m.PATHS[1:-1]:
with self.subTest(path=path), self.assertRaises(m.Refused):
m.lane_matrix(Fake(changes={path: ['read']}), 'test-query', self.auth(), 'operator')
def test_incomplete_matrix_refused(self):
with self.assertRaises(m.Refused):
m.checked_caps({'data': {m.DATA: ['read']}}, {m.DATA: ['read'], 'other': ['deny']})
def test_wrong_policy_default_identity_role_ttl_refused(self):
for field, value in [('policies', ['root']), ('policies', [m.POLICY+'operator', 'default']),
('metadata', {'role': 'platform-admin'}), ('lease_duration', 301),
('client_token', 'invalid token')]:
auth = self.auth()
auth[field] = value
with self.subTest(field=field, value=value), self.assertRaises(m.Refused):
m.lane_matrix(Fake(), 'test-query', auth, 'operator')
def test_wrong_eso_namespace_refused(self):
auth = self.auth('eso')
auth['metadata']['service_account_namespace'] = 'sso'
with self.assertRaises(m.Refused):
m.lane_matrix(Fake(), 'test-query', auth, 'eso')
def test_login_failure_is_never_negative_acceptance(self):
for err in [b'connection refused', b'HTTP 403 permission denied', b'user cancelled',
b'claim "groups" does not match any associated bound claim values']:
result, category = m.oidc({}, lambda *args: (1, b'', err))
self.assertIsNone(result)
self.assertIn(category, ['unproved_login_failure', 'group_refusal_candidate'])
self.assertNotIn('accepted', category)
def test_no_store_and_fixed_native_role(self):
calls = []
def runner(argv, env):
calls.append(argv)
return 1, b'', b'cancelled'
m.oidc({}, runner)
self.assertIn('-no-store', calls[0])
self.assertIn('role=' + m.OP_ROLE, calls[0])
def test_api_rejects_secret_gets_role_writes_and_generic_endpoints(self):
api = m.Api()
for endpoint in [m.DATA, m.DATA.replace('/data/', '/metadata/'),
'sys/policies/acl/new', 'auth/netkingdom/role/new', 'auth/token/create']:
with self.subTest(endpoint=endpoint), self.assertRaises(m.Refused):
api.call(endpoint, 'test-only')
def test_credentials_not_in_child_env(self):
import os
from unittest.mock import patch
with patch.dict(os.environ, {'BAO_TOKEN': 'test', 'VAULT_TOKEN': 'test',
'HTTPS_PROXY': 'https://wrong', 'BAO_NAMESPACE': 'other'}):
env = m.safe_env('/tmp/test-only')
for name in ['BAO_TOKEN', 'VAULT_TOKEN', 'HTTPS_PROXY', 'BAO_NAMESPACE']:
self.assertNotIn(name, env)
self.assertEqual(env['HOME'], '/tmp/test-only')
def test_eso_cluster_mismatch_stops_before_tokenrequest(self):
calls = []
def runner(argv, env):
calls.append(argv)
return 0, b'{"items": [{"status": {"addresses": [{"address": "10.0.0.1"}]}}]}', b''
with self.assertRaises(m.Refused):
m.eso(Fake(), runner)
self.assertEqual(len(calls), 1)
def exercise_main(self, auth, failure=None, mode='operator', query_info=None, bad_revoke_response=None, login_error=None):
import tempfile
import os
from unittest.mock import patch
with tempfile.TemporaryDirectory() as directory:
credential = Path(directory) / 'test-fixture'
credential.write_text('test-only-query')
token_fd = os.open(credential, os.O_RDONLY)
receipt = Path(directory) / 'receipt.json'
fake = Fake()
if query_info is not None:
fake.info.update(query_info)
old_call = fake.call
def call(endpoint, token='', body=None):
if endpoint == 'auth/token/revoke-self' and bad_revoke_response is not None:
native = NATIVE_API()
native.opener = self.opener(bad_revoke_response)
return native.call(endpoint, token, body)
if endpoint == 'auth/token/revoke-self' and token == failure:
raise m.Transport('test_cleanup_transport')
return old_call(endpoint, token, body)
fake.call = call
argv = ['verifier', mode, '--survey-token-fd', str(token_fd),
'--approval-reference', 'test-not-an-approval', '--approved-source-sha256',
m.hashlib.sha256(SCRIPT.read_bytes()).hexdigest(), '--attended', '--receipt', str(receipt)]
with patch.object(m.sys, 'argv', argv), patch.object(m.sys.stdin, 'isatty', return_value=True), \
patch.dict(os.environ, {}, clear=True), patch.object(m, 'Api', return_value=fake), \
patch.object(m, 'oidc', return_value=(auth, 'login_succeeded'), side_effect=login_error):
code = m.main()
return code, m.json.loads(receipt.read_text()), fake.calls
def test_cleanup_attempted_even_when_policy_check_fails(self):
auth = self.auth()
auth['policies'] = ['root']
code, receipt, calls = self.exercise_main(auth)
self.assertEqual(code, 1)
self.assertTrue(receipt['lane_revoke'])
self.assertTrue(receipt['query_revoke'])
self.assertNotIn('test-only-not-a-credential', m.json.dumps(receipt))
def test_cleanup_transport_invalidates_positive_matrix(self):
for token in ['test-only-query', 'test-only-not-a-credential']:
code, receipt, calls = self.exercise_main(self.auth(), failure=token)
self.assertEqual(code, 1)
self.assertEqual(receipt['result'], 'unproved')
self.assertEqual(len([c for c in calls if c[0] == 'auth/token/revoke-self']), 1)
def test_unexpected_nonmember_success_is_revoked_and_fails(self):
code, receipt, calls = self.exercise_main(self.auth(), mode='nonmember')
self.assertEqual(code, 1)
self.assertEqual(receipt['failure_class'], 'nonmember_obtained_token')
self.assertTrue(receipt['lane_revoke'])
self.assertEqual(receipt['owner_acceptance'], 'pending')
def response(self, status=200, content='application/json', raw=b'{"data":{}}', length=None):
from email.message import Message
headers = Message()
if content:
headers['Content-Type'] = content
if length is not None:
headers['Content-Length'] = length
class Response:
def __enter__(self): return self
def __exit__(self, *args): pass
def read(self, count): return raw[:count]
response = Response()
response.status = status
response.headers = headers
return response
def opener(self, response):
from types import SimpleNamespace
return SimpleNamespace(open=lambda *args, **kwargs: response)
def test_mixed_login_error_preserves_recovered_token_cleanup(self):
raw=m.json.dumps({'errors':['private-synthetic'], 'auth':self.auth()}).encode()
with self.assertRaises(m.RecoveredLoginRefused) as error:
m.oidc({},lambda *args:(0,raw,b''))
code, receipt, _ = self.exercise_main(self.auth(), login_error=error.exception)
self.assertEqual(code,1)
self.assertTrue(receipt['lane_revoke'])
self.assertTrue(receipt['query_revoke'])
self.assertNotIn('private-synthetic',m.json.dumps(receipt))
native=m.Api();native.opener=self.opener(self.response(raw=raw))
with self.assertRaises(m.RecoveredLoginRefused) as error:
native.call('auth/kubernetes/login',body={})
self.assertEqual(error.exception.token,'test-only-not-a-credential')
def test_endpoint_http_success_contracts(self):
for endpoint, raw in [('auth/token/lookup-self', b'{"data":{}}'),
('sys/capabilities', b'{"data":{}}'),
('auth/kubernetes/login', b'{"auth":{}}')]:
api = m.Api();api.opener = self.opener(self.response(raw=raw))
self.assertIsInstance(api.call(endpoint, 'test', {}), dict)
api = m.Api();api.opener = self.opener(self.response(status=204, content='', raw=b''))
self.assertEqual(api.call('auth/token/revoke-self', 'test', {}), {})
def test_bad_metadata_status_content_size_shape_errors_refused(self):
responses = [self.response(status=status) for status in [201,202,206,301,302,307,308]]
responses += [self.response(content='text/plain'), self.response(length='65537'),
self.response(length='-1'), self.response(raw=b'x'*65537),
self.response(raw=b'private-synthetic-invalid-json'),
self.response(raw=b'{"data":[]}'), self.response(raw=b'{}'),
self.response(raw=b'{"errors":[],"data":{}}'),
self.response(raw=b'{"errors":["private-synthetic"],"data":{}}')]
for response in responses:
api=m.Api();api.opener=self.opener(response)
with self.subTest(response=response), self.assertRaises((m.Refused,m.Transport)):
api.call('auth/token/lookup-self', 'synthetic')
def test_revoke202_wrong_content_error_body_never_confirmed_in_receipt(self):
responses = [self.response(status=status) for status in [200,201,202,206]]
responses += [self.response(status=204, raw=b'error')]
for response in responses:
code, receipt, _ = self.exercise_main(self.auth(), bad_revoke_response=response)
self.assertEqual(code, 1)
self.assertEqual(receipt['result'], 'unproved')
self.assertFalse(receipt['lane_revoke'])
self.assertFalse(receipt['query_revoke'])
def test_valid204_main_cleanup_confirmation(self):
code, receipt, _ = self.exercise_main(self.auth(), bad_revoke_response=self.response(status=204, content='', raw=b''))
self.assertEqual(code, 0)
self.assertTrue(receipt['lane_revoke'])
self.assertTrue(receipt['query_revoke'])
def test_missing_invalid_accessors_fail_with_recovered_token_cleanup(self):
for accessor in [None, '', 123, ' ', 'x'*257]:
auth=self.auth();auth['accessor']=accessor
code, receipt, _ = self.exercise_main(auth)
self.assertEqual(code, 1)
self.assertTrue(receipt['lane_revoke'])
self.assertTrue(receipt['query_revoke'])
self.assertNotIn('lane_accessor_sha256',receipt)
code, receipt, _ = self.exercise_main(self.auth(), query_info={'accessor':accessor})
self.assertEqual(code, 1)
self.assertTrue(receipt['query_revoke'])
self.assertNotIn('query_accessor_sha256',receipt)
def test_ttl_boolean_zero_negative_overbudget_refused(self):
for ttl in [True,False,0,-1,301,'300']:
auth=self.auth();auth['lease_duration']=ttl
with self.subTest(ttl=ttl), self.assertRaises(m.Refused):
m.lane_matrix(Fake(), 'test-query', auth, 'operator')
def test_approved_revision_mismatch_precedes_receipt_token_or_native(self):
from unittest.mock import patch
import os
argv=['verifier','operator','--survey-token-fd','99','--approval-reference','test',
'--approved-source-sha256','wrong','--attended','--receipt','/tmp/must-not-exist-mason-test']
with patch.object(m.sys,'argv',argv), patch.object(m.sys.stdin,'isatty',return_value=True), \
patch.dict(os.environ,{},clear=True), patch.object(m.os,'open',side_effect=AssertionError('no IO')):
with self.assertRaises(SystemExit) as error:
m.main()
self.assertEqual(error.exception.code,2)
def test_subprocess_capture_is_bounded_while_running(self):
import sys
with self.assertRaises(m.Refused):
m.run([sys.executable,'-c', 'import sys; sys.stdout.write("x"*1000000)'], {'PATH':'/usr/bin:/bin'})
result=m.run([sys.executable,'-c','import sys; print("synthetic"); sys.stderr.write("synthetic error")'],{'PATH':'/usr/bin:/bin'})
self.assertEqual(result,(0,b'synthetic\n',b'synthetic error'))
def test_real_relative_redirect_and_hostile_proxy_refused(self):
import threading
import os
from unittest.mock import patch
from http.server import BaseHTTPRequestHandler, HTTPServer
calls=[]
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
calls.append(self.path)
self.send_response(302);self.send_header('Location','/private');self.end_headers()
def log_message(self,*args):pass
server=HTTPServer(('127.0.0.1',0),Handler)
thread=threading.Thread(target=server.serve_forever,daemon=True);thread.start()
try:
with patch.object(m,'BASE','http://127.0.0.1:'+str(server.server_port)), \
patch.dict(os.environ, {'HTTP_PROXY':'http://127.0.0.1:1','http_proxy':'http://127.0.0.1:1','NO_PROXY':'','no_proxy':''}):
with self.assertRaises(m.Refused):
m.Api().call('auth/token/lookup-self','synthetic-not-a-credential')
finally:
server.shutdown();server.server_close();thread.join()
self.assertEqual(calls,['/v1/auth/token/lookup-self'])
def test_redirect_refused(self):
with self.assertRaises(m.Refused):
m.NoRedirect().redirect_request(None, None, None, None, None, None)
if __name__ == '__main__':
unittest.main()

View file

@ -84,3 +84,33 @@ This bounded review task is complete. Corrections and native proof remain live
in MASON-WP-0009-T05; no plan completion or route activation is claimed and no
new residual is parked in prose. No native action, credential request, value
access, approval disposition, commit, push or consistency sync was performed.
### T03 corrected-source re-review — same review trial
Completed independent re-review preparation before the refined formal entry.
Original request-changes review and frozen refinement01 are preserved. Exact
corrected manifest SHA256 4dbf808e2d79c2e6600a163e147db7d15b075e59a8edf8cd236f31fdce9b5155,
verifier SHA256 f7685646dca48db5874174940c1826db186e1ed73e167814d8c0f37af3ad619c.
Verified all five immutable hashes and actual existing WP0009 baseline.
Corrected source now accepts only HTTP200 application/json object envelopes with
endpoint data/auth shape and no errors member. Self-revoke requires HTTP204 and
empty body; stale or error responses cannot confirm cleanup. Nonempty typed
bounded accessors are mandatory; recovered query/lane tokens still revoke after
missing accessor or other later verification refusal. TTL types/limits, mixed
errors plus auth recovery, source digest preflight, incremental 64KiB native
stdout/stderr capture and redirect/proxy guards are covered. All 29 maker tests
pass independently. Four additional independent counterexamples pass: cross-origin
redirect target gets no token/request; absent revoke response and failed cleanup
remain unproved; expired TTL/missing accessor retain cleanup with no correlation
success; oversized native stderr is refused while child runs.
INTENT Mission/Responsibility boundary remains respected: no KV GET, no widening,
source preparation only, exact founder go still required. Nonmember acceptance
continues to need issuer-authenticated human nonmember/aal2 and correlated native
platform groups refusal for the same attempted subject/flow; callback text alone
never passes. Source acceptance does not attest native isolation or finish T05.
Corrections to the first review's two blocking defects are complete at source;
existing MASON-WP-0009-T05 retains all native matrices, nonmember correlation,
owner acceptance and route activation. No new residual or native act is claimed.

View file

@ -9,7 +9,7 @@ flavor: planning
owner: claude
topic_slug: netkingdom
created: "2026-10-06"
updated: "2026-10-07"
updated: "2026-10-11"
state_hub_workstream_id: "d9be08f5-a0f5-5cd1-8f87-0a6868661d77"
---
@ -182,3 +182,35 @@ only, 5m, 8 uses, no default policy). ops-mason's checks are complete. Pointer
entry proposed to ops-warden as draft, non-resolvable (message `8ad8ed4e`).
Left owner-run: capability checks with live operator and ESO tokens and a
non-member refusal.
2026-10-11 (preparation only, no native action): a guarded founder-run verifier
and packet are prepared for T05: `scripts/verify-keycape-client-custody.py`,
`docs/keycape-client-custody-attended-verification.md`, source preparation record
`docs/evidence/2026-10-11-keycape-client-custody-native-preparation.json`.
The existing read-only survey query authority is reused because neither lane
policy grants sys/capabilities-self; lane tokens are queried in memory via
sys/capabilities without any KV GET. The query grant is checked for exact task
identity, policies, bounded TTL and explicit data denial. Native OIDC uses
-no-store; ESO uses a separately minted, 10m Kubernetes TokenRequest for the
existing ServiceAccount, never a Secret/controller-token read. Failed cleanup
invalidates observations. Nonmember CLI failure cannot pass: independently
verified key-cape identity/groups/aal2 and platform's correlated groups refusal
are required before owner acceptance. 29 offline tests passed, including broad
query authority, wrong role/ESO identity, unsafe matrix, endpoint secret-read
refusal, transport versus claim failure, unexpected nonmember success and
cleanup failures. Formal ops-mason source review, exact-revision founder
attendance approval, native receipts and nonmember owner correlation remain
unresolved. T05 and route activation remain open; no foreign owner review,
authority grant, workplan completion or live acceptance is asserted.
2026-10-11 source refinement (same T05 trial, no native action): independent
review of the initial immutable stage requested HTTP revoke/status/error-shape
and native accessor corrections. Corrected source requires endpoint-specific
HTTP200 JSON or empty HTTP204 self-revoke, rejects mixed errors, requires actual
nonempty native accessors, preserves recovered-token cleanup on malformed login,
uses positive integer TTLs and bounded incremental native stdout/stderr capture.
29 tests pass; corrected manifest is separate and preserves the rejected input.
Formal review of corrected bytes and all founder/native acceptance gates remain
open; this refinement is not a second scored success or an authority change.