Skip to content

fix(agent route): let the sandboxed app open its socket; record the ADR-0013 spike - #112

Merged
nniiovoo merged 1 commit into
mainfrom
fix/agent-socket-sandbox
Nov 9, 2026
Merged

nniiovoo merged 1 commit into
mainfrom
fix/agent-socket-sandbox

Conversation

@nniiovoo

@nniiovoo nniiovoo commented Nov 9, 2026

Copy link
Copy Markdown
Owner

The ADR-0013 spike found a real problem in the merged agent-key route (ADR-0012), and this PR fixes it with one sandbox rule you approved. It also records the spike in ADR-0013.

The problem

As built, the app could not have opened its agent socket in a real, sandboxed build. The folder exception for /Users/Shared/encrypted-note-keys covers files, not Unix sockets. The route's tests pass because they run unsandboxed.

The spike used a stand-in program signed with this app's exact Entitlements.plist and hardened runtime, under the real App Sandbox, with fake data only. Every test also ran unsandboxed as a control.

In the excepted folder Result under the sandbox
Write an ordinary file allowed (a control folder without the exception is refused)
bind() a Unix socket refused (EPERM)
bind() with network.server added refused
Connect out to a socket there refused
bind() with the rule in this PR works: listens, accepts, and identifies each caller

The fix (the one entitlement change)

<key>com.apple.security.temporary-exception.sbpl</key>
<array>
    <string>(allow network-bind network-inbound (local unix-socket (subpath "/Users/Shared/encrypted-note-keys")))</string>
</array>
  • What it allows: listening on, and accepting, Unix sockets in that one folder.
  • What it doesn't add: no outbound connections, no IP sockets, and no new network entitlement.
  • Tested: the spike ran with the exact file committed here, at the real path; the folder was removed again afterwards.
  • Pinned: scripts/pinned-hashes.txt is updated for it (101968d3… → cd6e849a…).
  • You approved it on 2026-11-09.

Still to confirm: a Developer ID-signed, notarized build. The spike was signed ad-hoc. docs/agent-keys.md keeps that hand check open.

What ADR-0013 now records

  • The Security framework is refused under the sandbox. Its checks of another program's signature read that program's file, so they're blocked for Claude Code, Codex and other accounts' processes.
  • The kernel's own record works. Read with csops_audittoken (and sysctl for the parent process), it gives identity, team and "validated as Developer ID" for Claude Code, Codex, and processes owned by other accounts.
  • The parent check works. sysctl reads the parent's process number even across accounts, where proc_pidinfo is refused.
  • A fake is caught. A program signed ad-hoc as com.anthropic.claude-code matches the identity, but has no team and no Developer ID category.
  • So the check must match all of these: identity, team, Developer ID category, hardened runtime, and not debuggable.
  • Your decision: Claude Code is accepted even though its signature disables library validation (Codex's doesn't). The risk is stated in Consequences.
  • The Claude app's Code tab is covered. It runs its own copy of Claude Code with the same identity and team.

Files

  • src-tauri/Entitlements.plist, scripts/pinned-hashes.txt: the rule and its hash.
  • docs/adr/0013-…md: the spike result, how the check reads programs, and the Claude Code decision.
  • docs/adr/0012-…md, docs/agent-keys.md, docs/security-model.md: they now describe the two entitlements, and the hand check notes what the spike answered.
  • src-tauri/src/agent_route.rs: one comment.

Checks, stated plainly

Passed locally:

  • the pinned-entitlements check, with the new hash;
  • networking-source rules;
  • Reader publication tests;
  • cargo fmt;
  • secret scan;
  • all 51 relative links in the changed docs.

Not completed locally:

  • Rust tests: the run was killed partway.
  • Companion tests: clock-sensitive tests timed out, 2 to 7 per run, while the Mac's load was 18 to 31 from other work.

This PR changes no code path and nothing in companion/, so CI on clean macOS and Windows runners is the full check.

🤖 Generated with Claude Code

…DR-0013 spike

The ADR-0013 spike, run with this app's exact entitlements and hardened
runtime, found that the folder exception for /Users/Shared/encrypted-
note-keys covers files but not Unix sockets: bind() there is refused
under the App Sandbox, with or without network.server. So the merged
ADR-0012 route could not have opened its socket in a sandboxed build;
the tests pass because they run unsandboxed.

Add one temporary-exception.sbpl rule allowing only network-bind and
network-inbound on Unix sockets in that folder (no outbound, no IP
sockets), approved by the owner on 2026-11-09, and update its pinned
hash. The spike showed the rule works with the exact file committed
here; a Developer ID-signed, notarized build still has to confirm it.

ADR-0013 records the spike: the Security framework's signature checks
are refused under the sandbox for other programs, but the kernel's own
record (csops_audittoken, sysctl) gives identity, team and validation
category for Claude Code, Codex and other accounts' processes, and a
fake ad-hoc "com.anthropic.claude-code" shows no team and no Developer
ID category. The owner accepted Claude Code although its signature
disables library validation. ADR-0012, the guide and the security
model describe the two entitlements.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nniiovoo
nniiovoo enabled auto-merge November 9, 2026 23:06
@nniiovoo
nniiovoo merged commit d287992 into main Nov 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant