Repository navigation
[security] - Classify the Keychain CLI and uv in the telemetry egress inventory - #1637
Conversation
… inventory otel-hardening sweep of 2026-10-13 (static checker + mutation self-test green before and after; vendor controls re-verified against the pinned source tags and the installed packages, all unchanged). Two components had slipped past T13's classification sweep, which only knows the binaries named in KNOWN_EXTERNAL_COMPONENTS: - /usr/bin/security (macOS Keychain CLI): sync/scheduled.py started spawning it in PR #1632, after the 2026-09-28 sweep. Category 4 under the local OS process tooling row; aliased and listed so a future removal is caught too. - uv: retired 2026-09-11 with the Dockerfile stage, back since PR #1572 via scripts/refresh-*-lock.sh and the cloud venv driver. New category-5 row: crates/uv-static/src/env_vars.rs defines no telemetry, analytics or update-check variable; self-update is explicit-only and the OSV malware check is opt-in and never reached by `uv pip`. Also refreshes the reviewed dates and evidence on the seven category-1 rows re-read today (chromadb 1.5.9, langsmith 0.13.0 incl. its tracing-mode surface sitting behind tracing_is_enabled(), nemoguardrails 0.24.0, onnxruntime 1.30.0, gh, pwsh) and notes that locks/ hash-pin transformers on the documented install path. SKILL.md records the precedent: sweep spawn sites with an AST walk, not a literal-argv grep. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
|
PR is a complete fill of |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8bd3153cb5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…tory Codex review of PR #1637 (P2): a binary named only in KNOWN_EXTERNAL_COMPONENTS stayed "classified" forever, because T13 unioned the list unconditionally and verified source markers only for DYNAMIC_LAUNCHER_SITES. That is the exact mechanism behind the stale uv row of 2026-09-11 and the day-late security(1) classification. - FIXED_LAUNCHER_SITES: one (component, file, markers) row per spawn site for all 20 fixed binaries, shell and PowerShell files included (brew, two of the uv sites, the Keychain scripts). T13 warns when a marker disappears, like a dynamic site, and FAILS when a known binary has no FIXED/DYNAMIC row and is not a documented FRONTED_SERVICES entry (ollama: a loopback daemon CyClaw probes but never spawns). - verify.sh: two mutations (a site losing its marker -> WARN; a binary losing every site row -> FAIL); the scratch tree now copies every site file straight from the checker's table so they cannot drift. - SKILL.md step 5: the sweep covers the non-Python launch surfaces the AST walk cannot see (macos/*.sh, scripts/*.sh, powershell/*.ps1, the Dockerfile/compose command lines, the skill drivers) and documents the site-row requirement. - uv row: UV_BIN (like PYTHON_BIN) is the operator's path to their own uv install, the same operator-resolved-binary shape as shutil.which(); not modelled as a second launcher. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f7aa63719
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…e names Second Codex round on PR #1637 (three P2s, all confirmed): - The uv site rows matched only the `uv_bin="${UV_BIN:-uv}"` assignment, so deleting the `"$uv_bin" pip compile` call while keeping the override plumbing left T13 green. Both rows now also pin the invocation, and a new verify.sh mutation deletes just the call. - Naming the cloud venv driver as a swept shell surface obliges the sweep to classify every binary it runs: curl was absent. New category-3 row with its five sites (three operator setup/launch scripts, two sandbox drivers): every call is a loopback readiness/smoke probe or the sandbox's agent-proxy status endpoint, except the ollama.com installer pipe in setup-from-clone.sh, which sits behind an interactive confirm that defaults to no. curl itself has no telemetry; no runtime path, the Dockerfile or the PowerShell scripts spawn it. - The reverse-consistency check skipped DYNAMIC_LAUNCHER_SITES, so a dynamic row without a KNOWN_EXTERNAL_COMPONENTS entry verified its marker and never entered the sweep. Dynamic names are now included, and a verify.sh mutation adds an unlisted dynamic row and expects the FAIL. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d1ae1a7356
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…export the kill block there Third Codex round on PR #1637 (three P2s, all confirmed): - A default parameter is not an invocation: `rclone_bin: str = "rclone"` survives the removal of every rclone spawn. Every fixed row now pins the argv literal or shell command line as well (rclone, gh, git, openssl, numbat), and the table's comment states the rule. - macos/setup-from-clone.sh runs `gh auth status` / `gh auth login` directly, with whatever GH_TELEMETRY the operator's shell carries, and the gh row claimed every spawn site forced the canonical env. The script now evals the same `utils.telemetry_kill --export shell` block invoke-cyclaw.sh does, before its gh and ollama steps (non-fatal, same -S -E isolation); the eval line is a site marker so removing it is a WARN (verify.sh scenario 34). agentic/writer.py's gh argv is sited too. - ollama was listed as never spawned; the same setup script starts `ollama serve` and runs `ollama pull`. Moved from FRONTED_SERVICES to FIXED_LAUNCHER_SITES with both lines pinned; the row text says so. FRONTED_SERVICES is empty for now and kept for the next real case. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2e0ccb0fe3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…cations, verify evidence paths Fourth Codex round on PR #1637 (three P2s, all confirmed): - `ollama pull` contacts the model registry; it sat on the category-4 daemon row, which means local-only. The pull is now its own component (`ollama-pull`) and category-3 row: interactive setup only, behind the operator's confirm, tag validated before it reaches argv. The daemon row is back to `ollama serve` alone. - The Keychain helper's security(1) site pinned only the SECURITY_BIN resolver. It now pins the find-generic-password call, and the two other Keychain scripts (set + setup-cyclaw-keys) are sited on their add-/find-generic-password calls too. verify.sh scenario 36 removes the helper's call alone and expects the WARN. - The curl row cited docs/setup-guide.md, which does not exist; the guide is setup-guide.md at the root. T13 now fails any row whose repo-relative url is absent (scenario 35), and the self-test's scratch tree copies those evidence paths from the inventory so the rule can run there. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e0229f935a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…uites' curl Fifth Codex round on PR #1637 (two P2s, both confirmed): - macos/setup-from-clone.sh continued into its gh auth step when the canonical export could not be produced, so an ambient GH_TELEMETRY could still reach gh(1), which cannot self-apply the Python kill block. The script now records whether the export succeeded and skips step 6 when it did not: fail closed for the one telemetry-capable binary it runs, non-fatal for the rest of setup (the servers re-apply at import). The skip guard is a FIXED_LAUNCHER_SITES marker beside the eval line. - CyClaw-Sandbox/smoke.sh and macos-smoke.sh run curl and were not sited, although their sibling verify.sh was. Both are rows now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
The skip guard added in 5081ec5 fell through to the "gh: not on PATH" message when the real reason was a failed telemetry export. Report the actual reason so the operator is not sent to install a gh they have. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
Owner decision on PR #1637: scripts under .claude/ are agent tooling and out of the egress inventory, except the two sandbox surfaces that exercise the gateway itself (cyclaw-gotchas/driver.sh and the CyClaw-Sandbox suite), whose rows stay. SWEPT_AGENT_SURFACES names those two; T13 fails any FIXED_LAUNCHER_SITES row under .claude/ outside them, so the boundary is only ever widened by editing that tuple on purpose. verify.sh scenario 37 adds a row for a session hook and expects the FAIL. SKILL.md step 5 states the decision and why the hooks', skill helpers' and babysit wrappers' gh/git calls are not findings. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 23c7c36e51
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… URL opens, pin the setup Ollama bind Seventh Codex round on PR #1637 (five P2s, all confirmed): - An alias whose target named no inventory row still counted as "classified": the sweep short-circuited on the alias key. The sweep now resolves the target, and a dangling target is a FAIL (verify.sh 38). - launchctl pinned only the which() lookup; sync/scheduler.py's bootout and print argv are sited. - telegram/cli.py generates launchd / Task Scheduler health jobs that run curl and curl.exe; both literals are curl sites and the row says so. - `open <url>` in the four macOS operator scripts is the browser, not the file-manager reveal: new component open-url, category 3 (one remote page, the signed Ollama download, behind a setup prompt; the rest open the loopback console). - The ollama daemon row claimed the setup-started daemon binds loopback unconditionally, but the script preserves the operator's environment and never set OLLAMA_HOST. The spawn now pins OLLAMA_HOST=127.0.0.1:11434 on its own command line (the threat model's Ollama endpoint), the marker pins that, and the row states the daemon-policy caveat for a daemon the operator runs themselves. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d1debd055
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…s, bound every site row, pin all Ollama client calls Eighth Codex round on PR #1637 (four P2s, all confirmed): - The site table was built reactively, one binary at a time, so the "all fixed launcher sites carry their markers" line was a completeness claim over an incomplete table. A sweep of every in-boundary Python, shell and PowerShell file per known binary adds the missing rows: rclone in sync/scheduled.py; git in the executor manifest, the eval dogfood script and both git-hook installers; security in cyclaw-keychain-load.sh and uninstall-cyclaw.sh; openssl in setup-cyclaw-keys.sh and cyclaw-linux-key.sh; launchctl in setup-cyclaw-keys.sh, uninstall-cyclaw.sh and launchd_plist.py's bootout; crontab, schtasks, bwrap and sandbox-exec argv; brew's python install; ollama's list and --version. verify.sh 40 removes one of the new invocations and expects the WARN. - windows-smoke.ps1, inside the swept sandbox surface, makes Invoke-RestMethod / Invoke-WebRequest calls; the curl row had said PowerShell makes none (true of powershell/ and windows/ only). New pwsh-http component and category-3 row, sited; the curl row is corrected. - The agent-tooling boundary checked fixed rows only and matched the single-file exception by prefix. It now covers dynamic rows too and matches a non-directory entry exactly (verify.sh 39 adds both an excluded dynamic row and "driver.sh.backup" and expects both named). - OLLAMA_HOST was pinned on the serve line alone; `ollama list` and the confirmed pull still inherited the ambient value, so a remote OLLAMA_HOST would list and pull against another daemon. Step 7 now exports the loopback endpoint once for every Ollama call it makes; the markers pin the export and each client call. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2f64b059a7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…erShell launchers, pin the Homebrew export Ninth Codex round on PR #1637 (four P2s, all confirmed): - uv's only invocations (`uv pip compile`, `uv pip install`) resolve and download from a package index on a cold cache; the row said as much and still called it category 5. It is category 3 now: the row describes what the invocations do on the network, and the telemetry negative finding stays in the evidence. SECURITY.md's egress classification moves uv accordingly and names the rest of the install-time category-3 surface so the two copies agree. - The installers' `git clone` / `git pull` contradicted the git row's "local-only subcommands" claim. New git-remote component and category-3 row with the three installer sites (two macOS, one PowerShell); the local row now says where network git lives. - powershell/Uninstall-CyClaw.ps1 runs cmd.exe and schtasks.exe, Install-CyClaw.ps1 launches PowerShell children and Invoke-CyClaw.ps1 opens the console with Start-Process: all sited. The earlier sweep's patterns were shell- and Python-shaped and missed PowerShell forms. - `export HOMEBREW_NO_ANALYTICS=1` is the pre-execution protection the Homebrew row claims and was not a marker; it is now, with verify.sh 41 removing it and expecting the WARN. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4e1c8ad819
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…lls, site the gated git push Tenth Codex round on PR #1637 (three P2s, all confirmed): - The repo-relative url rule joined `root / url`, which an absolute url discards and a `..` component escapes. The schema now refuses absolute paths and `..` components outright and requires the resolved target to stay under the root (verify.sh 42 and 43). - pip's hash-pinned installs in macos/install-cyclaw.sh, powershell/Install-CyClaw.ps1, the Dockerfile and the venv driver's fallback, and the confirmed `brew install python@3.12`, are package-index downloads: category-3 pip-install and brew-install components with their sites, consistent with the uv reclassification. The Homebrew analytics row keeps its export and analytics-off markers; the install moves to the new row. SECURITY.md's egress paragraph names them. - RepoWorkspaceTools.push_branch runs `git push --set-upstream origin` when allow_git_write_tools and agentic.enabled are both switched on; the git-remote row now covers installers' clone/pull and that gated runtime push, with the argv literal as its marker, and the local git row says so. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
…-sweep-2026-10-13
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 430a7fc424
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…t the kill block before pip Codex round ten on #1637, all four findings confirmed: - macos/cyclaw-keychain-env.sh and powershell/CyClaw-CredMan-Env.ps1 exec whatever command follows their `--`, so they are dynamic launchers like the pre-action hook and the executor checks. New component `secret-env-wrapper` on the caller-configured row, sited on the export and exec lines of both wrappers; verify.sh scenario 44 removes the macOS exec and expects the dynamic-site WARN. - The swept sandbox surfaces ran pip (CyClaw-Sandbox/verify.sh), git clone (run_full_verification.py) and local git (driver.sh inventory) without site rows. All three are sited, the driver's torch retry is pinned beside its pip fallback, and the pip, git and git-remote rows name them; scenarios 46 and 47 remove the pip install and the clone. - Site row paths and repo-relative evidence urls share one rule (_repo_relative_problem): no absolute path, no '.'/'..' component, no redundant separator, resolved target inside the root. A bad site path is a FAIL before the boundary prefix test or any marker read; scenario 45 adds a `CyClaw-Sandbox/../../hooks/...` row and an absolute one. - Both operator installers eval the canonical telemetry block immediately before their pip calls (shell and PowerShell forms, modelled on the launchers), so setup-from-clone's step 4 no longer runs pip under the ambient PIP_DISABLE_PIP_VERSION_CHECK. Warn-and-continue on failure: a category-2 version check is not worth aborting an install. The pip row says per site where the block reaches pip and where it does not. Checks: strict checker 0/0 (42 rows, 84 components, 70 sites / 28 binaries, 4 dynamic sites), verify.sh ALL PASS (51), doc-sync 0 drift, ruff clean, bash -n, and the three test files that pin the installer scripts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9e01b0f1b7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…tory Codex review of PR #1637 (P2): a binary named only in KNOWN_EXTERNAL_COMPONENTS stayed "classified" forever, because T13 unioned the list unconditionally and verified source markers only for DYNAMIC_LAUNCHER_SITES. That is the exact mechanism behind the stale uv row of 2026-09-11 and the day-late security(1) classification. - FIXED_LAUNCHER_SITES: one (component, file, markers) row per spawn site for all 20 fixed binaries, shell and PowerShell files included (brew, two of the uv sites, the Keychain scripts). T13 warns when a marker disappears, like a dynamic site, and FAILS when a known binary has no FIXED/DYNAMIC row and is not a documented FRONTED_SERVICES entry (ollama: a loopback daemon CyClaw probes but never spawns). - verify.sh: two mutations (a site losing its marker -> WARN; a binary losing every site row -> FAIL); the scratch tree now copies every site file straight from the checker's table so they cannot drift. - SKILL.md step 5: the sweep covers the non-Python launch surfaces the AST walk cannot see (macos/*.sh, scripts/*.sh, powershell/*.ps1, the Dockerfile/compose command lines, the skill drivers) and documents the site-row requirement. - uv row: UV_BIN (like PYTHON_BIN) is the operator's path to their own uv install, the same operator-resolved-binary shape as shutil.which(); not modelled as a second launcher. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
…e names Second Codex round on PR #1637 (three P2s, all confirmed): - The uv site rows matched only the `uv_bin="${UV_BIN:-uv}"` assignment, so deleting the `"$uv_bin" pip compile` call while keeping the override plumbing left T13 green. Both rows now also pin the invocation, and a new verify.sh mutation deletes just the call. - Naming the cloud venv driver as a swept shell surface obliges the sweep to classify every binary it runs: curl was absent. New category-3 row with its five sites (three operator setup/launch scripts, two sandbox drivers): every call is a loopback readiness/smoke probe or the sandbox's agent-proxy status endpoint, except the ollama.com installer pipe in setup-from-clone.sh, which sits behind an interactive confirm that defaults to no. curl itself has no telemetry; no runtime path, the Dockerfile or the PowerShell scripts spawn it. - The reverse-consistency check skipped DYNAMIC_LAUNCHER_SITES, so a dynamic row without a KNOWN_EXTERNAL_COMPONENTS entry verified its marker and never entered the sweep. Dynamic names are now included, and a verify.sh mutation adds an unlisted dynamic row and expects the FAIL. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
…export the kill block there Third Codex round on PR #1637 (three P2s, all confirmed): - A default parameter is not an invocation: `rclone_bin: str = "rclone"` survives the removal of every rclone spawn. Every fixed row now pins the argv literal or shell command line as well (rclone, gh, git, openssl, numbat), and the table's comment states the rule. - macos/setup-from-clone.sh runs `gh auth status` / `gh auth login` directly, with whatever GH_TELEMETRY the operator's shell carries, and the gh row claimed every spawn site forced the canonical env. The script now evals the same `utils.telemetry_kill --export shell` block invoke-cyclaw.sh does, before its gh and ollama steps (non-fatal, same -S -E isolation); the eval line is a site marker so removing it is a WARN (verify.sh scenario 34). agentic/writer.py's gh argv is sited too. - ollama was listed as never spawned; the same setup script starts `ollama serve` and runs `ollama pull`. Moved from FRONTED_SERVICES to FIXED_LAUNCHER_SITES with both lines pinned; the row text says so. FRONTED_SERVICES is empty for now and kept for the next real case. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
…cations, verify evidence paths Fourth Codex round on PR #1637 (three P2s, all confirmed): - `ollama pull` contacts the model registry; it sat on the category-4 daemon row, which means local-only. The pull is now its own component (`ollama-pull`) and category-3 row: interactive setup only, behind the operator's confirm, tag validated before it reaches argv. The daemon row is back to `ollama serve` alone. - The Keychain helper's security(1) site pinned only the SECURITY_BIN resolver. It now pins the find-generic-password call, and the two other Keychain scripts (set + setup-cyclaw-keys) are sited on their add-/find-generic-password calls too. verify.sh scenario 36 removes the helper's call alone and expects the WARN. - The curl row cited docs/setup-guide.md, which does not exist; the guide is setup-guide.md at the root. T13 now fails any row whose repo-relative url is absent (scenario 35), and the self-test's scratch tree copies those evidence paths from the inventory so the rule can run there. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
…uites' curl Fifth Codex round on PR #1637 (two P2s, both confirmed): - macos/setup-from-clone.sh continued into its gh auth step when the canonical export could not be produced, so an ambient GH_TELEMETRY could still reach gh(1), which cannot self-apply the Python kill block. The script now records whether the export succeeded and skips step 6 when it did not: fail closed for the one telemetry-capable binary it runs, non-fatal for the rest of setup (the servers re-apply at import). The skip guard is a FIXED_LAUNCHER_SITES marker beside the eval line. - CyClaw-Sandbox/smoke.sh and macos-smoke.sh run curl and were not sited, although their sibling verify.sh was. Both are rows now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
Owner decision on PR #1637: scripts under .claude/ are agent tooling and out of the egress inventory, except the two sandbox surfaces that exercise the gateway itself (cyclaw-gotchas/driver.sh and the CyClaw-Sandbox suite), whose rows stay. SWEPT_AGENT_SURFACES names those two; T13 fails any FIXED_LAUNCHER_SITES row under .claude/ outside them, so the boundary is only ever widened by editing that tuple on purpose. verify.sh scenario 37 adds a row for a session hook and expects the FAIL. SKILL.md step 5 states the decision and why the hooks', skill helpers' and babysit wrappers' gh/git calls are not findings. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
… URL opens, pin the setup Ollama bind Seventh Codex round on PR #1637 (five P2s, all confirmed): - An alias whose target named no inventory row still counted as "classified": the sweep short-circuited on the alias key. The sweep now resolves the target, and a dangling target is a FAIL (verify.sh 38). - launchctl pinned only the which() lookup; sync/scheduler.py's bootout and print argv are sited. - telegram/cli.py generates launchd / Task Scheduler health jobs that run curl and curl.exe; both literals are curl sites and the row says so. - `open <url>` in the four macOS operator scripts is the browser, not the file-manager reveal: new component open-url, category 3 (one remote page, the signed Ollama download, behind a setup prompt; the rest open the loopback console). - The ollama daemon row claimed the setup-started daemon binds loopback unconditionally, but the script preserves the operator's environment and never set OLLAMA_HOST. The spawn now pins OLLAMA_HOST=127.0.0.1:11434 on its own command line (the threat model's Ollama endpoint), the marker pins that, and the row states the daemon-policy caveat for a daemon the operator runs themselves. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
…s, bound every site row, pin all Ollama client calls Eighth Codex round on PR #1637 (four P2s, all confirmed): - The site table was built reactively, one binary at a time, so the "all fixed launcher sites carry their markers" line was a completeness claim over an incomplete table. A sweep of every in-boundary Python, shell and PowerShell file per known binary adds the missing rows: rclone in sync/scheduled.py; git in the executor manifest, the eval dogfood script and both git-hook installers; security in cyclaw-keychain-load.sh and uninstall-cyclaw.sh; openssl in setup-cyclaw-keys.sh and cyclaw-linux-key.sh; launchctl in setup-cyclaw-keys.sh, uninstall-cyclaw.sh and launchd_plist.py's bootout; crontab, schtasks, bwrap and sandbox-exec argv; brew's python install; ollama's list and --version. verify.sh 40 removes one of the new invocations and expects the WARN. - windows-smoke.ps1, inside the swept sandbox surface, makes Invoke-RestMethod / Invoke-WebRequest calls; the curl row had said PowerShell makes none (true of powershell/ and windows/ only). New pwsh-http component and category-3 row, sited; the curl row is corrected. - The agent-tooling boundary checked fixed rows only and matched the single-file exception by prefix. It now covers dynamic rows too and matches a non-directory entry exactly (verify.sh 39 adds both an excluded dynamic row and "driver.sh.backup" and expects both named). - OLLAMA_HOST was pinned on the serve line alone; `ollama list` and the confirmed pull still inherited the ambient value, so a remote OLLAMA_HOST would list and pull against another daemon. Step 7 now exports the loopback endpoint once for every Ollama call it makes; the markers pin the export and each client call. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
…erShell launchers, pin the Homebrew export Ninth Codex round on PR #1637 (four P2s, all confirmed): - uv's only invocations (`uv pip compile`, `uv pip install`) resolve and download from a package index on a cold cache; the row said as much and still called it category 5. It is category 3 now: the row describes what the invocations do on the network, and the telemetry negative finding stays in the evidence. SECURITY.md's egress classification moves uv accordingly and names the rest of the install-time category-3 surface so the two copies agree. - The installers' `git clone` / `git pull` contradicted the git row's "local-only subcommands" claim. New git-remote component and category-3 row with the three installer sites (two macOS, one PowerShell); the local row now says where network git lives. - powershell/Uninstall-CyClaw.ps1 runs cmd.exe and schtasks.exe, Install-CyClaw.ps1 launches PowerShell children and Invoke-CyClaw.ps1 opens the console with Start-Process: all sited. The earlier sweep's patterns were shell- and Python-shaped and missed PowerShell forms. - `export HOMEBREW_NO_ANALYTICS=1` is the pre-execution protection the Homebrew row claims and was not a marker; it is now, with verify.sh 41 removing it and expecting the WARN. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
…lls, site the gated git push Tenth Codex round on PR #1637 (three P2s, all confirmed): - The repo-relative url rule joined `root / url`, which an absolute url discards and a `..` component escapes. The schema now refuses absolute paths and `..` components outright and requires the resolved target to stay under the root (verify.sh 42 and 43). - pip's hash-pinned installs in macos/install-cyclaw.sh, powershell/Install-CyClaw.ps1, the Dockerfile and the venv driver's fallback, and the confirmed `brew install python@3.12`, are package-index downloads: category-3 pip-install and brew-install components with their sites, consistent with the uv reclassification. The Homebrew analytics row keeps its export and analytics-off markers; the install moves to the new row. SECURITY.md's egress paragraph names them. - RepoWorkspaceTools.push_branch runs `git push --set-upstream origin` when allow_git_write_tools and agentic.enabled are both switched on; the git-remote row now covers installers' clone/pull and that gated runtime push, with the argv literal as its marker, and the local git row says so. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
…-13) High-signal since 3fe8217 (#1619): #1637 telemetry/egress inventory completion (fail-closed kill block, full spawn-site table for PowerShell/pip/brew/git-remote cat-3, agent-tooling boundary, OLLAMA_HOST pin, secret-env wrappers); #1643/#1645 docs resyncs correcting I6/harness and checker skill drifts. Graph remains 12 nodes.
…d keyring helpers, drop the sandbox punkt fetch (#1646) Codex round eleven on #1637, all four findings confirmed, plus what a token-level sweep of every in-boundary shell surface turned up beside them: - gh's GitHub traffic is functional egress, not only suppressed telemetry: new category-3 component `gh-api` sited on the setup script's `gh auth status` / `gh auth login --with-token` and the agentic `gh pr view` / `gh pr create` argv, beside the category-1 telemetry row (the git / git-remote precedent). SECURITY.md's category-3 sentence names it. Scenario 48 removes both auth-login branches and expects the WARN. - The generated Task Scheduler rows pinned a filename and a which(): cmd.exe now pins the COMSPEC command construction and the `/c <launcher>` argument, powershell the executable resolver and the `-File wrapper` argv. COMSPEC is a path override of the same interpreter (the UV_BIN shape), so it stays a fixed component. Scenario 49 swaps the COMSPEC construction and expects the WARN. - macOS setup/uninstall helpers and the Linux keyring CLI had no rows: expect, osascript, pbcopy, pbpaste, lsof, sw_vers and secret-tool join the local OS tooling row with invocation sites; CYCLAW_SECRET_TOOL is a path override, not a caller-chosen command. The same sweep found the Dockerfile's apt-get (new category-3 row `apt-install`) and its groupadd/useradd (local tooling). Scenarios 50 and 51 remove the expect invocation and the secret-tool lookup. - The sandbox audit's `nltk.download('punkt')` step was obsolete (the stemmer uses PorterStemmer only, no punkt, per SECURITY.md) and would have reached the NLTK data host on a cold cache: removed from .claude/skills/CyClaw-Sandbox/verify.sh; the core-libs row says so. - SKILL.md step 5 records the token-level sweep method and the path-override rule so the next round starts from a complete list. Checks: strict checker 0/0 (44 rows, 95 components, 89 sites / 39 binaries, 4 dynamic sites), verify.sh ALL PASS (55), doc-sync 0 drift, ruff clean, bash -n on both verify scripts. Claude-Session: https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3 Co-authored-by: Claude <noreply@anthropic.com>
Proposed changes
Output of a full
/otel-hardeningrun on 2026-10-13 againstmain @ 6519135. The static checker and its mutation self-test were green before any edit; the live vendor sweep re-verified every category-1 control at the pinned versions; the only real findings were two components the classification inventory (T13) had never heard of, plus stale evidence text. The first commit closes those. The later commits (1f7aa63,d1ae1a7,2e0ccb0,e0229f9,5081ec5,d791d62,3d1debd,2f64b05,4e1c8ad,37aa395,9e01b0ffrom ten Codex review rounds, and23c7c36from the owner's boundary decision) close the mechanism that let them slip: every fixed binary now has verified spawn sites pinned to their invocations across a complete per-binary sweep of Python, shell and PowerShell, the sweep is consistent in both directions including alias targets, evidence paths must exist inside the repository, every install-time network operation is classified as the functional egress it is, the one shell surface that ran a telemetry-capable binary without the canonical block now exports it and fails closed when it cannot, both operator installers export the block before pip runs, every Ollama call the setup script makes is pinned to loopback, the secret-store exec wrappers are modelled as the dynamic launchers they are, site paths cannot escape the repository, and the inventory's boundary with agent tooling is stated and enforced over fixed and dynamic rows.430a7fcmerges main (docs-only commits) so the push hook's fresh-main ancestry rule is satisfied without rewriting history;9e01b0fsits on top of it.What T13 missed and why. T13 discovers Python dependencies from the four manifests on its own, but an external binary is only swept if it is named in
KNOWN_EXTERNAL_COMPONENTS. Two binaries entered the repo after the 2026-09-28 sweep without being named there, so the checker reported "all declared components classified" while being blind to both:/usr/bin/security(macOS Keychain CLI).sync/scheduled.pystarted spawning it in [security] - Retrieve scheduled rclone credentials at execution time #1632 (2026-10-12) to read the scheduled rclone credential at execution time. Local Keychain read, fixed absolute path, runs underchild_environment("filesystem"), no network sink. Classified category 4 under the existing "local OS process tooling" row; added toKNOWN_EXTERNAL_COMPONENTSandINVENTORY_ALIASESso a future removal is caught too. Themacos/cyclaw-keychain-*.shscripts already used the same binary; the row now names them.uv. Retired from the checker 2026-09-11 when the Dockerfile's uv stage was dropped (the comment literally said "the repo spawns uv nowhere; re-add the row if uv ever returns"). It returned in [infra] - Build the cloud test venv in the background at session start, with uv #1572 (2026-10-07) viascripts/refresh-runtime-lock.sh,scripts/refresh-ci-locks.sh, and.claude/skills/cyclaw-gotchas/driver.sh. New row:crates/uv-static/src/env_vars.rsat current main defines no telemetry, analytics, tracking, or update-check variable; self-update is explicit-only (uv self update); the post-uv syncOSV malware check is opt-in (UV_MALWARE_CHECK=1) and never reached byuv pip. No control was invented for it, per the skill's rule for categories 3–5. (Filed as category 5 at first; moved to category 3 in the eleventh commit because its only invocations are index access, withSECURITY.mdupdated to match.)Second commit: spawn-site verification for fixed binaries (Codex round 1, 1f7aa63). Until now a name in
KNOWN_EXTERNAL_COMPONENTSwas unioned into the sweep unconditionally; onlyDYNAMIC_LAUNCHER_SITEShad its source markers checked. So a retired binary stayed "classified" forever, which is exactly how the uv row went stale in September. Now:FIXED_LAUNCHER_SITESholds one(component, file, markers)row per spawn site for every fixed binary (shell and PowerShell files are first-class sites, since a marker is a text match). T13 warns when a marker disappears, as for dynamic sites.FRONTED_SERVICESentry is a hard FAIL.verify.shgains mutations for both rules, and_mktreenow copies the site files straight from the checker's table viaast.literal_eval, so the scratch tree and the rule cannot drift.Third commit: Codex round 2 (d1ae1a7), all three findings confirmed.
uv_bin=assignment; they now also pin the"$uv_bin" pip compileinvocation, with a mutation that deletes just the call.curlwas missing. New category-3 row with its sites (three operator setup/launch scripts, the sandbox driver, the three CyClaw-Sandbox smoke/verify scripts, and the launchd/Task Scheduler health jobstelegram/cli.pygenerates): every call is a loopback readiness/smoke probe or the sandbox's agent-proxy status endpoint, except theollama.com/install.shpipe insetup-from-clone.sh, which sits behind an interactive confirm that defaults to no.DYNAMIC_LAUNCHER_SITES; dynamic names are now included, with a mutation that adds an unlisted dynamic row and expects the FAIL.Fourth commit: Codex round 3 (2e0ccb0), all three findings confirmed. This one touches a file outside the skill directory.
rclone_bin: str = "rclone"survives the removal of every rclone spawn. Every row whose marker was only a name declaration now also pins an argv literal or shell command line; the table comment and SKILL.md state the rule.macos/setup-from-clone.shrunsgh auth status/gh auth logindirectly, with whateverGH_TELEMETRYthe operator's shell carries, while the gh row claimed every spawn site forced the canonical env. The script now evals the sameutils.telemetry_kill --export shellblockinvoke-cyclaw.shdoes, after the key/.envloading and before its gh and ollama steps, same-S -Eisolation. Values are rendered by the canonical module, never hand-copied. The eval line is a site marker andverify.shscenario 34 removes it and expects the WARN.agentic/writer.py's gh argv is sited too.ollamawas inFRONTED_SERVICESas never spawned; the same setup script startsollama serve. Moved toFIXED_LAUNCHER_SITES.FRONTED_SERVICESis empty for now and kept for the next real case.Fifth commit: Codex round 4 (e0229f9), all three findings confirmed.
ollama pullcontacts the model registry and had landed on the category-4 (local-only) daemon row. It is now its own componentollama-pullwith a category-3 row: interactive setup step 7 only, behind the operator's confirm, tag validated before argv. The daemon row is back toollama servealone.securitysite pinned only the resolver; it now pins thefind-generic-passwordcall, and the other Keychain scripts are sited on their calls too, with a mutation that removes the helper's call alone.docs/setup-guide.md, which does not exist (the guide issetup-guide.mdat the root). Fixed, and T13 now fails any row whose repo-relativeurlis absent; the scratch tree copies those evidence paths from the inventory so the rule runs in the self-test too.Sixth and seventh commits: Codex round 5 (5081ec5, d791d62), both findings confirmed.
GH_TELEMETRYcould still reach gh, which cannot self-apply the Python kill. It now fails closed: the script records whether the export succeeded and skips step 6 when it did not; the rest of setup continues (Python entry points re-apply at import, ollama has no vendor switch to lose). The skip guard is a site marker beside the eval line. A hand-copiedGH_TELEMETRY=falsefallback was rejected on purpose: a second copy of a kill value outside the oracle is the drift this checker exists to catch.d791d62makes the skip message name the real reason instead of falling through to "gh: not on PATH".CyClaw-Sandbox/smoke.shandmacos-smoke.shrun curl and were not sited although their siblingverify.shwas. Both are rows now.Eighth commit: the agent-tooling boundary (owner decision, 23c7c36). A full sweep of
.claude/**/*.shhad shownghrun by thebabysit-github-prhelper scripts andgitby the session hooks and skill bootstraps, none exporting the kill block. The owner's decision: agent tooling under.claude/is out of the inventory; those scripts run on the agent's own machine or sandbox, outside the gateway's process tree, and their gh/git calls are not findings. The two exceptions stay because they exercise the gateway itself:cyclaw-gotchas/driver.shand theCyClaw-Sandbox/suite, now named inSWEPT_AGENT_SURFACES. T13 fails any site row under.claude/outside those, so the boundary is only ever widened by editing that tuple on purpose;verify.shscenario 37 adds a row for a session hook and expects the FAIL. SKILL.md step 5 states the decision.Ninth commit: Codex round 6 (3d1debd), all five findings confirmed.
verify.shscenario 38).which()lookup;sync/scheduler.py'sbootoutandprintargv are sited.telegram/cli.pygenerates launchd / Task Scheduler health jobs that runcurlandcurl.exe; both literals are curl sites.open <url>in the macOS operator scripts is the browser, not the file-manager reveal: new componentopen-url, category 3 (one remote page, the signed Ollama download, behind a setup prompt; the rest open the loopback console).OLLAMA_HOST. Pinned to loopback (widened to the whole step in the tenth commit).Tenth commit: Codex round 7 (2f64b05), all four findings confirmed.
verify.shscenario 40 removes one of the new invocations and expects the WARN.windows-smoke.ps1, inside the swept sandbox surface, makesInvoke-RestMethod/Invoke-WebRequestcalls; the curl row had said PowerShell makes none. New componentpwsh-httpwith a category-3 row, sited; the curl row is corrected.verify.shscenario 39 adds both offending shapes and requires both named in the FAIL.OLLAMA_HOSThad been pinned on theserveline alone;ollama listand the confirmed pull still inherited the ambient value. Step 7 now exports the loopback endpoint once for every Ollama call it makes; the markers pin the export and each client call.Eleventh commit: Codex round 8 (4e1c8ad), all four findings confirmed. This one also touches
SECURITY.md.uv pip compile,uv pip install) resolve and download from an index on a cold cache. The row now describes what the invocations do on the network and keeps the telemetry negative finding in its evidence.SECURITY.md's egress classification is the other copy of this list: uv moves out of its category-5 sentence and the install-time category-3 surface is named there so the checker and the document agree.git clone/git pullcontradicted the git row's "local-only subcommands" claim. New componentgit-remotewith a category-3 row and three installer sites (two macOS, one PowerShell); the local git row now says where network git lives.Uninstall-CyClaw.ps1(cmd.exe and schtasks.exe),Install-CyClaw.ps1(PowerShell children) andInvoke-CyClaw.ps1(Start-Processon the console URL, joining open-url).export HOMEBREW_NO_ANALYTICS=1is the pre-execution protection the Homebrew row claims and was not a marker; it is now, withverify.shscenario 41 removing it and expecting the WARN.Twelfth commit: Codex round 9 (37aa395), all three findings confirmed.
root / url, which an absolute url discards and a..component escapes. The schema now refuses absolute paths and..components outright and requires the resolved target (symlinks followed) to stay under the root;verify.shscenarios 42 and 43 cover both shapes.macos/install-cyclaw.sh,powershell/Install-CyClaw.ps1, the Dockerfile and the venv driver's fallback, and the confirmedbrew install python@3.12, are package-index downloads: new category-3 componentspip-installandbrew-installwith their sites, consistent with the uv reclassification. The Homebrew analytics row keeps only its export andanalytics offmarkers.SECURITY.md's paragraph names both.RepoWorkspaceTools.push_branchrunsgit push --set-upstream originwhenallow_git_write_toolsandagentic.enabledare both switched on (both ship false). Thegit-remoterow is broadened to "git remote operations (installers + gated push)" with that argv as a marker, and the local git row says so.Thirteenth commit: Codex round 10 (9e01b0f), all four findings confirmed. This one touches both operator installers.
macos/cyclaw-keychain-env.sh(exec "$@") andpowershell/CyClaw-CredMan-Env.ps1(& $exe @exeArgs) run whatever command follows their--, so they are dynamic launchers like the pre-action hook and the executor checks. New componentsecret-env-wrapperon the caller-configured row, sited on the secret export and the exec line of each; the row records who supplies the argv (the launchd and Task Scheduler generators, or the operator).verify.shscenario 44 removes the macOS exec and expects the WARN.CyClaw-Sandbox/verify.sh),git clone(run_full_verification.py) and local git (driver.sh's inventory) without site rows. All three are sited and the pip, git and git-remote rows name them; scenarios 46 and 47 remove the pip install and the clone../..component, no redundant separator, resolved target inside the root. A bad site path is a FAIL before the boundary prefix test or any marker read, soCyClaw-Sandbox/../../hooks/xcannot smuggle a hook in; scenario 45 adds that row and an absolute one.install-cyclaw.shin shell form,Install-CyClaw.ps1with the same two rigid line shapesInvoke-CyClaw.ps1parses), so setup-from-clone's step 4 no longer runs pip under the ambientPIP_DISABLE_PIP_VERSION_CHECK. Warn-and-continue on failure: pip carries no telemetry and its version check is a category-2 update check, not worth aborting an install. The pip row says per site where the block reaches pip and where it does not (the Dockerfile build stage, the sandboxverify.sh, the venv driver), and the installers' export lines are site markers.Declined, owner-confirmed (round 1): treat an operator's
UV_BINpath override as a dynamic launcher. It is the same operator-resolved-binary shape as everyshutil.which()site, none of which are modelled that way; the uv row's text says so and the site rows pin both scripts'uv_bin=lines. The owner agreed and the thread is resolved.Vendor sweep results (all unchanged, dates and evidence refreshed on the rows):
docs/Privacy.mdat main + installed wheelORT_DISABLE_TELEMETRY=1pre-init still the documented process-lifetime control for all 1DS builds;disable_telemetry_eventsstill callabletelemetry.pyat v0.24.0 tag + installedconstants.pyat v1.32.0 tagtelemetry/opentelemetry/__init__.py+config.pyat 1.5.9 tagotel_initearly-returns on granularitynone; Settings is pydantic-settings with noenv_prefixandenv_file=".env", andos.environoutranks the dotenv, so the pre-import overlay still winsclient.py,run_helpers.py,run_trees.py,langchain_core/tracers/context.pyLANGSMITH_TRACING_MODE/ legacyOTEL_ENABLED/OTEL_ONLY. Traced it: these only pick a destination for runs thattracing_is_enabled()already admits, so no new kill pair is owed and none was added (speculative variables are the advertise-false-protection anti-pattern).LANGSMITH_RUNS_ENDPOINTSis now parsed as write replicas; still a fan-out destination, still scrubbedpkg/cmd/root/help_topic.goat trunkGH_TELEMETRY=falsestill disables and takes precedence overDO_NOT_TRACK, which the map also setsabout_TelemetryAlso corrected the sentence-transformers row's
versionstext:transformersis still unbounded on the wheel surface, but the new hashed locks inlocks/pin it at 5.18.0 on the documented install path, which is why T13's standing INFO finding stays accurate per-surface.Invariant / Governance Impact: none of the six invariants or I6 are touched. The diff is the checker's inventory data, its self-test, the skill text, one paragraph of
SECURITY.md's egress classification (kept in step with the checker's categories), inmacos/setup-from-clone.shone additive export block, a fail-closed skip guard, and a step-wide loopback export for the Ollama step, and inmacos/install-cyclaw.shandpowershell/Install-CyClaw.ps1one additive export block each before their pip calls (operator setup scripts; not core-six files, not launchers the server depends on). No production map (utils/telemetry_kill.py), no kill value, no scrub name changed;CONTRACT_DIGESTis unaffected.check_invariants.pywas not needed because no core file moved.Agent-instruction surface (the pre-commit security gate flagged this, correctly): the skill files steer later agent sessions. Every evidence string is my own paraphrase of what I read at the cited source; the two short phrases I had initially quoted verbatim from vendor docs were rewritten before push so no web text is copied into an instruction file. The gate's secure-by-design question about a "new config input" at the uv row does not apply: the row has empty
controls, so nothing new is read at runtime. On the second commit the gate asked about a new code-execution or deserialization path: the only new evaluation isast.literal_evalover the checker's own tables insideverify.sh's scratch-tree builder, reading a file from this repo, never model output, retrieved text or a request field; it runs only in the self-test. The setup script'seval "$_kill_env"evaluates only the output of the repo's own stdlib module, run with-S -E, exactly asinvoke-cyclaw.shalready does.Types of changes
Scope note: Infrastructure / CI (the
verify-skillschecker's egress inventory and its self-test) plus an additive export block, a fail-closed guard and a step-wide loopback export in the macOS setup script, an additive export block before pip in each operator installer, and one paragraph ofSECURITY.md; no server runtime path.Benefits / why
securitymiss does not recur: AST-walk Python, then grep the shell, PowerShell, container and driver surfaces the AST cannot see with patterns shaped for each language, pin invocations rather than declarations, sweep per binary rather than per finding, and split a binary into two rows when its calls differ in kind.Risks to monitor
utils/,gate*.py,graph.py, or the launchers the server depends on changed. A regression in the checker would be a reporting regression, not a telemetry leak.gh authis now skipped with a warning naming the reason, where before it ran ambient; setup still completes and the operator runsgh auth loginby hand later. (2) Step 7 exportsOLLAMA_HOST=127.0.0.1:11434for every Ollama call it makes and for the convenience daemon it may start; an operator who wants a LAN-bound or remote daemon runs it themselves, as before, and the export persists for the rest of the script run (the servers readconfig.yaml'sbase_url, not this variable). Both are macOS-only and not executed here (Linux sandbox); CI'sshellcheck (macos scripts)leg passed on the head that introduced the block.FIXED_LAUNCHER_SITESmarker is a literal substring; an innocent refactor (renamingrclone_bin, reflowing an argv list, acurlline, apip installline or a PowerShell launch line) trips a T13 WARN that must be re-pointed. With 70 fixed sites and 4 dynamic ones that surface is wider than before. That is the intended cost: the WARN names the row and the fix. Signal: averify-skills (otel-hardening)leg going yellow on an unrelated PR. The evidence-path rule has the same shape: moving a cited doc fails the schema until the row is re-pointed.revieweddate crossing T4's 120-day window, and the/otel-hardeningstep-3 vendor table, which should gain a uv line at the next run._confirmor config gate would not.securityrow scope creep. The row documents reads, the setup-time writes and the uninstall-time delete the Keychain scripts make; the site markers pin each call, so a new kind of call needs a new marker.babysit-github-prscripts do runghwithout the kill block, on the agent's machine. If that ever matters (an operator running them on the production host), the fix is to widenSWEPT_AGENT_SURFACESand add the export eval to those scripts, deliberately.verify-skillsiscontinue-on-errorinci.yml, so a redcheck_otel.pyleg would not block a merge. That is pre-existing and documented in the skill's gotchas; I am noting it because it means the green I report below is the only gate this PR actually has.Checklist
docs/THREAT_MODEL.md,INVARIANTS.mdandSECURITY.md(and any relevant Phase docs)GROK_API_KEY=dummy pytest tests/ -q --tb=short, andbash .claude/skills/CyClaw-Sandbox/verify.shfor core RAG/agentic paths) and passes with no regressions — deliberately not run, per CLAUDE.md §5's verification policy; CI runs the suites. The checks that do exercise this diff were run directly and are listed under Further comments.SECURITY.md's egress classification updatedFurther comments
Checks run directly on the pushed head
9e01b0f(all green):python3 .claude/skills/otel-hardening/check_otel.py --strict --as-of 2026-10-13→ 0 failures, 0 warnings; 42 inventory rows, 84 declared components resolve, 70 fixed launcher sites (28 binaries) and 4 dynamic sites carry their invocation markers, every site path and repo-relative evidence path is a clean path inside the repository, every alias target names a row, every.claude/site of either kind is inside the boundary.bash .claude/skills/otel-hardening/verify.sh→ ALL PASS (51 mutation scenarios, eighteen new across the review rounds and the boundary decision).python3 .claude/skills/doc-sync/doc_sync.py→ 0 drift items, re-run on the merged tree.bash -nonverify.sh,macos/setup-from-clone.shandmacos/install-cyclaw.sh;ruff checkon the checker clean;bash scripts/check-pr-template.shon this body → OK.tests/test_installer_python_contract.py,tests/test_telemetry_env_delivery.pyandtests/test_powershell_windows_parity.py(the files that pin the installer scripts' text and runinstall-cyclaw.shunder a fake Python) → green; the fake Python rejects-S -E -m utils.telemetry_kill, so they exercise the new warn-and-continue path.subprocess.run/Popen/check_*/callandcreate_subprocess_execsite, each non-literal first argv element resolved by reading the call site; then a per-binary grep of every in-boundary Python and shell file for invocations; then a PowerShell-aware pass overpowershell/andwindows/(& cmd,cmd.exe /c,Start-Process,.exeforms); and a grep of every launch surface forcurl/wget/Invoke-WebRequest/Invoke-RestMethod, for git's network subcommands and forpip install.Only CI has run: the pytest suites and the 80% coverage gate (this diff adds no Python under coverage sources),
shellcheckon the macOS scripts (not installed in the sandbox), and any PowerShell parse ofInstall-CyClaw.ps1(nopwshhere; the new block is the launcher's proven block with the identical regex lines, and the brace count balances).Not run, and why: the four telemetry pytest files the skill's step 4 names. They pin
utils/telemetry_kill.py's maps, the Dockerfile/compose delivery, and the ONNX seam, none of which this PR touches; running them would have exercised nothing in the diff. The setup script itself was not executed: it is macOS-only and interactive.Network note for the sweep itself: the sandbox proxy denies
docs.trychroma.com,cli.github.com,learn.microsoft.com, anddocs.astral.sh, so the vendor re-reads above were done against the pinned source tags onraw.githubusercontent.comand the installed packages in the cloud venv (which carry exactly the pinned versions). That is a stronger oracle than the marketing docs anyway, but it means the row URLs that point at doc sites were not themselves fetched this round.Review threads: all thirty-four Codex threads are resolved: thirty-three fixed, one (
UV_BIN) declined with reasoning and confirmed by the owner.Body layout note:
pr-template-check.ymlrequires theLast updatedstamp to be the body's last non-blank line, so the Claude Code attribution block sits just above it rather than after it.Suggested merge order of open PRs
Trial merge verified (
git merge --no-commit --no-ff+ abort) against main @ e61481d on 2026-10-13, then main merged into the branch (430a7fc) to satisfy the push hook's fresh-main rule;9e01b0fsits on that merge and main has not moved since.#1637 (this PR) · Safe ✅
Impact: Checker inventory, its self-test, skill text, one
SECURITY.mdparagraph, an additive block, a fail-closed guard and a loopback export in the macOS setup script, and an additive export block in each operator installer; no server runtime path. Independent, only open PR.Action: Merge when CI on
9e01b0fis green; auto-merge (rebase) is already enabled by the owner.ELI5
Think of the telemetry checker as a bouncer with a guest list. Every program CyClaw can start has to be on the list with a note saying whether it phones home. Two programs got in without being on the list:
security, the Mac keychain tool thatsync/scheduled.pystarted using last week to fetch the rclone password, anduv, the fast pip replacement that came back in the lock-refresh scripts after the checker had been told it was gone. Neither one phones home, but the bouncer couldn't tell you that, because he'd never looked at them.The first commit adds both to the list with the evidence (where they are started, what they do, why they don't need a telemetry switch), and re-dates seven existing entries after re-reading each vendor's actual source at the version we pin. One vendor, langsmith, grew a new set of knobs since the last read; I traced them and they only matter if tracing is already on, which ours never is, so I deliberately did not add fake "protection" variables for them.
The second commit fixes the deeper problem the Codex review pointed at: the guest list was never compared with who actually shows up. A program could leave the codebase and stay on the list forever, which is exactly what happened with
uvin September. Now every listed program has to name the file that launches it and a line of text that only exists while that file still does; if the line disappears, the checker complains, and if a listed program has no file at all, the checker fails.The later commits answer Codex's next nine looks, which kept finding the same shape of gap one layer down. Two of them fixed the pattern rather than another instance: every known program was swept across every file in scope at once, first the Python and shell files and then, when that still missed things, the PowerShell files with patterns written for PowerShell. Along the way the lines pinned were often the line that names a program, not the line that runs it, so now every entry pins the actual command. The sweeps turned up
curland the PowerShell web calls (local "is the server up" probes in the setup scripts, the sandbox smoke suites on three platforms, and the scheduled health jobs the Telegram channel generates, plus one Ollama installer fetch behind a prompt that defaults to no), the Mac setup script runningghwith whatever telemetry setting your shell had (it now loads the same canonical block the launcher does, and if it cannot, it skipsghrather than run it unprotected), the same script starting Ollama and pulling a model, the installers cloning the repository from GitHub and downloading packages with pip and Homebrew, the scripts opening web pages in your browser, and the one place the running code can push to GitHub, which needs two switches turned on that ship off. The last look found the two little wrapper scripts that fetch a password from the Mac Keychain or Windows Credential Manager and then run whatever command you hand them, which makes them launchers whose program nobody can name in advance, so they are now listed as exactly that; it also found that the two installer scripts ran pip before anyone had told pip not to check PyPI for a newer version of itself, so both now load the telemetry block first. Anything that actually reaches the internet on purpose now carries its own entry marked as intentional internet traffic instead of hiding under a "local" or "no mechanism" one, and the security summary document says the same thing in the same words. Every Ollama command the setup script runs is now told to talk to localhost only. The checker also now complains if a launch site is written down without the program being on the guest list, if an entry or a launch site points at a path that does not exist, lives outside the repository or sneaks out of the sandbox folder with a.., and if a program's list entry points at a note that does not exist, which was a real bug in the checker's own logic. The self-test proves every one of these complaints can fire.One line was drawn by the owner: the helper scripts the AI agents use (session hooks, skill self-tests, the PR-babysitting wrappers) are not CyClaw and are not on the guest list, even though some of them run
ghandgit. The two sandbox scripts that poke the actual server stay on it. The checker now refuses any new entry for an agent helper, of either kind, with the single allowed file matched exactly, so that line only moves if someone edits it on purpose.What could still go wrong: the markers are plain text, so an innocent rename in one of those files will make the checker grumble until someone re-points the row; with 74 of them that will happen more often than before, and that is the price of it noticing at all. The Mac setup script now skips
ghlogin when it cannot load the telemetry block and points every Ollama command at localhost; both are behaviour changes you will see as a warning or a fixed address, not a failure. They were not run here (Linux sandbox), but the shell lint passed in CI on the first head that carried the block. uv is not version-pinned, so if Astral ever adds telemetry, the "no telemetry" note goes stale until the 120-day review alarm fires. Nothing in the actual running server changed, so there is no new way for data to leave the box because of this diff. I did not run the full test suite; CI does that. I did run the checker, its self-test, and the doc-drift checker directly on the pushed head, all green.🤖 Generated with Claude Code
https://claude.ai/code/session_012semJHbJjTtdTy3zrhUnR3
Last updated: 2026-10-13 04:50 ET