Claude Code

Anthropic's CLI coding agent with an external launcher boundary and kernel sandboxing outside the model.

Last updated

Cloud API TypeScript

1. Installation

Prerequisites

  • macOS 13+ or Linux (kernel 5.13+ for Landlock)
  • Node.js 18+
  • An Anthropic API key or Claude.ai login

Install Claude Code

brew install --cask claude-code

Install the preferred stack

brew tap nvk/tap
brew install nvk/tap/agent-bondage
brew install nono

# Optional, only if you want API-key-based launches:
brew install nvk/tap/envchain-xtra
macOS Claude auth note: if you use Claude Code with the normal Claude.ai web/OAuth login on macOS, the normal Claude profile still needs Keychain access so the login persists. If you derive your default Claude profile from a no-Keychain variant, Claude may ask you to re-authenticate every session. Reserve no-Keychain Claude profiles for API-key-only or experimental paths.

Verify

claude --version
bondage --help
nono --version
envchain --version  # if installed

2. nono Profile

nono applies kernel-level restrictions (macOS Seatbelt / Linux Landlock) so Claude Code can only access the project directory and the network destinations you intend. In the preferred setup, bondage chooses the exact profile and target, but nono is still the layer enforcing filesystem and network policy.

What the sandbox allows

ResourceAccessWhy
Current working directoryRead + WriteProject files
api.anthropic.comNetworkAPI calls
/dev/tty, /dev/nullRead + WriteTerminal I/O
/dev/urandomReadCrypto randomness
Node.js runtimeExecuteClaude Code binary

What the sandbox blocks

ResourceWhy blocked
~/.ssh/SSH keys
~/.aws/, ~/.gnupg/Cloud and GPG credentials
~/Documents/, ~/Desktop/Personal files
/Volumes/External drives
All other networkNo lateral movement

nono ships with a built-in claude-code profile. On macOS, if you use Claude.ai/OAuth, your normal Claude profile should usually inherit from the Keychain-capable variant so login state can persist. A no-Keychain Claude profile is better treated as a special-case profile for API-key-only or experimental launches, not the default.

Note: The --allow-cwd flag grants read/write to your current directory. Always cd into the project before launching.

3. Optional envchain-xtra

Claude Code often uses its own login flow, so envchain-xtra is not always required for normal use. Use it when you want an API-key-based launch path or when you need per-profile secret injection as part of a larger agent stack.

Store your API key

# Interactive prompt — key never appears in shell history
envchain --set claude ANTHROPIC_API_KEY

This creates a Keychain entry named envchain-claude. You can verify it exists in Keychain Access.app.

Why not just export in .zshrc?

Risk: export ANTHROPIC_API_KEY="sk-ant-..." in your shell config puts the key in every child process — npm scripts, VS Code extensions, git hooks. A malicious postinstall script can read it with echo $ANTHROPIC_API_KEY. envchain limits the key to exactly the process that needs it.

4. bondage Wrapper

Add this thin wrapper to your shell config. It keeps shell logic out of the trust boundary and lets bondage choose whether this profile uses envchain-xtra, nono, or both.

claude() {
  bondage exec claude ~/.config/bondage/bondage.conf -- "$@"
}

Sample stack snippets

Assuming your shared [global] block already exists in ~/.config/bondage/bondage.conf, this is the per-tool shape to adapt:

# ~/.config/bondage/bondage.conf
[profile "claude"]
use_envchain = false
use_nono = true
nono_profile = claude-code
touch_policy = none
target_kind = native
target = /absolute/path/to/claude
target_fp = sha256:replace-me
nono_allow_cwd = true
nono_allow_file = /dev/tty
nono_allow_file = /dev/null
nono_read_file = /dev/urandom
{
  "extends": "claude-code",
  "meta": {
    "name": "claude-code",
    "description": "Claude Code with project-only access and persistent auth on macOS"
  },
  "policy": {
    "add_deny_access": ["/Volumes"]
  },
  "workdir": {
    "access": "readwrite"
  }
}

On macOS, keep the normal Claude.ai/OAuth profile on the Keychain-capable claude-code base. Reserve no-Keychain variants for API-key-only or experimental use.

How it works

  1. The shell wrapper passes only the profile name and your arguments
  2. bondage verifies the exact target and any pinned runtime artifacts
  3. bondage applies the chosen nono profile
  4. If configured, bondage also runs the launch through envchain-xtra

Reload your shell:

source ~/.zshrc

Optional: strip API key from subprocesses

Claude Code can scrub the API key from all child processes (Bash tool, hooks, MCP servers):

export CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1

Add this to your ~/.zshrc alongside the wrapper. Combined with envchain, the key exists only in the Claude Code parent process.

5. Verification

Test the full chain

# Should launch Claude Code normally
bondage verify claude ~/.config/bondage/bondage.conf
bondage chain claude ~/.config/bondage/bondage.conf -- --version
claude --version

Confirm credential isolation

# Inside Claude Code, run:
# ! env | grep ANTHROPIC
# Should show ANTHROPIC_API_KEY (injected by envchain)

# ! env | grep OPENAI
# Should show nothing (different namespace)

Confirm no plaintext keys on disk

# Should return nothing
grep -r "sk-ant" ~/.zshrc ~/.bashrc ~/.claude/ 2>/dev/null

Troubleshooting

SymptomCauseFix
"Invalid API key" Wrong login mode or envchain namespace missing If using envchain-xtra, re-store ANTHROPIC_API_KEY in the expected namespace
"Operation not permitted" nono blocking a needed path Check nono why for denied paths
Keychain popup on each launch Missing ACL on Keychain entry Re-store with -T /usr/bin/security flag
Claude asks you to log in again every session Default Claude profile derived from a no-Keychain variant Use a Keychain-capable claude-code base for the normal Claude.ai/OAuth profile
Shell name → bondage → [envchain-xtra] → nono → Claude Code Convenience Launch policy Optional secrets Kernel sandbox Actual agent