"""Same-login setup and native MXC qualification. Never grants roadmap authorization.""" import csv from datetime import datetime, timezone, timedelta import hashlib import json import os from pathlib import Path import shutil import socket import subprocess import sys import uuid PROFILE = 'jobjakt-local' CATEGORIES = {'configuration', 'approvals', 'key-file', 'personal', 'normal-codex', 'other-repository', 'production-canary'} def single(config): return config.get('mode') == 'single-user-local' def detect_identity(): result = subprocess.run(['whoami','/user','/fo','csv','/nh'],capture_output=True,text=True,check=True,timeout=15) username,sid = next(csv.reader(result.stdout.splitlines())) return username,sid def validate_identity(config, sid): if not sid.startswith('S-1-') or config['ownerSid'] != sid or config['runnerSid'] != sid: raise ValueError('single-user-local requires the configured current Windows identity for both owner and runner') def validate_config(root, config): root = Path(root).resolve() repo = Path(config['repository']).resolve() auth = Path(config['workerCodeXHome']).resolve() normal = Path(config['normalCodeXHome']).resolve() temporary = Path(config['workerTemp']).resolve() if root.is_relative_to(repo) or repo.is_relative_to(root): raise ValueError('ControlRoot and worker must be disjoint') if auth == normal or auth.is_relative_to(repo) or not auth.is_relative_to(root): raise ValueError('Dedicated auth-only CODEX_HOME must be inside ControlRoot, outside worker, distinct from normal auth') if (temporary.is_relative_to(root) or temporary.is_relative_to(normal) or temporary == repo or root.is_relative_to(temporary) or normal.is_relative_to(temporary)): raise ValueError('Use a separate disposable worker temp directory') for name in config.get('toolReadRoots',[]): tool=Path(name).resolve() if any(tool.is_relative_to(private) or private.is_relative_to(tool) for private in (root,normal)): raise ValueError('Tool roots cannot reopen protected control or normal Codex paths') if config.get('trustedCodexOverrides'): raise ValueError('single-user-local plugin/MCP access is disabled until independently qualified') if config.get('executionPolicy') not in ('operator-controlled','qualified-unattended'): raise ValueError('Unknown single-user execution policy') for name in ('config.toml','plugins','skills','hooks.json','AGENTS.md'): if (auth / name).exists(): raise ValueError('Auth-only CODEX_HOME contains unrelated config, hooks, skills or plugins') for name in ('.codex/config.toml','.mcp.json'): if (repo / name).exists(): raise ValueError('Worker has an unqualified repository Codex/MCP configuration') def filesystem_rules(root, config): repo = Path(config['repository']).resolve() rules = {':root':'deny', ':minimal':'read', repo.as_posix():'write', Path(config['workerTemp']).resolve().as_posix():'write'} for name in config.get('toolReadRoots',[]): path = Path(name).resolve() if path == Path(path.anchor) or path == Path.home(): raise ValueError('Tool read roots cannot grant an entire drive or home directory') rules[path.as_posix()] = 'read' for path in (Path(root),Path(config['workerCodeXHome']),Path(config['normalCodeXHome']), Path.home()/'.ssh',Path.home()/'AppData/Roaming/Microsoft/Crypto'): rules[path.resolve().as_posix()] = 'deny' for name in ('.env','.env.local','.env.production','appsettings.Production.json'): rules[(repo/name).as_posix()] = 'deny' rules[(repo/'.git').as_posix()] = 'read' return rules def permission_args(root, config, command): validate_config(root,config) table = '{' + ','.join(json.dumps(k)+'='+json.dumps(v) for k,v in filesystem_rules(root,config).items()) + '}' args = ['-c','windows.sandbox="mxc"','-c','approval_policy="never"', '-c','cli_auth_credentials_store="file"','-c','forced_login_method="chatgpt"', '-c','features.apps=false','-c','features.plugins=false','-c','features.remote_plugin=false', '-c','mcp_servers={}','-c','plugins={}','-c','web_search="disabled"', '-c',f'default_permissions="{PROFILE}"', '-c',f'permissions.{PROFILE}.filesystem={table}', '-c',f'permissions.{PROFILE}.network.enabled=false'] if command == 'sandbox': args += ['--include-managed-config','--permission-profile',PROFILE,'--'] return args def binding(root,config): value = {k:v for k,v in config.items() if k != 'executionPolicy'} for name in ('local_mode.py','controller.py','Probe-JobjaktSandbox.ps1'): source = Path(root)/name if source.exists(): value[name+'Sha256'] = hashlib.sha256(source.read_bytes()).hexdigest() return hashlib.sha256(json.dumps(value,sort_keys=True).encode()).hexdigest() def probes_passed(result): files = result.get('files',[]) return (result.get('positiveControl') is True and result.get('networkDenied') is True and result.get('signingKeyDenied') is True and {f.get('category') for f in files} == CATEGORIES and all(f.get('readDenied') is True and f.get('writeDenied') is True for f in files)) def require_unattended(root,config): validate_config(root,config) if config['executionPolicy'] != 'qualified-unattended': raise ValueError('operator-controlled pause: no autonomous worker is launched; inspect requests and qualify MXC first') path = Path(root)/'qualification.json' if not path.is_file(): raise ValueError('Native sandbox qualification is missing') report = json.loads(path.read_text(encoding='utf-8')) issued = datetime.fromisoformat(report['issuedUtc']) now = datetime.now(timezone.utc) if (report.get('status') != 'PASS' or report.get('failure') or report.get('binding') != binding(root,config) or not probes_passed(report.get('probes',{})) or report.get('ownerKeyDenied') is not True or issued > now+timedelta(minutes=5) or now-issued > timedelta(hours=24)): raise ValueError('Native sandbox/key qualification failed, expired or changed; operator-controlled pause required') def configure_current_user(root,config): username,sid = detect_identity() root = Path(root).resolve() normal = Path(os.environ.get('CODEX_HOME',str(Path.home()/'.codex'))).resolve() config.update(mode='single-user-local',executionPolicy='operator-controlled', windowsUsername=username,ownerSid=sid,runnerSid=sid,normalCodeXHome=str(normal), workerCodeXHome=str(root/'auth'), workerTemp=str(root.parent/(root.name+'-worker-temp')), codexExecutable=shutil.which('codex') or 'codex',gitExecutable=shutil.which('git') or 'git', powershellExecutable=shutil.which('pwsh') or 'pwsh',pythonExecutable=sys.executable, trustedCodexOverrides=[],toolReadRoots=sorted({str(Path(sys.executable).parent), str(Path(shutil.which('pwsh') or sys.executable).parent)}), ownerCertificateThumbprint=None) validate_config(root,config) Path(config['workerCodeXHome']).mkdir(parents=True,exist_ok=True) Path(config['workerTemp']).mkdir(parents=True,exist_ok=True) return config def auth_command(config, status=False): args = [config['codexExecutable'],'-c','cli_auth_credentials_store="file"', '-c','forced_login_method="chatgpt"','login'] return args + (['status'] if status else []) def qualify(engine): """Only synthetic canaries and a local TCP listener. No model or production contact.""" root,config = engine.root,engine.config validate_config(root,config) _,sid = detect_identity() validate_identity(config,sid) token = uuid.uuid4().hex workspace = engine.repo/'.artifacts/orchestrator-probes'/token outside = root.parent/('orchestrator-probes-'+token) normal = Path(config['normalCodeXHome'])/('.jobjakt-probe-'+token) workspace.mkdir(parents=True) outside.mkdir() paths = [] owned = [] key = None report = dict(issuedUtc=datetime.now(timezone.utc).isoformat(),binding=binding(root,config), status='BLOCKED',ownerKeyDenied=False,probes={},failure=None) listener = socket.socket() try: for category in sorted(CATEGORIES): path = normal if category == 'normal-codex' else ( root/('canary-'+token+'-'+category) if category in ('configuration','approvals','key-file') else outside/category) path.write_text('SYNTHETIC-'+token,encoding='utf-8') owned.append(path) with path.open('r+b'): pass # prove a denial is not a missing file or preexisting host ACL failure paths.append(dict(category=category,path=str(path))) helper = root/'Probe-JobjaktSandbox.ps1' setup = subprocess.run([config['powershellExecutable'],'-NoProfile','-File',str(helper), '-Action','CreateKey','-Tag',token],text=True,capture_output=True,timeout=30) if setup.returncode: raise ValueError('Temporary signing-key positive control failed: '+setup.stderr[:250]) key = json.loads(setup.stdout) owner_key = None if config.get('ownerCertificateThumbprint'): positive = subprocess.run([config['powershellExecutable'],'-NoProfile','-File',str(helper), '-Action','TestOwnerKey','-Thumbprint',config['ownerCertificateThumbprint']], text=True,capture_output=True,timeout=30) if positive.returncode: raise ValueError('Configured owner key is not usable in the trusted operator context') owner_key = json.loads(positive.stdout) if owner_key['certificateSha256'] != config['ownerCertificateSha256']: raise ValueError('Configured owner private key does not match the pinned public certificate') listener.bind(('127.0.0.1',0)) listener.listen(4) with socket.create_connection(listener.getsockname(),timeout=2): pass accepted,_ = listener.accept() accepted.close() plan = dict(files=paths,port=listener.getsockname()[1],key=key, ownerThumbprint=config.get('ownerCertificateThumbprint'),ownerKeyName=owner_key['keyName'] if owner_key else None) (workspace/'plan.json').write_text(json.dumps(plan),encoding='utf-8') shutil.copyfile(helper,workspace/'probe.ps1') argv = [config['codexExecutable'],'sandbox','-C',str(engine.repo), *permission_args(root,config,'sandbox'),config['powershellExecutable'],'-NoProfile', '-File',str(workspace/'probe.ps1'),'-Action','Probe','-PlanPath',str(workspace/'plan.json')] logs = root/'probe'/token engine.runner.supervise(engine,argv,logs,45) probe = json.loads((logs/'stdout.log').read_text(encoding='utf-8')) report.update(probes=probe,ownerKeyDenied=probe.get('ownerKeyDenied') is True, status='PASS' if probes_passed(probe) else 'FAIL') report['scope'] = 'Synthetic filesystem/network/signing canaries; real owner key '+('probed' if owner_key else 'NOT_CONFIGURED') except Exception as error: report['failure'] = str(error)[:500] finally: listener.close() if key: cleanup = subprocess.run([config['powershellExecutable'],'-NoProfile','-File', str(root/'Probe-JobjaktSandbox.ps1'),'-Action','RemoveKey','-Tag',token, '-Thumbprint',key['thumbprint']],capture_output=True,text=True,timeout=30) if cleanup.returncode: report['status'] = 'BLOCKED' report['failure'] = 'Canary certificate cleanup failed; inspect the recorded synthetic thumbprint' for path in owned: path.unlink(missing_ok=True) # Only the exact disposable directories created above are removed. if workspace.resolve().is_relative_to(engine.repo.resolve()/'.artifacts/orchestrator-probes'): shutil.rmtree(workspace) if outside.parent.resolve() == root.parent.resolve() and outside.name == 'orchestrator-probes-'+token: shutil.rmtree(outside) engine_file = root/'qualification.json' from controller import atomic atomic(engine_file,report) return report