Retain interactive isolation target and identity review
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 10s
Reject generated-only commits / generated-commit-guard (push) Successful in 11s

This commit is contained in:
codex 2026-10-19 14:47:02 +02:00
parent 03eb3fbd3e
commit 903616f2b7
18 changed files with 704 additions and 0 deletions

View file

@ -0,0 +1,98 @@
# Actual interactive Custodian assignment: supported handoff proposal
2026-10-19; preparation only. User selected this interactive Codex assignment
first. No host/user creation, authentication, handoff, sudo, deployment, daemon
restart, or production mutation occurred. This adds evidence to CUST-WP-0073;
it neither supersedes the original credential-boundary revision 2 nor proves
admission. The protected k3s preflight remains separately reviewable at
`/tmp/cust73-privileged-preflight-r1`; its earlier boundary-choice field is
historical and is superseded by the user's explicit interactive-first choice.
## Observed current execution boundary
`observation.json` records a UTC-bound, sanitized process ancestry. In the tool
PID namespace, parent PID 1 executes the installed Codex 0.163.0 app-server-daemon
binary and all observed UIDs are 1000. PATH resolves a different Codex installation.
This does not establish the outer desktop frontend, transport endpoint, or
daemon invocation: PID namespace ancestry ends at PID 1. No raw command line,
environment, authentication file, session transcript, or credential was printed.
The inspected user config has no explicit approval/sandbox override or MCP server
entries; this does not describe managed settings, frontend apps, or dynamic tools.
## Supported route and concrete operator integration
The official [remote connections guide](https://learn.chatgpt.com/docs/remote-connections)
supports an SSH connection that starts Codex under the remote login user, then
desktop handoff of an existing chat to a saved matching Git project. Select the
chat's run location, choose destination, and confirm Hand off. Handoff transfers
chat/Git state and interrupts an active response. This chat cannot self-handoff;
the operator or a separate chat must perform it. Confirm the installed frontend
exposes these controls before provisioning; this environment cannot inspect its UI.
Use a dedicated non-admin OS account on an approved target, with a private home
and private Codex state, no inherited SSH agent/admin kubeconfig/OpenBao tokens,
no sudo or container-control groups, and an isolated repo checkout. Choose the
target in the proposal rather than assuming the current workstation or the live
k3s server is already suitable. Do not copy the operator's `.codex`, `.ssh`,
`.kube`, environment, or global Git credential helper. Authentication for this
account requires its own approved owner route. SSH alias must specify the new
account and pinned host identity, with agent forwarding disabled.
After account provisioning and owner-authentication approval: verify `codex` on
that account's login PATH; add the alias in desktop Settings > Connections;
save the same Git repository/subdirectory there; capture its version/digest,
OS identity and policy. Use the native handoff UI for THIS chat. No custom
Glas rein is required merely to obtain the documented interactive transport.
Keep old chat paused after handoff until admission passes. Failure returns to
supervised preparation; it does not enable the old credential-bearing execution
path for assignment work. Preserve receipts and explicitly record any rollback.
The [app-server approval protocol](https://learn.chatgpt.com/docs/app-server)
sends command/file approval requests to the client with thread and turn IDs.
Retain this native approval UI, and capture both approved and denied canary
outcomes in the destination chat. Its decision never grants OS sudo rights.
Custom clients would need these request/response flows; building one is not the
minimal route. Direct network exposure of app-server is unnecessary.
## Concrete owner work
* Host provisioning owner (railiance-infra for managed estate hosts): add dedicated
account/private checkout and explicit no-admin group/sudo policy. Existing
`ansible/roles/ssh_ca_host/tasks/main.yml` installs per-user principals but does
not create or confine a Codex account. Add its principal mapping only alongside
the new account; never map the agt principal to tegwick/admin. Resolve an
unmanaged workstation owner explicitly if that is the selected target.
* ops-warden: existing SSH certificate route, after exact account/principal and
target approval. It does not vend Codex authentication or Kubernetes tokens.
* Custodian assignment supervisor: bind proposal revision, actual chat identity,
target and host account; operator performs native handoff and verifies the new
chat execution receipt. A new unrelated CLI session is not this acceptance.
* Glas: accept/record the externally attached interactive assignment's evidence
if its existing admission API is used; add a truthful interactive capability
only after proof. Current gateway/rein catalog and bounded sand-boxer runner
do not implement this frontend transport. Do not gate this route on another
Claude startup rehearsal or claim the old native lane proves Codex isolation.
## Acceptance, without touching credential contents
Run `probe_identity.py EXPECTED_UID EXPECTED_MACHINE_ID_SHA256` from a tool call
in the handed-off chat, with expected values set by the supervisor's target
receipt. It rejects UID 0 or the current UID 1000 and suppresses all credential
values. This is a partial identity check, not admission by itself.
Then bind an external supervisor observation of the app-server/tool process
identity and thread to that same receipt; child-only probes can be fabricated by
launching a child and therefore never suffice. Verify: private state, denied
admin kubeconfig/SSH socket/OpenBao access, no privileged group/socket/sudo
paths, bounded Kubernetes credential and aggregate PLUS every-namespace RBAC
denials from the original packet, actual denied/approved native UI canaries,
and no old process receiving tool execution. Inventory frontend connectors and
dynamic tools separately: moving the shell UID does not revoke frontend account
capabilities. Disable or explicitly scope external privileged tools.
Unresolved concrete gates: selected target/account, installed UI handoff support,
account/SSH/authentication approval, protected k3s selector preflight and host
mode600 approval if targeting that host, authoritative namespace inventory,
actual-chat identity/approval/credential-denial receipts, external-tool scope.
No acceptance status should move to done from these fixture or documentation
results.

View file

@ -0,0 +1,6 @@
ad6383e665e1d9866929d350ca17f65f337c3fe84396537b86f1bd404cd19cee README.md
f4fd07b9e35bc2163accaeca921713ff2a3fe439ccc60f550e0e35c30eccb348 observation.json
62fe730496e3ce8087af9753349dd3cb9556435a7a5bfb7dd7bddf14ba549f7f probe_identity.py
c9bdb1a755ad56c5adce4ff2930c3fc627ee36eb226f7315eb9b015189b991fe source-pins.json
781f490edf93c119b7ceb5aea34c4796b16ace9104e5cb6ecaa7aa5e6a38c6c7 test_probe_identity.py
5c95e40fe85e6ee928d598ff2c435023db0d743f65e85d62d5d0fdf76e9d481c validation.json

View file

@ -0,0 +1,46 @@
{
"utc": "2026-10-19T12:34:11.864590+00:00",
"codex": "/home/worsch/.nvm/versions/node/v24.11.1/bin/codex",
"ancestry": [
{
"pid": 3,
"comm": "python3",
"uid": [
"1000",
"1000",
"1000",
"1000"
],
"ppid": 2,
"exe": "/usr/bin/python3.12"
},
{
"pid": 2,
"comm": "bash",
"uid": [
"1000",
"1000",
"1000",
"1000"
],
"ppid": 1,
"exe": "/usr/bin/bash"
},
{
"pid": 1,
"comm": "codex",
"uid": [
"1000",
"1000",
"1000",
"1000"
],
"ppid": 0,
"exe": "/home/worsch/.codex/packages/app-server-daemon/releases/0.163.0-x86_64-unknown-linux-musl/bin/codex"
}
],
"config": {
"mcp_names": [],
"feature_names": []
}
}

View file

@ -0,0 +1,38 @@
#!/usr/bin/env python3
"""Partial read-only identity evidence. Never an admission authority."""
import datetime
import hashlib
import json
import os
from pathlib import Path
import sys
def evaluate(expected_uid, expected_machine, actual_uid, machine_digest):
return {
"identity_matches": actual_uid == expected_uid
and actual_uid not in (0, 1000)
and len(expected_machine) == 64
and expected_machine == machine_digest,
"actual_uid": actual_uid,
"assignment_admitted": False,
"scope": "partial_process_identity_only",
}
def main():
try:
expected_uid = int(sys.argv[1])
expected_machine = sys.argv[2]
machine = hashlib.sha256(Path('/etc/machine-id').read_bytes()).hexdigest()
result = evaluate(expected_uid, expected_machine, os.geteuid(), machine)
result['capture_utc'] = datetime.datetime.now(datetime.timezone.utc).isoformat()
except Exception:
result = {'identity_matches': False, 'assignment_admitted': False,
'reason': 'identity_probe_refused'}
print(json.dumps(result, sort_keys=True))
return 0 if result['identity_matches'] else 1
if __name__ == '__main__':
sys.exit(main())

View file

@ -0,0 +1,27 @@
{
"glas-harness": {
"head": "a19bff487d41f18c58a8fd713f6d4e04c3a218d5",
"files": {
"src/glas_harness/gateway.py": "5e998bfa53ade5dafbae799dc04b77031b6414e2af49797f45727d59d7397d42",
"registry/reins/rein-aharness.yaml": "8cdbe7b5ee45e2fb5ca2ed089cab377580034dc261c46bfcf889bddb68cf3da0"
}
},
"sand-boxer": {
"head": "c7c310266aba96ab7dd03cd8bfcaa65f22a3b66e",
"files": {
"src/sandboxer/extensions/bwrap_runner.py": "c83cdd2758bae58cd5d5694f5d6752736f59d479cfc6e9f0f8841c1f5596cba6"
}
},
"railiance-infra": {
"head": "8b7ade9f66fe95d7002b5494d6302763a168827b",
"files": {
"ansible/roles/ssh_ca_host/tasks/main.yml": "c840ccb53d351ca6a1bb8b7a8406efae4451bb651707b03353f5145aa8f21fd3"
}
},
"state-hub": {
"head": "39b51a21a51204675cc46d4b51c00fddd1851449",
"files": {
"scripts/configure-codex.sh": "c6e0e2f47f5ff9e34f7b04a887e3e8e942fcd3eb78c5713be9d1eb4d49091c32"
}
}
}

View file

@ -0,0 +1,21 @@
import unittest
from probe_identity import evaluate
class IdentityTests(unittest.TestCase):
def test_old_identity_and_root_never_pass(self):
for uid in (0, 1000):
self.assertFalse(evaluate(uid, 'a'*64, uid, 'a'*64)['identity_matches'])
def test_wrong_host_or_user_fail(self):
self.assertFalse(evaluate(1200, 'a'*64, 1201, 'a'*64)['identity_matches'])
self.assertFalse(evaluate(1200, 'a'*64, 1200, 'b'*64)['identity_matches'])
def test_matching_child_is_never_assignment_admission(self):
result = evaluate(1200, 'a'*64, 1200, 'a'*64)
self.assertTrue(result['identity_matches'])
self.assertFalse(result['assignment_admitted'])
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,8 @@
{
"interactive_first": true,
"fixture_tests": 3,
"privileged_preflight_fixture_tests": 8,
"live_cutover": false,
"privileged_execution": false,
"actual_assignment_admitted": false
}

View file

@ -0,0 +1,98 @@
# Actual interactive Custodian assignment: supported handoff proposal
2026-10-19; revision 2, preparation only. Revision 1 is preserved unchanged.
See TARGET.md and provisioning-contract.json for the concrete destination. User selected this interactive Codex assignment
first. No host/user creation, authentication, handoff, sudo, deployment, daemon
restart, or production mutation occurred. This adds evidence to CUST-WP-0073;
it neither supersedes the original credential-boundary revision 2 nor proves
admission. The protected k3s preflight remains separately reviewable at
`/tmp/cust73-privileged-preflight-r1`; its earlier boundary-choice field is
historical and is superseded by the user's explicit interactive-first choice.
## Observed current execution boundary
`observation.json` records a UTC-bound, sanitized process ancestry. In the tool
PID namespace, parent PID 1 executes the installed Codex 0.163.0 app-server-daemon
binary and all observed UIDs are 1000. PATH resolves a different Codex installation.
This does not establish the outer desktop frontend, transport endpoint, or
daemon invocation: PID namespace ancestry ends at PID 1. No raw command line,
environment, authentication file, session transcript, or credential was printed.
The inspected user config has no explicit approval/sandbox override or MCP server
entries; this does not describe managed settings, frontend apps, or dynamic tools.
## Supported route and concrete operator integration
The official [remote connections guide](https://learn.chatgpt.com/docs/remote-connections)
supports an SSH connection that starts Codex under the remote login user, then
desktop handoff of an existing chat to a saved matching Git project. Select the
chat's run location, choose destination, and confirm Hand off. Handoff transfers
chat/Git state and interrupts an active response. This chat cannot self-handoff;
the operator or a separate chat must perform it. Confirm the installed frontend
exposes these controls before provisioning; this environment cannot inspect its UI.
Use a dedicated non-admin OS account on an approved target, with a private home
and private Codex state, no inherited SSH agent/admin kubeconfig/OpenBao tokens,
no sudo or container-control groups, and an isolated repo checkout. Use the dedicated workstation distribution proposed in TARGET.md; the current
operator distribution and production server are not pre-approved destinations. Do not copy the operator's `.codex`, `.ssh`,
`.kube`, environment, or global Git credential helper. Authentication for this
account requires its own approved owner route. SSH alias must specify the new
account and pinned host identity, with agent forwarding disabled.
After account provisioning and owner-authentication approval: verify `codex` on
that account's login PATH; add the alias in desktop Settings > Connections;
save the same Git repository/subdirectory there; capture its version/digest,
OS identity and policy. Use the native handoff UI for THIS chat. No custom
Glas rein is required merely to obtain the documented interactive transport.
Keep old chat paused after handoff until admission passes. Failure returns to
supervised preparation; it does not enable the old credential-bearing execution
path for assignment work. Preserve receipts and explicitly record any rollback.
The [app-server approval protocol](https://learn.chatgpt.com/docs/app-server)
sends command/file approval requests to the client with thread and turn IDs.
Retain this native approval UI, and capture both approved and denied canary
outcomes in the destination chat. Its decision never grants OS sudo rights.
Custom clients would need these request/response flows; building one is not the
minimal route. Direct network exposure of app-server is unnecessary.
## Concrete owner work
* Host provisioning owner (railiance-infra for managed estate hosts): add dedicated
account/private checkout and explicit no-admin group/sudo policy. Existing
`ansible/roles/ssh_ca_host/tasks/main.yml` installs per-user principals but does
not create or confine a Codex account. Add its principal mapping only alongside
the new account; never map the agt principal to tegwick/admin. Resolve an
unmanaged workstation owner explicitly if that is the selected target.
* ops-warden: existing SSH certificate route, after exact account/principal and
target approval. It does not vend Codex authentication or Kubernetes tokens.
* Custodian assignment supervisor: bind proposal revision, actual chat identity,
target and host account; operator performs native handoff and verifies the new
chat execution receipt. A new unrelated CLI session is not this acceptance.
* Glas: accept/record the externally attached interactive assignment's evidence
if its existing admission API is used; add a truthful interactive capability
only after proof. Current gateway/rein catalog and bounded sand-boxer runner
do not implement this frontend transport. Do not gate this route on another
Claude startup rehearsal or claim the old native lane proves Codex isolation.
## Acceptance, without touching credential contents
Run `probe_identity.py EXPECTED_UID EXPECTED_MACHINE_ID_SHA256` from a tool call
in the handed-off chat, with expected values set by the supervisor's target
receipt. It rejects UID 0 or the original machine-ID/UID pair and suppresses all credential
values. This is a partial identity check, not admission by itself.
Then bind an external supervisor observation of the app-server/tool process
identity and thread to that same receipt; child-only probes can be fabricated by
launching a child and therefore never suffice. Verify: private state, denied
admin kubeconfig/SSH socket/OpenBao access, no privileged group/socket/sudo
paths, bounded Kubernetes credential and aggregate PLUS every-namespace RBAC
denials from the original packet, actual denied/approved native UI canaries,
and no old process receiving tool execution. Inventory frontend connectors and
dynamic tools separately: moving the shell UID does not revoke frontend account
capabilities. Disable or explicitly scope external privileged tools.
Unresolved concrete gates: approval/provisioning of proposed target/account, installed UI handoff support,
account/SSH/authentication approval, protected k3s selector preflight and host
mode600 approval if targeting that host, authoritative namespace inventory,
actual-chat identity/approval/credential-denial receipts, external-tool scope.
No acceptance status should move to done from these fixture or documentation
results.

View file

@ -0,0 +1,9 @@
a4de5b7169f2cb9ca507d966aaa3f9cdf1f455fbd18f081a30333ceb3309e191 README.md
c98fb8513680fb76e2eb47462690874df6a5212ac5e2cf51362ea220a74268ab TARGET.md
f4fd07b9e35bc2163accaeca921713ff2a3fe439ccc60f550e0e35c30eccb348 observation.json
1dea78b11d589f9270da975a7a7a2a11f51f8b1eba274da1c8d1dd01a9b90b22 probe_identity.py
682b46f1548d9df685273861657aa5c41efae7dc0572edd70933f8d52d65aa6a provisioning-contract.json
c9bdb1a755ad56c5adce4ff2930c3fc627ee36eb226f7315eb9b015189b991fe source-pins.json
9c19707c11f36c72e9539c8536c21f444a39f6236ea8f615d423602267b55940 test_probe_identity.py
ca9512654414b1175087141b2a1793393b8f105e17af42d90d1318e60d526270 validation.json
39af77d96203bacedf7971483978f9baedbe64d75f2f041f66805ae73eca23f0 workstation-metadata.json

View file

@ -0,0 +1,90 @@
# Recommended target and provisioning source contract
Recommendation: workstation `bnt-lap001`, new WSL2 distribution
`custodian-codex`, Linux account `custodian-codex`, private home
`/home/custodian-codex`, project `/home/custodian-codex/work/the-custodian`,
desktop SSH alias `custodian-codex-workstation`. This names the proposed target;
it does not assert the distribution, account, SSH listener, or connection exists.
No infrastructure spend is proposed.
## Why this destination
| Option | Evidence and consequence |
| --- | --- |
| Another UID in current WSL distribution | Only `worsch` UID1000 currently exists. Windows drive is visible, operator `.codex` and `.kube` directories are mode755, and a container socket is visible. Directory permissions alone do not prove secret-file readability. A second UID alone cannot establish the necessary namespace, socket and Windows boundary. |
| Production `railiance01` | Prior metadata proves a running k3s server and admin kubeconfig mode0644; protected selector preflight and mode600 change remain outstanding. Attaching this assignment there adds production proximity and requires hardening/restart approval. |
| New workstation WSL2 distribution | WSL2 substrate is present. A clean filesystem and deliberately limited account avoid production placement and reuse of operator home/state. Windows shares, interop, shared mounts and network-reachable host control planes still require explicit denial proof. This is the recommended preparation target. |
Read-only `wsl.exe --list --verbose` succeeded after explicit filesystem sandbox
escalation; no distribution was started or modified. Inventory: Ubuntu-18.04
(WSL1, stopped), Ubuntu (WSL2, stopped), archlinux (WSL2, stopped), Ubuntu-24.04
(WSL2, running), Debian (WSL2, stopped). None is proven disposable or isolated.
Linux-side `virsh`, QEMU, virt-install and Podman were absent; `/dev/kvm` absent;
Docker and bwrap commands present. Command presence is not authorization or proof
of an isolated long-lived service. Do not reuse any stopped distribution.
The Linux metadata observation is timestamped in workstation-metadata.json.
Windows inventory was obtained on 2026-10-19 during this review. Source-only
contract is provisioning-contract.json; it is intentionally not a deployment
script and refuses readiness until the listed artifact/connection facts exist.
## Exact source-level work
1. Custodian/workstation owner adds a reviewed distribution recipe for the named
guest: pinned Ubuntu 24.04 rootfs SHA, resource limits, private distribution
backing storage owned by the operator, no imported operator filesystem or
private state. Do not export/import the current credential-bearing distro.
This is workstation configuration, outside railiance-infra's currently
declared two-server inventory; do not silently enroll it there.
2. In the guest recipe, disable Windows executable interop and automatic Windows
drive mounting, and prevent access to shared WSL/Docker mounts. Root-owned
boot/service policy must enforce this before agent startup. No Windows
executables, host drive, forwarded agent, Docker daemon socket or privileged
network control-plane endpoint may be reachable by the account. Verify the
effective namespace and network access, not merely configuration values.
3. Create the named account with its own group, password login locked, private
home/state, no sudo/admin/docker/lxd groups or delegation. Provision a clean
checkout and root-owned pinned Codex executable. Install only approved
dependency tools. Its login shell must resolve that executable, not the
operator's NVM installation. No credential helper/global operator Git config.
4. Configure a guest-only SSH service for that user: no root/password login,
agent forwarding, TCP forwarding or X11; transport and repository access
separately scoped. The desktop connects through an approved loopback-only
endpoint; actual free port and Windows/WSL reachability must be verified
before fixing the SSH alias. SSH stdio/session startup is required, so do
not impose a force-command that breaks app-server startup. Pin host identity.
5. Route SSH authentication via warden's catalog, and Codex inference login via
its actual credential owner. Neither is issued by this proposal. Install
account-scoped authentication only after approval; do not copy operator auth.
6. Operator adds the alias to the desktop connection list, saves the matching
repository project, checks native approval UI, and hands off THIS chat.
Record destination machine-ID digest/UID, external process identity and
original/destination chat association. The machine-ID must be fresh for the
new distribution; a duplicated image identity fails host binding.
Verification must include effective absence of `/mnt/c`, Windows executable
interop, shared `/mnt/wsl` control sockets, operator homes, SSH agents and admin
credentials, plus denied access to network-accessible Docker/host control planes.
WSL2 being available does not prove these conditions. Supervisor-owned canaries
can test denial without opening real credentials. The unchanged full assignment
admission requirements in README.md and the original CUST73 packet still apply.
Rollback: stop assignment work, disconnect destination in the desktop, retain
receipt and paused original chat; operator may stop the dedicated guest after
collecting evidence. Never unregister/delete the guest or silently resume tools
under the old operator identity. No production restart is part of this route.
Minimum operator decision, only once this source contract is reviewed: approve
creating `custodian-codex` as a new local WSL2 distribution on `bnt-lap001`, or
identify an already-approved isolated workstation guest to substitute. Rootfs
artifact and SSH port validation are implementation work, not reasons to ask
the user an open-ended target-design question.
## Probe correction
Revision 1 incorrectly rejected UID1000 globally. Revision 2 rejects root and
the originating pair `(4f615ffa89bf23a138468bad4a51ba074a4523f7b2dabd0322dc1458f092f0f6,
1000)`. A distinct host with UID1000 can pass the partial identity check; a new
UID on the same host can also pass. Neither is an admission result. Supervisor
identity must bind machine digest plus UID to the known SSH host identity and
actual chat; machine-ID alone is not a cryptographic host identity.

View file

@ -0,0 +1,46 @@
{
"utc": "2026-10-19T12:34:11.864590+00:00",
"codex": "/home/worsch/.nvm/versions/node/v24.11.1/bin/codex",
"ancestry": [
{
"pid": 3,
"comm": "python3",
"uid": [
"1000",
"1000",
"1000",
"1000"
],
"ppid": 2,
"exe": "/usr/bin/python3.12"
},
{
"pid": 2,
"comm": "bash",
"uid": [
"1000",
"1000",
"1000",
"1000"
],
"ppid": 1,
"exe": "/usr/bin/bash"
},
{
"pid": 1,
"comm": "codex",
"uid": [
"1000",
"1000",
"1000",
"1000"
],
"ppid": 0,
"exe": "/home/worsch/.codex/packages/app-server-daemon/releases/0.163.0-x86_64-unknown-linux-musl/bin/codex"
}
],
"config": {
"mcp_names": [],
"feature_names": []
}
}

View file

@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""Partial read-only identity evidence. Never an admission authority."""
import datetime
import hashlib
import json
import os
from pathlib import Path
import sys
ORIGIN_UID = 1000
ORIGIN_MACHINE = '4f615ffa89bf23a138468bad4a51ba074a4523f7b2dabd0322dc1458f092f0f6'
def evaluate(expected_uid, expected_machine, actual_uid, machine_digest):
return {
"identity_matches": actual_uid == expected_uid
and actual_uid != 0
and (actual_uid, machine_digest) != (ORIGIN_UID, ORIGIN_MACHINE)
and len(expected_machine) == 64
and all(c in '0123456789abcdef' for c in expected_machine)
and expected_machine == machine_digest,
"actual_uid": actual_uid,
"assignment_admitted": False,
"scope": "partial_process_identity_only",
}
def main():
try:
expected_uid = int(sys.argv[1])
expected_machine = sys.argv[2]
machine = hashlib.sha256(Path('/etc/machine-id').read_bytes()).hexdigest()
result = evaluate(expected_uid, expected_machine, os.geteuid(), machine)
result['capture_utc'] = datetime.datetime.now(datetime.timezone.utc).isoformat()
except Exception:
result = {'identity_matches': False, 'assignment_admitted': False,
'reason': 'identity_probe_refused'}
print(json.dumps(result, sort_keys=True))
return 0 if result['identity_matches'] else 1
if __name__ == '__main__':
sys.exit(main())

View file

@ -0,0 +1,49 @@
{
"proposal_id": "CUST-WP-0073-interactive-launch",
"revision": 2,
"status": "source_proposal_not_applied",
"target": {
"workstation": "bnt-lap001",
"substrate": "WSL2",
"distribution": "custodian-codex",
"account": "custodian-codex",
"uid": null,
"home": "/home/custodian-codex",
"project": "/home/custodian-codex/work/the-custodian",
"ssh_alias": "custodian-codex-workstation",
"ssh_endpoint": null,
"rootfs_sha256": null,
"codex_sha256": null,
"host_identity": null
},
"account_policy": {
"password_login": false,
"sudo": false,
"supplementary_admin_groups": [],
"home_mode": "0700",
"codex_state_mode": "0700",
"operator_auth_copy": false
},
"required_effective_denials": [
"Windows executable interop",
"Windows drive mounts",
"shared WSL and container-control sockets",
"operator homes and SSH agent",
"admin kubeconfig and OpenBao credentials",
"network-accessible host and container control planes"
],
"ssh_policy": {
"exposure": "operator-loopback-only",
"root_login": false,
"password_login": false,
"agent_forwarding": false,
"tcp_forwarding": false,
"x11_forwarding": false,
"native_stdio_session": true
},
"attachment": "native-desktop-existing-chat-handoff",
"approval": "native-app-server-client-approval-ui-retained",
"spend_authorized": false,
"live_changes_authorized_by_this_file": false,
"actual_assignment_admitted": false
}

View file

@ -0,0 +1,27 @@
{
"glas-harness": {
"head": "a19bff487d41f18c58a8fd713f6d4e04c3a218d5",
"files": {
"src/glas_harness/gateway.py": "5e998bfa53ade5dafbae799dc04b77031b6414e2af49797f45727d59d7397d42",
"registry/reins/rein-aharness.yaml": "8cdbe7b5ee45e2fb5ca2ed089cab377580034dc261c46bfcf889bddb68cf3da0"
}
},
"sand-boxer": {
"head": "c7c310266aba96ab7dd03cd8bfcaa65f22a3b66e",
"files": {
"src/sandboxer/extensions/bwrap_runner.py": "c83cdd2758bae58cd5d5694f5d6752736f59d479cfc6e9f0f8841c1f5596cba6"
}
},
"railiance-infra": {
"head": "8b7ade9f66fe95d7002b5494d6302763a168827b",
"files": {
"ansible/roles/ssh_ca_host/tasks/main.yml": "c840ccb53d351ca6a1bb8b7a8406efae4451bb651707b03353f5145aa8f21fd3"
}
},
"state-hub": {
"head": "39b51a21a51204675cc46d4b51c00fddd1851449",
"files": {
"scripts/configure-codex.sh": "c6e0e2f47f5ff9e34f7b04a887e3e8e942fcd3eb78c5713be9d1eb4d49091c32"
}
}
}

View file

@ -0,0 +1,30 @@
import unittest
from probe_identity import evaluate, ORIGIN_MACHINE
class IdentityTests(unittest.TestCase):
def test_old_identity_and_root_never_pass(self):
self.assertFalse(evaluate(0, 'a'*64, 0, 'a'*64)['identity_matches'])
self.assertFalse(evaluate(1000, ORIGIN_MACHINE, 1000, ORIGIN_MACHINE)['identity_matches'])
def test_same_uid_distinct_host_is_valid_partial_identity(self):
self.assertTrue(evaluate(1000, 'a'*64, 1000, 'a'*64)['identity_matches'])
def test_separate_uid_same_host_is_valid_partial_identity(self):
self.assertTrue(evaluate(1200, ORIGIN_MACHINE, 1200, ORIGIN_MACHINE)['identity_matches'])
def test_malformed_digest_refused(self):
self.assertFalse(evaluate(1200, 'z'*64, 1200, 'z'*64)['identity_matches'])
def test_wrong_host_or_user_fail(self):
self.assertFalse(evaluate(1200, 'a'*64, 1201, 'a'*64)['identity_matches'])
self.assertFalse(evaluate(1200, 'a'*64, 1200, 'b'*64)['identity_matches'])
def test_matching_child_is_never_assignment_admission(self):
result = evaluate(1200, 'a'*64, 1200, 'a'*64)
self.assertTrue(result['identity_matches'])
self.assertFalse(result['assignment_admitted'])
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,10 @@
{
"revision": 2,
"identity_fixture_tests": 6,
"interactive_first": true,
"live_cutover": false,
"privileged_execution": false,
"read_only_wsl_inventory_escalated": true,
"new_guest_created": false,
"actual_assignment_admitted": false
}

View file

@ -0,0 +1,45 @@
{
"hostname": "bnt-lap001",
"machine_id_sha256": "4f615ffa89bf23a138468bad4a51ba074a4523f7b2dabd0322dc1458f092f0f6",
"kernel_release": "6.18.54.1-microsoft-standard-WSL2",
"accounts": [
{
"name": "worsch",
"uid": 1000,
"home": "/home/worsch"
}
],
"candidate_account_exists": false,
"metadata": {
"/etc/ssh/sshd_config": {
"exists": false
},
"/usr/sbin/sshd": {
"exists": false
},
"/run/docker.sock": {
"uid": 65534,
"gid": 65534,
"mode": "0o660"
},
"/etc/rancher/k3s/k3s.yaml": {
"exists": false
},
"/home/worsch/.kube": {
"uid": 1000,
"gid": 1000,
"mode": "0o755"
},
"/home/worsch/.ssh": {
"uid": 1000,
"gid": 1000,
"mode": "0o700"
},
"/home/worsch/.codex": {
"uid": 1000,
"gid": 1000,
"mode": "0o755"
}
},
"capture_finished_utc": "2026-10-19T12:37:26.268366+00:00"
}