Ctrl K
ComunidadGuías…

docs/host-auth-acceptance.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Host authentication and tool isolation acceptance

Candidate: `codex/native-auth-reuse`, based on Node `be13e3a`. Initial admission implementation
`25c49b2c24449754d428a48bedf15fdd5be1d6bf`, verified on 2026-09-14 in
[PR #36](https://github.com/OpenLinker-ai/openlinker-agent-node/pull/36). This record
does not claim a release, root version update or deployment.
Only Node source changes. Plugin consumes the shared leaf in compatibility tests;
the root repository and Plugin pins are unchanged.

## HOME-lock follow-up

The follow-up in the same PR moves the coordination lock into private host HOME
state. Multiple participating Nodes must share the underlying state inode; different
HOME mounts/values are not coordinated. Native serial setups should use capacity
1; 50ms polling is non-FIFO and bounded by Run timeout. Stop older `/tmp`-lock
Nodes and their clients before upgrade; session scope/history does not change.

Local macOS and isolated Ubuntu arm64 tests exercise the production lock with
HOME aliases, unsafe directories, cancellation and process death. The Linux
`TestHostAuthPermitWithPrivateTmp` uses a real bwrap mount/user namespace with
empty `/tmp` and rebinds the same HOME at a different path. A sentinel confirms
that `/tmp` is private; the child waits until the parent releases the HOME lock.
It is a namespace regression, not a test of a shipped systemd unit (none exists).
Both actual client Run paths additionally try reading a synthetic state canary,
writing this directory and unlinking the lock. Host inode/content checks and
captured tool outputs confirm denial in the tested cases. Credentials remain
synthetic. The CI matrix executes this regression in the Linux native job;
the earlier CI links below document the pre-HOME-lock implementation, not the
follow-up's exact commit. Its final commit/checks are recorded in PR #36.

## Tested boundary

- Official Codex 0.153.0 and Claude Code 2.1.259, locked npm packages installed
  with `npm ci --ignore-scripts`. Current tests use macOS arm64, GitHub Ubuntu
  24.04 amd64 and isolated Ubuntu 24.04.5 arm64 (OrbStack kernel 7.0.14).
- Each client uses a private synthetic authentication home. The initial Run has
  no API-key environment variable; cached authentication reaches a local model
  fixture. A later Run also checks optional credential environment scrubbing.
- Actual model-requested shell tools write the current workspace, attempt to
  read credentials, another conversation, an outside file and a symlink escape,
  inspect parent processes, and attempt offline-network escape. Tests inspect
  returned tool output and host files, not just launch parameters.
- A → B → A resumes exactly A. Claude additionally exercises Read, Write and
  Edit, with a successful in-workspace edit and a rejected outside write.
- macOS uses an explicitly named temporary keychain and synthetic password:
  readable by the outside positive control, not returned by either tool. The
  operator's login keychain is never queried or unlocked.
- Current GitHub Ubuntu testing uses the workflow's per-executable bubblewrap
  userns authorization if the global restriction is enabled, checks that the
  global value remains unchanged, and runs the actual host-auth/client suites.
  It does not use a privileged Docker container or disable global AppArmor.
- The separate Ubuntu arm64 machine has no host mounts or forwarded SSH agent.
  Its OrbStack kernel lacks AppArmor and the Ubuntu userns sysctl; no system
  policy was relaxed. Node 22.23.2 was downloaded from the official distribution
  with SHA-256 verification; bubblewrap is 0.9.0. This is additional Linux
  evidence, not a stock Ubuntu kernel test. An earlier offline Debian container
  relaxed its outer policies and is now only historical evidence.
- Admission tests drive both actual provider Run entrypoints, with two independent
  session roots and a blocked local model request. The second client cannot reach
  the model; canceling it leaves the first running; the next client proceeds after
  completion. Separate compiled-test subprocesses verify cross-process exclusion,
  cancellable waits, crash release, timeout/event errors and unsafe-path rejection.
  Crash release proves the lock primitive only, not orphan-client cleanup.
- Claude defaults to Bash. The opt-in matrix executes all five file tools, file
  and directory symlinks, sandbox-created and host-preseeded hard links, and Linux
  `/proc/self` aliases. Positive controls ensure tools actually work. Host-preseeded
  hard links disclose synthetic contents through Read/Grep and Bash: they remain
  explicitly unsafe inputs. Passing means the documented assertions hold, not
  that every attempted read is denied. Check/open link swaps remain untested.
- Codex host settings deliberately enable `view_image` and a synthetic `notify`
  command in the fixture; native mode still hides that tool and never runs notify.

## Failures found and resolved during this work

1. Claude's global `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB` switch adds broad Linux
   writable roots. Those re-bind paths reopened denied reads in this combination.
   The final Linux path uses ordinary Bash sandbox credential-deny rules and its
   PID/proc isolation. Both cached-auth and environment-key probes pass.
2. Repeated `/bin`/`/usr` aliases in Linux root-deny overlays hid the shell.
   Canonical, deduplicated root masks preserve only the requested code grants.
3. Codex's argv0 helper aliases reside under its auth home's tmp directory.
   The thread override grants validated code-only alias directories and executable
   files, preserving base permissions; no auth-home or whole tmp grant is added.
4. Linux can create a matching pathname in an empty private overlay. The write
   oracle checks the real host directory rather than equating overlay writes with
   a host mutation. Credential contents still must never reach model input/output.

## Results

[GitHub CI 34810002330](https://github.com/OpenLinker-ai/openlinker-agent-node/actions/runs/34810002330)
is successful for implementation `25c49b2`:

| Job/environment | Result |
| --- | --- |
| `test`, Ubuntu 24.04 amd64 | Full tests, race, vet, release build/staging and dependency boundaries passed |
| `native-session-isolation (ubuntu-latest)` | Actual host-auth, admission and file-tool/link tests passed, not skipped |
| `native-session-isolation (macos-latest)` | Same current client tests passed, including temporary synthetic keychain |
| Local macOS arm64 | Full `GOWORK=off go test -race -count=1 ./...`, real-client suite, workspace tests and vet passed |
| Isolated Ubuntu 24.04.5 arm64 | Compiled current adapter tests: host-auth, admission and complete link matrix passed; no race instrumentation in this cross-compiled binary |

The cached-auth suite uses 5 Codex and 13 Claude fixture requests per system;
the admission suite adds independent synthetic model calls. No real model or
subscription is used. The CI script also retains separate SRT regression coverage;
that coverage is not the proof for the current host-auth production path.

Six-target module/package boundary checks pass, including CGO variants. Plugin's
actual `packages/agent-adapters/agent` appfiles consumers pass both candidate
workspace and `GOWORK=off` temporary-local-replacement tests; its host packages
also pass with the local replacement. No dependency declarations were changed.
This is source compatibility evidence, not a Plugin version bump or root pin.

## Remaining limits

No per-session memory, disk, process-count or spending hard quota. Actual model
calls, WebSearch, live account refresh and all keychain/IPC attack paths have not
been accepted. Trust the official binary, OS administrator and approved code
paths; keep experimental access limited to trusted callers. This is not account
sharing authorization or proof against a compromised client. Native serial
admission cannot coordinate external/older/opt-out clients. A hard-killed Node
can leave a client alive after the lock releases; inspect and stop that Node's
remaining clients before restarting. No real token-rotation test or guarantee
of account-wide exclusion is claimed.

Old SRT histories stay untouched and are not imported. Migration starts a new
scope once; later Runs resume that scope. See [setup and migration](native-session-isolation.md).

Relevant primary references: [Claude sandbox configuration](https://code.claude.com/docs/en/sandboxing),
[Claude environment controls](https://code.claude.com/docs/en/env-vars),
[Codex argv0 helpers](https://github.com/openai/codex/blob/rust-v0.153.0/codex-rs/arg0/src/lib.rs).