diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index df376e8..bf9743b 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -71,15 +71,15 @@ | workplan | CUST-WP-0067 | finished | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | | workplan | CUST-WP-0068 | finished | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | | workplan | CUST-WP-0070 | finished | — | workplans/CUST-WP-0070-publication-repo-category.md | -| workplan | CUST-WP-0071 | blocked | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| workplan | CUST-WP-0071 | active | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | | workplan | CUST-WP-0072 | finished | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | -| workplan | CUST-WP-0073 | blocked | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| workplan | CUST-WP-0073 | active | — | workplans/CUST-WP-0073-agent-credential-separation.md | | workplan | CUST-WP-0074 | finished | — | workplans/CUST-WP-0074-qualified-wait-states.md | | workplan | CUST-WP-0075 | finished | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | | workplan | CUST-WP-0076 | finished | — | workplans/CUST-WP-0076-provisioning-window-cadence.md | | workplan | CUST-WP-0077 | finished | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | | workplan | CUST-WP-0078 | finished | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | -| workplan | CUST-WP-0079 | blocked | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| workplan | CUST-WP-0079 | active | — | workplans/CUST-WP-0079-deterministic-security-flow.md | | workplan | CUST-WP-0080 | finished | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | | workplan | CUST-WP-0081 | finished | — | workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md | | workplan | CUST-WP-0082 | backlog | — | workplans/CUST-WP-0082-netkingdom-security-hardening.md | @@ -464,7 +464,7 @@ | task | CUST-WP-0070-T02 | done | — | workplans/CUST-WP-0070-publication-repo-category.md | | task | CUST-WP-0070-T03 | done | — | workplans/CUST-WP-0070-publication-repo-category.md | | task | CUST-WP-0071-T01 | done | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | -| task | CUST-WP-0071-T02 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| task | CUST-WP-0071-T02 | progress | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | | task | CUST-WP-0071-T03 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | | task | CUST-WP-0071-T04 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | | task | CUST-WP-0071-T05 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | @@ -473,7 +473,7 @@ | task | CUST-WP-0072-T03 | done | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | | task | CUST-WP-0072-T04 | done | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | | task | CUST-WP-0073-T01 | done | — | workplans/CUST-WP-0073-agent-credential-separation.md | -| task | CUST-WP-0073-T02 | wait | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| task | CUST-WP-0073-T02 | progress | — | workplans/CUST-WP-0073-agent-credential-separation.md | | task | CUST-WP-0073-T03 | done | — | workplans/CUST-WP-0073-agent-credential-separation.md | | task | CUST-WP-0073-T04 | wait | — | workplans/CUST-WP-0073-agent-credential-separation.md | | task | CUST-WP-0073-T05 | wait | — | workplans/CUST-WP-0073-agent-credential-separation.md | @@ -501,7 +501,7 @@ | task | CUST-WP-0078-T04 | done | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | | task | CUST-WP-0079-T01 | done | — | workplans/CUST-WP-0079-deterministic-security-flow.md | | task | CUST-WP-0079-T02 | done | — | workplans/CUST-WP-0079-deterministic-security-flow.md | -| task | CUST-WP-0079-T03 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T03 | progress | — | workplans/CUST-WP-0079-deterministic-security-flow.md | | task | CUST-WP-0079-T04 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md | | task | CUST-WP-0079-T05 | done | — | workplans/CUST-WP-0079-deterministic-security-flow.md | | task | CUST-WP-0079-T06 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md | diff --git a/docs/changes/CUST-WP-0073/privileged-preflight-r1/INTERACTIVE-OWNER-DELIVERABLE.md b/docs/changes/CUST-WP-0073/privileged-preflight-r1/INTERACTIVE-OWNER-DELIVERABLE.md new file mode 100644 index 0000000..0ff07a5 --- /dev/null +++ b/docs/changes/CUST-WP-0073/privileged-preflight-r1/INTERACTIVE-OWNER-DELIVERABLE.md @@ -0,0 +1,65 @@ +# Actual interactive assignment launch — owner finding + +This is conditional source preparation. The founder's current choice between the +actual interactive Codex assignment and native-worker-first acceptance is pending; +this document selects neither and does not treat one as proof of the other. + +## Existing reusable surfaces + +- sand-boxer owns capability.execution.sandbox-provision: provisioning, protected + runtime mounts, execution environment, egress and teardown. Its current bwrap + runner accepts bounded stdin and uses subprocess.communicate; this is command + execution, not an admitted long-lived interactive Codex session transport. +- glas-harness owns capability.execution.profile-harness: profile resolution, + assignment attribution, sandbox creation, rein dispatch and cleanup. Its gateway + executes one request. The committed concrete rein descriptors are Claude Code + unattended and open-weight; no Codex interactive descriptor or admission receipt + was found in these source catalogs. +- rein-aharness owns capability.execution.scheduled-agent-rein: Activity Core claim, + heartbeat and routing plus repository acceptance. Its f599 offline startup is + proven; it does not launch this current interactive Codex assignment. +- state-hub/scripts/configure-codex.sh registers local service/tool settings. It + does not isolate the Codex parent process or admit a restricted execution host. + +## Concrete next owner return, if interactive scope is selected + +Lead: glas-harness for the exact assignment launch/admission contract, with +sand-boxer delivering the executable boundary and railiance-infra the separate +account. The owner of the CURRENT frontend/executor attachment must be identified +explicitly: the local catalog does not establish who can reattach this managed +interactive session. Do not silently replace it with a new CLI session. + +Return one versioned launch packet containing: + +1. The selected frontend/transport and actual launch point before the first agent + tool: pinned executable/profile, UID, workspace, private HOME/state, inherited + environment allowlist, allowed tool transports and explicit supervisor identity. +2. A way to start the entire interactive executor/session under that boundary, + or a documented inability of the current frontend to attach to it. No parent + process with host admin authority may remain agent-callable. For a selected CLI + frontend, implement/test initial launch inside the owner boundary; for a selected + managed frontend, obtain its supported restricted-executor attachment contract. + Neither path is delivered by a child sandbox launched from this current session. +3. Positive interactive start/input/output and cancellation/teardown proof, followed + by reconnect/resume evidence that uses the same restricted identity and reviewed + profile. No fallback to the old host executor on refusal or reconnect failure. +4. A complete tool-path inventory: shell/files, SSH agents, containers, GitOps write + paths, plugins/MCP and remote connectors. Remove or route all privileged external + paths to a separately owned exact-approval executor; filesystem isolation alone + cannot restrict an external connector's independent authority. +5. Native proof from that assignment: original and per-namespace auth denials, + old host/admin paths denied, sanitized exact approved action succeeds, and + changed/unapproved/expired/replayed actions refuse. Bind all receipts to that + assignment and actual runtime revision. Keep autopilot disabled. + +No new general supervisor service is required. The first missing deliverable is +this owner-supported initial launch/attachment, not another disposable bwrap test. +The native-worker-first path may reuse existing f599/Glas/rein work but must retain +the interactive migration gap as unfinished unless the founder explicitly changes +that scope. Provider login/credential work is outside this source preparation. + +Official context, not a claim about this session: OpenAI's self-hosted environment +documentation describes an executor started inside the selected isolated environment +and keeping application credentials outside it. That reinforces the launch-point +requirement but does not prove this frontend uses that API or supports reattachment: +https://developers.openai.com/api/docs/guides/agents-api/environments/self-hosted diff --git a/docs/changes/CUST-WP-0073/privileged-preflight-r1/OPERATOR.md b/docs/changes/CUST-WP-0073/privileged-preflight-r1/OPERATOR.md new file mode 100644 index 0000000..594aa96 --- /dev/null +++ b/docs/changes/CUST-WP-0073/privileged-preflight-r1/OPERATOR.md @@ -0,0 +1,47 @@ +# Review-only privileged preflight, CUST73 existing-host addendum + +Prepared but NOT executed. This is a separate proposed read-only action; it grants +no unit replacement, sudo-policy change, credential issuance or service restart. +The original credential-boundary revision2 and existing-host addendum are unchanged. + +Owner: railiance-cluster (effective k3s configuration); attended privileged executor +and execution approval remain the supervisor's responsibility. This script requires +root only to inspect protected service environment/config selectors and the active +k3s process environment. Protected values exist transiently in its memory; they +are never printed, hashed into receipts, logged, evaluated or forwarded elsewhere. +Only fixed boolean predicates and presence of four explicitly named configuration +keys leave the process. Any exception prints one constant refusal, never traceback. + +After review, the human owner must place the exact reviewed script in a private +root-owned non-agent-writable directory and verify its SHA256 against SHA256SUMS. +Do not execute an agent-writable /tmp file under sudo or pipe a live checkout into +root Python. Proposed owner command, on the verified Railiance01 host: + +```sh +sudo -- /usr/bin/python3 -I -B /root/cust73-preflight/preflight.py +``` + +No command above has been executed by this worker. Stage/install/approval are still +needed before the command is usable. No password, token or environment value is to +be pasted into the transcript. The script makes no writes, reload, restart, network +request or credential issuance call. Its only subprocess is read-only systemctl show +with fixed service and property names; all subprocess output is captured. + +The checks bind the expected hashed machine identity, exact original unit digest +and root-owned file identity, stable main-process PID/starttime, absent unit drop-ins, +no pending reload, mode644 launch argument, no CLI config selector, and only known +EnvironmentFile sources. It parses a deliberately conservative simple assignment +subset; unsupported quoting/continuations, duplicate keys, unknown source paths, +selector presence, unreadable source or a changed unit/process require owner review. +It never follows a protected selector's value into another file. Default config and +config drop-ins must be absent for this exact baseline packet to pass. + +A pass settles only the protected-input predicates for the prepared mode600 unit +change. It does not authorize that change or prove workload health, account isolation, +SSH principal denial, interactive assignment admission, native-model acceptance or +supervised privileged execution. A present selector is an owner inspection gate, +not an instruction to disclose its value or to clear it automatically. + +Validation: eight fixture tests pass, including sensitive-value non-emission, +selector detection, complex/duplicate syntax refusal, non-root refusal before reads, +and constant-output exception handling. There is no live privileged receipt yet. diff --git a/docs/changes/CUST-WP-0073/privileged-preflight-r1/SHA256SUMS b/docs/changes/CUST-WP-0073/privileged-preflight-r1/SHA256SUMS new file mode 100644 index 0000000..c0b4649 --- /dev/null +++ b/docs/changes/CUST-WP-0073/privileged-preflight-r1/SHA256SUMS @@ -0,0 +1,6 @@ +3326079f7dd2662d08a2fb019394a897892abfafa479b82b9a01b1f0c0b98838 INTERACTIVE-OWNER-DELIVERABLE.md +66f76ae06ea5d3554c08810dee924fdd9a977798a56a7adfd680b06a7a9c338f OPERATOR.md +0e9058355cb1357e32090d6de6c9f6e6694069335eb3ca54701e0be48d393990 preflight.py +83a50903f7c9d05fb721d21585184f0f9965f09b0445cb9057760a726e6c54de source-pins.json +738beccd2b5fe679926d243b25c7b77a87de5879f5e1cfd0e871500fc8c43ac3 test_preflight.py +04c636a085f0440d0b8a3760e41afedd4703360dec128b119ef32aceb94bf22d validation.json diff --git a/docs/changes/CUST-WP-0073/privileged-preflight-r1/preflight.py b/docs/changes/CUST-WP-0073/privileged-preflight-r1/preflight.py new file mode 100644 index 0000000..c9820ff --- /dev/null +++ b/docs/changes/CUST-WP-0073/privileged-preflight-r1/preflight.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +"""Privileged READ ONLY k3s selector preflight; emit fixed predicates, never values.""" +import datetime +import hashlib +import json +import os +from pathlib import Path +import re +import stat +import subprocess + +UNIT_SHA = 'b5398c385430ce225faadc1f3724597fe4b05dd999a04b8abbca64d4b0d68c0e' +MACHINE_SHA = 'fe7367a60951922122af7ba18ed2cde65c7e0a232bbedfb045cb575de2094c2f' +ENV_FILES = ('/etc/default/k3s', '/etc/sysconfig/k3s', '/etc/systemd/system/k3s.service.env') +SELECTORS = ('K3S_CONFIG_FILE', 'K3S_KUBECONFIG_MODE', 'K3S_KUBECONFIG_OUTPUT', 'K3S_KUBECONFIG_GROUP') + + +def read_fixed(path, max_bytes=1024*1024): + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + try: + before = os.fstat(fd) + if not stat.S_ISREG(before.st_mode) or before.st_uid != 0 or before.st_mode & 0o022: + raise ValueError('file_custody') + with os.fdopen(os.dup(fd), 'rb') as stream: + data = stream.read(max_bytes+1) + after = os.fstat(fd) + if len(data) > max_bytes or (before.st_size,before.st_mtime_ns,before.st_ctime_ns) != (after.st_size,after.st_mtime_ns,after.st_ctime_ns): + raise ValueError('file_changed_or_oversized') + return data, before + finally: + os.close(fd) + + +def env_predicates(raw, *, process=False): + """Only key presence leaves this function. No shell evaluation or raw errors.""" + found = set() + valid = True + for line in (raw.split(b'\0') if process else raw.splitlines()): + line = line.strip() + if not line or (not process and line.startswith((b'#',b';'))): + continue + match = re.fullmatch(rb'([A-Za-z_][A-Za-z_0-9]*)=(.*)',line) + if not match: + valid = False + continue + name = match.group(1).decode('ascii') + if name in found: + valid = False + found.add(name) + if not process: + # Conservative subset of systemd EnvironmentFile, not a shell parser. + # Complex/multiline/escape forms require owner review, never guess. + value = match.group(2) + if b'\\' in value or value.count(b'"') not in (0,2) or value.count(b"'") not in (0,2): + valid = False + return {'syntax_supported': valid, + 'selectors_absent': all(key not in found for key in SELECTORS), + 'selector_presence': {key:key in found for key in SELECTORS}} + + +def show(prop): + result = subprocess.run(['/usr/bin/systemctl','show','k3s','--property='+prop,'--value'], + capture_output=True,timeout=10,check=False) + if result.returncode: + raise ValueError('systemd_read_failed') + return result.stdout.strip() + + +def inspect(): + if os.geteuid() != 0: + return {'status':'refused_requires_attended_root','read_only':True,'passed':False} + checks = {} + checks['expected_machine'] = hashlib.sha256(Path('/etc/machine-id').read_bytes()).hexdigest() == MACHINE_SHA + if not checks['expected_machine']: + return {'status':'refused_target','read_only':True,'passed':False,'checks':checks} + unit, meta = read_fixed('/etc/systemd/system/k3s.service') + checks['pinned_unit'] = hashlib.sha256(unit).hexdigest() == UNIT_SHA + checks['unit_identity'] = (meta.st_dev,meta.st_ino,meta.st_gid,stat.S_IMODE(meta.st_mode),meta.st_nlink)==(2049,7875,0,0o644,1) + if not all(checks.values()): + return {'status':'refused_unit_drift','read_only':True,'passed':False,'checks':checks} + checks['fragment_expected'] = show('FragmentPath') == b'/etc/systemd/system/k3s.service' + checks['no_dropins'] = show('DropInPaths') == b'' + checks['no_pending_reload'] = show('NeedDaemonReload') == b'no' + checks['active'] = show('ActiveState') == b'active' and show('SubState') == b'running' + pid = show('MainPID') + if not re.fullmatch(rb'[1-9][0-9]*',pid): + raise ValueError('main_pid_unavailable') + proc = Path('/proc')/pid.decode('ascii') + proc_stat = (proc/'stat').read_bytes() + start = proc_stat.rsplit(b')',1)[1].split()[19] + # Read only this bound process environment. Never emit raw bytes or digest. + environment = (proc/'environ').read_bytes() + effective = env_predicates(environment,process=True) + checks['effective_env_simple'] = effective['syntax_supported'] + checks['effective_selectors_absent'] = effective['selectors_absent'] + argv = (proc/'cmdline').read_bytes().split(b'\0') + checks['effective_mode644'] = argv.count(b'--write-kubeconfig-mode=644') == 1 + checks['no_cli_config_selector'] = not any(x in (b'-c',b'--config') or x.startswith(b'--config=') or (x.startswith(b'-c') and len(x)>2) for x in argv) + refs=[] + for match in re.finditer(rb'^EnvironmentFile=([^\r\n]+)',unit,re.M): + value=match.group(1).strip();optional=value.startswith(b'-');value=value.lstrip(b'-') + if value.decode('utf-8') not in ENV_FILES: + raise ValueError('unknown_environment_source') + refs.append((value.decode('utf-8'),optional)) + checks['known_environment_sources'] = len(refs)>0 and len({x[0] for x in refs})==len(refs) + file_results={} + for path,optional in refs: + try: + raw, st = read_fixed(path) + predicates=env_predicates(raw) + file_results[path]={'present':True,**predicates} + except FileNotFoundError: + file_results[path]={'present':False,'syntax_supported':optional,'selectors_absent':optional} + checks['environment_file_'+str(ENV_FILES.index(path))] = file_results[path]['syntax_supported'] and file_results[path]['selectors_absent'] + checks['default_config_absent'] = not Path('/etc/rancher/k3s/config.yaml').exists() + directory=Path('/etc/rancher/k3s/config.yaml.d') + checks['default_dropins_absent'] = not directory.exists() or (directory.is_dir() and not directory.is_symlink() and not any(directory.iterdir())) + # Revalidate identity to refuse PID reuse/config replacement during inspection. + checks['same_process'] = show('MainPID') == pid and (proc/'stat').read_bytes().rsplit(b')',1)[1].split()[19] == start + checks['same_unit'] = hashlib.sha256(read_fixed('/etc/systemd/system/k3s.service')[0]).hexdigest()==UNIT_SHA + return {'status':'preflight_passed' if all(checks.values()) else 'owner_review_required', + 'passed':all(checks.values()),'read_only':True,'checks':checks, + 'environment_files':file_results,'effective_selector_presence':effective['selector_presence'], + 'target':'railiance01','captured_at_utc':datetime.datetime.now(datetime.timezone.utc).isoformat(), + 'protected_values_emitted':False,'unit_change_authorized':False,'assignment_admitted':False} + + +def main(): + try: + receipt=inspect() + except BaseException: + # No exception text/traceback, even when a parser embeds protected input. + receipt={'status':'preflight_refused','passed':False,'read_only':True,'protected_values_emitted':False} + print(json.dumps(receipt,sort_keys=True)) + return 0 if receipt['passed'] else 1 + + +if __name__=='__main__': + raise SystemExit(main()) diff --git a/docs/changes/CUST-WP-0073/privileged-preflight-r1/source-pins.json b/docs/changes/CUST-WP-0073/privileged-preflight-r1/source-pins.json new file mode 100644 index 0000000..54d5db8 --- /dev/null +++ b/docs/changes/CUST-WP-0073/privileged-preflight-r1/source-pins.json @@ -0,0 +1,29 @@ +{ + "glas-harness": { + "head": "a19bff487d41f18c58a8fd713f6d4e04c3a218d5", + "files": { + "src/glas_harness/gateway.py": "5e998bfa53ade5dafbae799dc04b77031b6414e2af49797f45727d59d7397d42", + "registry/reins/rein-aharness.yaml": "8cdbe7b5ee45e2fb5ca2ed089cab377580034dc261c46bfcf889bddb68cf3da0", + "SCOPE.md": "dbc1c24fee17a9fccf7bcae0162897980a5675877f69f3c968d7efd271f4be2e" + } + }, + "sand-boxer": { + "head": "c7c310266aba96ab7dd03cd8bfcaa65f22a3b66e", + "files": { + "src/sandboxer/extensions/bwrap_runner.py": "c83cdd2758bae58cd5d5694f5d6752736f59d479cfc6e9f0f8841c1f5596cba6", + "src/sandboxer/core/manager.py": "213884776cdd9aeab1daf02d178b95f4b70b4fe62af2d87df811f0ba129fa9e1" + } + }, + "rein-aharness": { + "head": "b80096133f9b7ad8688535e06ba253729f3948fa", + "files": { + "SCOPE.md": "21d3b85a79205541591d139caf5184f4402ee2f53dce562f3a7a1da1dec62683" + } + }, + "state-hub": { + "head": "39b51a21a51204675cc46d4b51c00fddd1851449", + "files": { + "scripts/configure-codex.sh": "c6e0e2f47f5ff9e34f7b04a887e3e8e942fcd3eb78c5713be9d1eb4d49091c32" + } + } +} diff --git a/docs/changes/CUST-WP-0073/privileged-preflight-r1/test_preflight.py b/docs/changes/CUST-WP-0073/privileged-preflight-r1/test_preflight.py new file mode 100644 index 0000000..7ae4696 --- /dev/null +++ b/docs/changes/CUST-WP-0073/privileged-preflight-r1/test_preflight.py @@ -0,0 +1,51 @@ +import contextlib +import io +import json +import unittest +from unittest.mock import patch +import preflight + +SENTINEL='synthetic-sensitive-fixture-value' + +class PreflightTests(unittest.TestCase): + def test_unrelated_protected_values_never_return(self): + result=preflight.env_predicates(('TOKEN='+SENTINEL+'\nOTHER="'+SENTINEL+'"\n').encode()) + self.assertTrue(result['syntax_supported']);self.assertTrue(result['selectors_absent']) + self.assertNotIn(SENTINEL,json.dumps(result)) + + def test_each_selector_blocks_without_exposing_value(self): + for key in preflight.SELECTORS: + result=preflight.env_predicates((key+'='+SENTINEL).encode()) + self.assertFalse(result['selectors_absent']);self.assertTrue(result['selector_presence'][key]) + self.assertNotIn(SENTINEL,json.dumps(result)) + + def test_effective_process_values_cannot_create_fake_keys(self): + result=preflight.env_predicates(('TOKEN=x\nK3S_CONFIG_FILE='+SENTINEL+'\0').encode(),process=True) + self.assertFalse(result['syntax_supported']) + self.assertNotIn(SENTINEL,json.dumps(result)) + + def test_process_selector_detected(self): + result=preflight.env_predicates(b'A=x\0K3S_CONFIG_FILE=/hidden\0',process=True) + self.assertFalse(result['selectors_absent']) + + def test_complex_syntax_refuses(self): + for raw in (b'export KEY=value',b'A="unterminated',b'A=x\\\ny',b'A=x\nA=y'): + self.assertFalse(preflight.env_predicates(raw)['syntax_supported']) + + def test_comments_cannot_activate_selector(self): + result=preflight.env_predicates(b'# K3S_CONFIG_FILE=/ignored\n; comment\nA=x\n') + self.assertTrue(result['selectors_absent']) + + def test_nonroot_refuses_before_any_files(self): + with patch.object(preflight.os,'geteuid',return_value=1000),patch.object(preflight,'read_fixed') as read: + self.assertEqual(preflight.inspect()['status'],'refused_requires_attended_root') + read.assert_not_called() + + def test_exception_text_never_emitted(self): + output=io.StringIO() + with patch.object(preflight,'inspect',side_effect=ValueError(SENTINEL)),contextlib.redirect_stdout(output): + self.assertEqual(preflight.main(),1) + self.assertNotIn(SENTINEL,output.getvalue()) + self.assertFalse(json.loads(output.getvalue())['passed']) + +if __name__=='__main__':unittest.main() diff --git a/docs/changes/CUST-WP-0073/privileged-preflight-r1/validation.json b/docs/changes/CUST-WP-0073/privileged-preflight-r1/validation.json new file mode 100644 index 0000000..4b870f6 --- /dev/null +++ b/docs/changes/CUST-WP-0073/privileged-preflight-r1/validation.json @@ -0,0 +1,7 @@ +{ + "captured_at": "2026-10-19T12:31:27.577940+00:00", + "tests_passed": 8, + "privileged_execution": false, + "live_mutations": false, + "interactive_scope_selected": false +} diff --git a/docs/decisions/2026-10-19-custodian-delivery-interview.md b/docs/decisions/2026-10-19-custodian-delivery-interview.md new file mode 100644 index 0000000..b47b11d --- /dev/null +++ b/docs/decisions/2026-10-19-custodian-delivery-interview.md @@ -0,0 +1,39 @@ +# Custodian delivery interview — 2026-10-19 + +Decided by Bernd Worsch in this interactive session. Scope: implementation +direction for CUST-WP-0071, CUST-WP-0073 and CUST-WP-0079. These answers do not +claim runtime acceptance or change the standing live-change/publication rules. + +## Runtime placement + +Selected: “Existing Activity Core worker with pinned scripts and private retained +state (recommended)”. Prepare the existing-worker implementation, exact source +packaging, fleet input publication and retained-state ownership. Keep both +schedules disabled until the reviewed deployment/activation conditions hold. +No dedicated worker or second scheduler is required by this selection. + +## Credential-separation priority + +Selected: “Isolate this interactive Codex assignment from admin credentials +(recommended)”. The first acceptance target is the actual interactive assignment, +including its tool transports, inherited credentials and privileged execution +boundary. A sandboxed child process or native Claude-worker rehearsal cannot +substitute for that target. Native-worker isolation remains distinct; do not +make it an artificial prerequisite unless the actual selected path needs it. + +## Provisional workload sizing + +Asked whether to permit an explicitly agreed synthetic workload for provisional +sizing before the attended Bernd/company pilot, with human workflow acceptance +remaining separate. Answer: “Let's do one and put the sizing parameters into +configs that can be tuned later on.” + +Proceed with a configurable synthetic workload and provisional sizing evidence. +Reuse the existing two-user, ordinary-operation p95 ≤ 2 seconds and zero-failure +criterion; report document transfer separately. Put corpus size, concurrency, +operation mix, duration and other assumptions in owner configuration. Retain +actual runtime/measurement provenance and limitations. Unit-test or SQLite +fixture timing is not deployed PostgreSQL/container sizing evidence. Do not +claim the separate human workflow/recovery acceptance from a synthetic run. + +Hub decision receipt: `8a64ba65-1068-409c-86f8-b9414b29b7b4`. diff --git a/workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md b/workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md index 0cac9e3..1840594 100644 --- a/workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md +++ b/workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md @@ -4,7 +4,7 @@ type: workplan title: "Size Railiance workloads from actual demand and establish a weekly allocation review" domain: infotech repo: the-custodian -status: blocked +status: active flavor: planning owner: the-custodian topic_slug: custodian @@ -94,11 +94,11 @@ updated reef-railiance-k3s owner evidence. ```task id: CUST-WP-0071-T02 -status: wait +status: progress priority: high assignee: the-custodian -depends_on: [CUST-WP-0071-T01, RAPPS-WP-0014-T03] -blocking_reason: "Await RAPPS-WP-0014-T03 (blocked, needs_human): populated two-user/document workflow run by Bernd and the company contact, giving representative load for p95 <= 2s / zero failures." +depends_on: [CUST-WP-0071-T01] +blocking_reason: "Configurable synthetic workload and real isolated runtime measurements in preparation under the 2026-10-19 interview decision. Human pilot acceptance remains RAPPS-WP-0014-T03 and does not gate provisional sizing." state_hub_task_id: "82192370-2fd7-5363-88d2-3c67889d3d68" ``` @@ -404,3 +404,20 @@ This does not close T05: collection, recommendation handling, delivery, owner acknowledgment and missed/failed-run recovery still need runtime acceptance, after the existing representative pilot and sizing gates. No new scheduler, model call or credential-isolation claim is introduced. + + +## Implementation direction from interview — 2026-10-19 + +Decision `8a64ba65-1068-409c-86f8-b9414b29b7b4`, source +`docs/decisions/2026-10-19-custodian-delivery-interview.md`, authorizes a +configurable synthetic workload for provisional sizing before attended human +acceptance. T02 is progress; its dependency on RAPPS-WP-0014-T03 is removed only +for this provisional evidence path. That owner's human workflow/recovery gate +remains unchanged and must not be claimed complete from synthetic results. + +The Vergabe owner worker must provide configuration for concurrency, corpus, +operation mix, duration and document sizes, a real HTTP/isolated-runtime driver, +and retained measurement provenance. Django/SQLite fixture timing is test +coverage, not deployment sizing. Retain the existing two-user p95≤2s/no-failure +criterion and separate document transfer measurements. Proposed allocations stay +provisional until representative evidence and owner review support them. diff --git a/workplans/CUST-WP-0073-agent-credential-separation.md b/workplans/CUST-WP-0073-agent-credential-separation.md index b3fffb8..d6b913e 100644 --- a/workplans/CUST-WP-0073-agent-credential-separation.md +++ b/workplans/CUST-WP-0073-agent-credential-separation.md @@ -4,7 +4,7 @@ type: workplan title: "Separate agent credentials and establish supervised privileged execution" domain: infotech repo: the-custodian -status: blocked +status: active owner: the-custodian topic_slug: custodian flavor: implementation @@ -84,10 +84,10 @@ limits or authorization of a live cutover. Existing human-only lanes still apply ```task id: CUST-WP-0073-T02 -status: wait +status: progress priority: high -depends_on: [GLAS-WP-0012] -blocking_reason: "Await GLAS-WP-0012 (glas-harness) production acceptance of the local supervised profile; then railiance-platform RBAC, railiance-cluster persistent k3s config, railiance-infra principal/account mapping and ops-warden certificate issuance." +depends_on: [] +blocking_reason: "Interactive Codex assignment is the first target under the 2026-10-19 interview decision. Preparing a supported initial launch/tool-transport boundary and exact owner preflight; no actual account/profile admission yet. Native Claude-worker GLAS-WP-0012 proof is a distinct path, not a substitute." state_hub_task_id: "4b88b0b7-dc7e-5612-aa5d-1a08f0530c35" ``` @@ -413,3 +413,23 @@ compare-and-swap recovery without silently restoring world-readable credentials. Full workstation/assignment isolation, native credential/egress acceptance and actual-account denial proof remain required under T02/T04. No scored proposal success or runtime admission follows from this read-only preparation. + + +## Interactive-assignment priority — 2026-10-19 interview + +Decision `8a64ba65-1068-409c-86f8-b9414b29b7b4` selects isolation of this actual +interactive Codex assignment first. T02 is progress for owner launch/transport +work. Remove the blanket GLAS-WP-0012 prerequisite: that native Claude-worker +proof cannot establish this assignment's boundary. Reuse any actual shared +mechanism needed, but require supported initial launch before the first tool, +no parent/external credential bypass, and the existing actual-account positive +and negative proofs. This prioritization grants no privileged cutover or access +scope change and does not weaken acceptance. + +The separate read-only protected-input preflight is retained at +`docs/changes/CUST-WP-0073/privileged-preflight-r1/OPERATOR.md`. Its eight fixture +tests and retained SHA256SUMS pass independent review. It has not been executed +with privileges. The historical interactive-choice field predates the interview; +the decision above supersedes that field without rewriting the original evidence. +Root-owned staging and an exact supervisor-approved execution revision remain +required for a live protected-input receipt; this preparation grants no cutover. diff --git a/workplans/CUST-WP-0079-deterministic-security-flow.md b/workplans/CUST-WP-0079-deterministic-security-flow.md index 302223a..a5e79ea 100644 --- a/workplans/CUST-WP-0079-deterministic-security-flow.md +++ b/workplans/CUST-WP-0079-deterministic-security-flow.md @@ -4,7 +4,7 @@ type: workplan title: "Deterministic security flow: standing authorizations, flow report, and agent re-engagement" domain: infotech repo: the-custodian -status: blocked +status: active owner: claude-code topic_slug: custodian created: "2026-10-11" @@ -59,7 +59,7 @@ resolved or accepted). ```task id: CUST-WP-0079-T03 -status: wait +status: progress priority: high state_hub_task_id: "1be0569c-221d-5925-a0ff-fd2af88e52b1" depends_on: [ACTIVITY-WP-0047] @@ -474,3 +474,23 @@ fleet publisher/freshness and state retention/backup acceptance remain owner inputs. They were requested without enabling schedules or claiming deployment approval. This is source preparation for T03, not a new scheduler or first-fire proof. Activity Core remained clean after the documentation commit. + + +## Owner delivery resumed after interview — 2026-10-19 + +Decision `8a64ba65-1068-409c-86f8-b9414b29b7b4` selects the existing Activity +Core worker with pinned owner scripts and private retained state. T03 is progress +for the delegated Activity Core/Repo Manager source packaging and snapshot +publisher work. Both definitions remain disabled pending the exact reviewed +implementation and existing activation conditions. No second worker/scheduler is +needed by this selected design. Routine live changes use the existing standing +build-mode rule only when its concrete review, rollback, abort-owner and receipt +conditions hold; access/secret scope, spend and public-publication exclusions +remain in force. + +Policy Nexus `8af92bf` resolves the two source-discovery review rows as explicit +metadata-pending under existing inventory rules. No publication mappings or +foreign canon changed. Its 65 tests,186-source audit (74 published/94 pending), +local immutable release verification and currency checks pass. T04 still needs +the founder's existing public-release disposition for the build-mode standard +and derived JSON; the two unrelated pending documents are not part of that ask.