Sandboxed Go CLIs fail TLS verification: sandbox denies mach-lookup com.apple.trustd.agent
Sandboxed Go CLIs fail TLS verification: deny mach-lookup com.apple.trustd.agent
Summary
On macOS, any Go-based CLI running inside the Bash sandbox fails TLS certificate
verification, because the sandbox profile denies mach-lookup com.apple.trustd.agent.
Go's crypto/x509 delegates chain verification to macOS trust services, so the
connection itself succeeds but verification fails.
The symptom is a misleading error that looks like a certificate or network problem:
Get "https://api.github.com/rate_limit": tls: failed to verify certificate: x509: OSStatus -26276
curl and security verify-cert are unaffected in the same sandbox at the same moment.
Environment
- Claude Code 2.1.222 (desktop app)
- macOS 26.5.2 (25F84), Apple silicon
gh2.94.0 (Homebrew), a Go binary- Sandbox enabled via
sandbox.enabled: true api.github.comreachable (routed through the sandbox's local proxy)
Evidence
Kernel sandbox log during a failing run:
sandboxd [com.apple.sandbox.reporting:violation] Sandbox: gh(64174) deny(1) mach-lookup com.apple.trustd.agent
Path: /opt/homebrew/Cellar/gh/2.94.0/bin/gh
Responsible: .../claude.app/Contents/MacOS/claude
Denial counts over a 3 minute window, same host, same sandbox:
| process | mach-lookup com.apple.trustd.agent denials |
|---|---|
| gh (Go) | 47 |
| security verify-cert | 0 |
| curl | 0 |
curl returns HTTP 200 and security verify-cert reports No error at the same
moments gh fails, so the certificate chain and the trust daemon are both fine.
The chain served inside the sandbox is identical to the one served outside
(3 certificates, genuine Sectigo issuer) - the proxy is not substituting anything.
What we ruled out
Each of these was tested and refuted, in case they come up:
- Domain restrictions.
github.com,*.github.com,*.githubusercontent.comwere
already in allowedDomains, and curl to the same host succeeds.
- A CA difference between tools.
curlis built against SecureTransport, so it uses
the same macOS trust services.
- TLS interception by the sandbox proxy. The presented chain is the genuine one.
- A missing intermediate forcing an AIA fetch. The chain is complete inside the sandbox.
trustdbeing unavailable.security verify-certsucceeds during the failures.- Go-side workarounds.
SSL_CERT_FILE,GODEBUG=x509usefallbackroots=1and
GODEBUG=netdns=go all have no effect, because Go on macOS uses the platform
verifier regardless.
Why this was hard to diagnose
excludedCommands contained gh *, so most invocations ran outside the sandbox and
worked. Only invocations where gh was nested hit the sandbox:
| invocation | excluded? |
|---|---|
| gh api ... | yes |
| echo x; gh api ... | yes |
| VAR=1 gh api ... | yes |
| for i in 1 2; do gh api ...; done | no |
| x=$(gh api ...) | no |
Because the exclusion appears to be matched against top level command segments only,
the same command succeeded or failed depending on how it was written. That made the
failure look intermittent and time dependent, and cost several hours to localise.
This also means a script that shells out to gh (for example a Python audit script
invoked as python3 audit.py) can never match gh *, and always fails.
Suggested fixes
- Allow
mach-lookup com.apple.trustd.agentin the sandbox profile. It is required
for TLS verification by any Go binary, and denying it does not add isolation once
network egress is already gated by the proxy.
- If it must stay denied, surface the denial rather than letting it surface as an
opaque x509: OSStatus -26276.
- Consider whether
excludedCommandsshould match nested invocations, or document
that it does not. The current behaviour makes an entry look applied when it is not.
Workarounds in use
- Keep Go CLIs at the top level of the command, never inside loops or
$(...). - For repeated calls, use
curlwith the token instead ofgh. - For anything that shells out to
ghfrom a script, run it in CI instead of locally.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗