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

Observed on 2026-10-26: Claude Code 2.1.310, agent-bondage 0.3.1, envchain-xtra 1.4.1, and upstream nono 0.80.0. Validate migrated Nono profiles before making them the daily default; the last public end-to-end stack test used Nono 0.61.1.

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 --version
nono --version
envchain --help  # prints the version header, 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 networkBlocked only when the selected Nono profile enables filtering or full network blocking

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.
Verify network separately: filesystem isolation does not imply a network allowlist. Current Nono leaves networking unrestricted unless the launch selects a network profile, allowed domains, credential routes, or full network blocking.

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 without printing values

# From a controlled shell inside the selected launch profile:
python3 -c 'import os; print("ANTHROPIC_API_KEY:", "set" if os.getenv("ANTHROPIC_API_KEY") else "unset")'
python3 -c 'import os; print("OPENAI_API_KEY:", "set" if os.getenv("OPENAI_API_KEY") else "unset")'

An API-key profile should report the Anthropic name as set and the unrelated OpenAI name as unset. If CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 is active, Claude-launched child processes should instead report the Anthropic name as unset. Never print the credential value into a transcript.

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