Blog | Zencoder – The AI Coding Agent

Fixing `gh` CLI (and other Go tools) in Claude Code's Sandbox Without Weakening Security

Written by Andrew Filev | Mar 6, 2026 10:32:00 PM

Claude Code's macOS sandbox blocks Go-based CLI tools (gh, gcloud, terraform, kubectl) from making TLS connections. The official fix (enableWeakerNetworkIsolation) works but opens a data exfiltration vector. Here's how to fix it properly by rebuilding gh with embedded root certificates.

As with any security workaround, evaluate the tradeoffs for your own setup and threat model before applying.

The Problem

Inside Claude Code's sandbox, any gh command that hits the network fails:

$ gh api /user
Get "https://api.github.com/user": tls: failed to verify certificate: x509: OSStatus -26276

This happens even though api.github.com is in the sandbox's allowedHosts. gh auth status works fine outside the sandbox.

Root Cause

Claude Code on macOS uses Apple's Seatbelt framework (sandbox-exec) for isolation. Network traffic is routed through a local proxy that enforces domain allowlists. The proxy correctly passes traffic to allowed hosts, and the TLS connection reaches the real server with the real certificate.

The failure is in certificate verification. On macOS, Go's crypto/x509 package verifies TLS certificates by talking to com.apple.trustd.agent via Mach IPC. The sandbox blocks this IPC channel. Without trustd, Go can't verify any certificate, so every TLS handshake fails.

The OSStatus -26276 error code is a macOS Security framework error indicating the trust evaluation couldn't be completed.

Why CGO_ENABLED=0 Doesn't Help

The obvious fix seems like building with CGO_ENABLED=0 to force Go's pure-Go x509 verifier. On Linux, this verifier reads CA certificates from /etc/ssl/certs/ and verifies without any system daemon.

This doesn't work on modern Go (1.18+). Go on macOS calls into the Security framework using //go:cgo_import_dynamic directives, which dynamically link to SecTrustEvaluateWithError() and friends without traditional cgo. You can confirm this: a binary built with CGO_ENABLED=0 still reports OSStatus errors and still has __cgo_* symbols (which are runtime stubs for the dynamic import mechanism).

Setting SSL_CERT_FILE=/etc/ssl/cert.pem doesn't help either. On macOS, Go's crypto/x509/root_darwin.go returns an empty CertPool with systemPool: true, which tells the verifier to delegate everything to systemVerify() (the Security framework path). The file root_unix.go, which reads SSL_CERT_FILE, has a build constraint that explicitly excludes darwin.

GODEBUG=x509usefallbackroots=1 alone also doesn't help. This flag only takes effect if x509.SetFallbackRoots() has been called first to provide an alternative root certificate pool. Without that, there are no fallback roots to fall back to.

The Official Fix (and Its Tradeoff)

Anthropic added enableWeakerNetworkIsolation in Claude Code v2.1.69 specifically for this problem:

// ~/.claude/settings.json
{
  "sandbox": {
    "enableWeakerNetworkIsolation": true
  }
}

This re-enables Mach IPC access to com.apple.trustd.agent in the Seatbelt profile. Anthropic's docs explicitly warn that "enabling this opens a potential data exfiltration vector through the trustd service."

The Better Fix: Rebuild gh with Embedded Root Certificates

The golang.org/x/crypto/x509roots/fallback package embeds the Mozilla/NSS root CA bundle directly into the binary. When combined with GODEBUG=x509usefallbackroots=1, Go replaces the macOS system pool with the embedded certificates and uses its pure-Go verifier. The Security framework is never called.

Step 1: Clone and patch

git clone --depth 1 --branch v2.74.2 https://github.com/cli/cli.git ~/repo/gh-cli
cd ~/repo/gh-cli

Add the fallback roots dependency:

go get golang.org/x/crypto/x509roots/fallback

Edit cmd/gh/main.go to add the import:

package main

import (
"os"

 "github.com/cli/cli/v2/internal/ghcmd"
 _ "golang.org/x/crypto/x509roots/fallback"
)

func main() {
 code := ghcmd.Main()
 os.Exit(int(code))
}

The blank import (_) triggers the package's init() function, which calls x509.SetFallbackRoots() with the embedded Mozilla/NSS certificates.

Step 2: Build

cd ~/repo/gh-cli
mkdir -p bin
CGO_ENABLED=0 go build -o bin/gh ./cmd/gh

Note: CGO_ENABLED=0 isn't what makes this fix work (the fallback roots import is what bypasses the Security framework). But it produces a cleaner static binary with no dynamic dependencies.

Step 3: Configure Claude Code

In ~/.claude/settings.json, add:

{
  "env": {
    "PATH": "/Users/YOU/repo/gh-cli/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin",
    "GODEBUG": "x509usefallbackroots=1"
  }
}

This makes Claude Code use the custom gh binary and sets the GODEBUG flag. Your regular terminal keeps using the Homebrew gh unaffected.

You also need ~/.config/gh readable by the sandbox so gh can access its auth token. In the sandbox permissions section of settings.json:

{
  "sandbox": {
    "permissions": {
      "read": {
        "allow": ["~/.config/gh"]
      }
    }
  }
}

Step 4: Verify

Restart Claude Code, then:

$ which gh
/Users/andrew/repo/gh-cli/bin/gh

$ gh api /user --jq '.login'
andrewzenc

No enableWeakerNetworkIsolation, full sandbox security intact.

How It Works Under the Hood

  1. The fallback package's init() calls x509.SetFallbackRoots(embeddedMozillaRoots)
  2. GODEBUG=x509usefallbackroots=1 tells initSystemRoots() to replace the darwin system pool ({systemPool: true}) with the fallback pool
  3. The replacement pool has systemPool: false
  4. In verify.go, the check if opts.Roots.systemPool is now false, so systemVerify() (the Security framework path) is skipped entirely
  5. Go's pure-Go verifier runs against the embedded Mozilla/NSS root certificates
  6. /etc/ssl/cert.pem and com.apple.trustd.agent are never consulted

Does CGO_ENABLED=0 Weaken Security?

No. It changes how Go does TLS verification, not the strength:

  • With cgo (default on macOS): Go calls macOS Security framework via Mach IPC to trustd, which checks the system keychain, does OCSP lookups, etc.
  • Without cgo + fallback roots: Go reads the embedded Mozilla/NSS CA bundle and does the same x509 chain validation with the same cryptography, just in pure Go.

What you give up is minor: no real-time OCSP revocation checks via the OS (though Go supports OCSP stapling), and no automatic system keychain updates (the embedded bundle is fixed at build time). These are the same tradeoffs every Go binary on Linux makes, which describes most Go binaries in production.

Applies to Other Go Tools

The same technique works for any Go CLI tool blocked by the sandbox: gcloud, terraform, kubectl, etc. Add the _ "golang.org/x/crypto/x509roots/fallback" import to the main package, rebuild, and set GODEBUG=x509usefallbackroots=1.

Maintenance

The embedded root certificate bundle is fixed at build time. To update it, periodically rebuild:

cd ~/repo/gh-cli
go get -u golang.org/x/crypto/x509roots/fallback
CGO_ENABLED=0 go build -o bin/gh ./cmd/gh

Also update the gh version itself when new releases come out. Note that checking out a new tag resets cmd/gh/main.go, so you'll need to re-apply the fallback import:

cd ~/repo/gh-cli
git fetch --tags
git checkout v2.XX.X
# Re-add the _ "golang.org/x/crypto/x509roots/fallback" import to cmd/gh/main.go
go get golang.org/x/crypto/x509roots/fallback
CGO_ENABLED=0 go build -o bin/gh ./cmd/gh