Documentation Index
Fetch the complete documentation index at: https://nono.sh/docs/llms.txt
Use this file to discover all available pages before exploring further.
When you run Claude Code under
nono, prefer using nono as the only sandbox layer. Leave Claude Code’s built-in sandbox disabled so all filesystem and network boundaries come from the kernel-enforced nono policy.If you keep Claude Code’s sandbox enabled, Anthropic documents an intentional escape hatch: when a command fails because of sandbox restrictions, Claude may retry it with dangerouslyDisableSandbox after going through Claude Code’s normal permissions flow. To disable that fallback, set "allowUnsandboxedCommands": false in Claude Code’s sandbox settings. See Anthropic’s sandboxing docs.Why Sandbox Claude Code?
Claude Code includes its own sandboxing, but Anthropic documents an intentional escape hatch: when a command fails because of sandbox restrictions, Claude may retry it withdangerouslyDisableSandbox after approval. Running Claude inside nono gives you a single kernel-enforced boundary that is easier to reason about and audit.
Quick Start
- Read+write access to the current working directory
- Read+write access to
~/.claude(agent state, debug logs, project config) - Read+write access to
~/.claude.json(settings file) - Read+write access to
~/.vscode(VS Code extensions directory) - Read+write access to the OS-specific VS Code app data directory:
~/Library/Application Support/Codeon macOS,~/.config/Codeon Linux - Read+write access to Claude Code’s OS-specific credential storage:
macOS login keychain files required for OAuth persistence/refresh, or
~/.local/share/claudeon Linux - Network access enabled (required for Anthropic API)
- Interactive mode enabled (preserves TTY for Claude’s terminal UI)
- Automatic hook installation for sandbox-aware error handling
- OAuth2 login support via supervised URL opening (allows
claude /loginto work)
Custom Profile
If you need different permissions, create a custom profile at~/.config/nono/profiles/claude-code.json:
Custom profiles with the same name override the pack profile. Remove or rename the file to revert to the pack-provided profile.
Multiple Claude Profiles (CLAUDE_CONFIG_DIR)
If you use multiple Claude Code configurations via the CLAUDE_CONFIG_DIR environment variable, you need to grant the sandbox access to the custom config directory and its sibling .lock directory. Claude Code creates $CLAUDE_CONFIG_DIR.lock/ during OAuth token refresh coordination:
Do not set
CLAUDE_CONFIG_DIR to its default value (~/.claude) explicitly — Claude Code treats “explicitly set” differently from “unset”, which changes keychain service names and config file lookup paths. Only set it when you genuinely need a non-default location.For the default
~/.claude config directory, no extra flags or directories are needed — the profile already grants access to both ~/.claude and ~/.claude.lock.Security Tips
Use Secrets Management
Instead of keeping your API key in environment variable exports or shell config files, load it from the system keystore: macOS:Restrict to Specific Projects
The profile grants access to the current working directory (wherever you run the command). To limit access to a specific directory regardless of where you invoke it:Read-Only Mode
For code review or exploration where Claude shouldn’t modify files:Block Network for Offline Work
If you want to prevent any outbound connections (e.g., for reviewing local code without API calls):Add Extra Domains
If Claude Code needs to reach a domain not in the built-in network profile:OAuth2 Login
Runningclaude /login inside a sandbox requires the browser to open outside the sandboxed process. On Linux (Landlock), nono can delegate approved URLs to the unsandboxed parent process. On macOS (Seatbelt), Claude’s login flow needs a temporary LaunchServices permission to open the browser.
The claude-code profile includes an origin allowlist that controls which URLs may be opened during supervised login flows:
https://claude.ai(OAuth2 authorize endpoint)localhostcallbacks (for the OAuth2 redirect)
nono auto-enables LaunchServices for the claude-code profile when no refresh-capable local auth is detected. You can also enable it explicitly for the login session:
--allow-launch-services unless you still need browser-opening help for that session.
Adding Custom OAuth2 Origins
If your organization uses a custom identity provider for Claude Code authentication, add its origin to your profile:open_urls, it replaces the base profile’s config entirely. Include all origins you need, including https://claude.ai if you still want the standard OAuth2 flow. Omit open_urls to inherit the base profile’s settings unchanged.
The origin allowlist is enforced by the supervisor when
nono delegates browser opening itself. On macOS, --allow-launch-services bypasses that supervisor path for the duration of the session in exchange for login compatibility.Enabling LSPs, Linters, and Dev Tools
Claude Code’s LSP plugins (pyright, rust-analyzer, etc.) spawn language servers as child processes. These spawns useposix_spawnp() which searches your PATH for the binary. If any PATH directory is unreadable to the sandbox, posix_spawnp() receives EPERM from the kernel and stops searching immediately (unlike ENOENT, which continues to the next entry).
nono’s built-in system paths cover standard directories (/usr/bin, /opt, etc.), but version managers and dev tools install binaries under ~/ which requires explicit read access.
Common home-directory PATH entries that need read access:
| Tool | Path |
|---|---|
| Rust / cargo | ~/.cargo/bin |
| Go | ~/go/bin |
| Python / pyenv | ~/.pyenv/shims, ~/.pyenv/bin |
| Node / fnm | ~/.local/share/fnm, ~/.local/state/fnm_multishells |
| Node / nvm | ~/.nvm |
| Node / pnpm | ~/Library/pnpm, ~/.local/share/pnpm |
| Haskell / ghcup | ~/.ghcup/bin |
| Ruby / rbenv | ~/.rbenv/shims, ~/.rbenv/bin |
| Local binaries | ~/.local/bin |
~/ entry that appears before the directory containing your LSP binary:
You only need
--read (not --allow) for these directories. This permits PATH lookup without granting write access.VS Code Extension
Claude Code installs a VS Code extension on startup. The profile already grants write access to~/.vscode plus the OS-specific VS Code data directory: ~/Library/Application Support/Code on macOS or ~/.config/Code on Linux. No additional flags are needed for VS Code extension installation.
Git Configuration
Claude Code reads git configuration for repository operations. The profile includes thegit_config group, which grants read access to ~/.gitconfig, ~/.gitignore_global, and ~/.config/git/ignore. No additional flags are needed for git operations.
Secretive (SSH Keys in Secure Enclave)
If you use Secretive to store SSH keys in the macOS Secure Enclave, git commit signing (git commit -S) needs access to the Secretive agent socket. Create a custom profile that extends the Claude Code profile:
Save as ~/.config/nono/profiles/claude-code-secretive.json:
- Read access to
~/.ssh/config(git signing configuration) - Read access to the Secretive agent socket (
~/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh) - Read+write access to
~/.ssh/known_hosts(SSH may append new host keys)
The Secretive socket is a Unix domain socket, not a regular file. nono supports granting capabilities on sockets directly, so only the socket itself is exposed — not the entire container directory.
Overriding Profile Settings
CLI flags always take precedence over profile settings:Automatic Pack Install and Plugin Wiring
As of v0.43, theclaude-code profile is no longer compiled into the CLI. It’s distributed as a signed registry pack at always-further/claude along with a Claude Code plugin that provides sandbox-aware diagnostics.
The first time you run nono run --profile claude-code -- claude, nono prompts to install the pack:
Y (or just Enter). nono pulls the pack, verifies its Sigstore provenance, and prints a summary that ties the release artifacts back to the source repository.
What “Pull” Actually Does
After verification, nono installs the pack to~/.config/nono/packages/always-further/claude/ and wires it into Claude Code as a single-plugin marketplace:
| What | Where |
|---|---|
| Pack files (profile, plugin manifest, hooks, skills) | ~/.config/nono/packages/always-further/claude/ |
| Marketplace manifest (synthesised by nono) | ~/.claude/plugins/marketplaces/always-further/.claude-plugin/marketplace.json |
| Plugin source (symlink → pack dir) | ~/.claude/plugins/marketplaces/always-further/plugins/nono |
| Cache pointer (symlink → pack dir) | ~/.claude/plugins/cache/always-further/nono/<version> |
| Marketplace registry entry | ~/.claude/plugins/known_marketplaces.json |
| Plugin install record | ~/.claude/plugins/installed_plugins.json |
| Enabled-plugins toggle | ~/.claude/settings.json::enabledPlugins["nono@always-further"] |
nono remove always-further/claude unwires every Claude-side entry too.
Subsequent Runs
Once installed,nono run --profile claude-code -- claude runs silently — no prompt. nono’s profile resolver finds claude-code in the pack store and re-asserts the marketplace wiring on every run, so deleting any of the symlinks above is recovered transparently on the next nono run.
If a newer version of the pack is available, nono prints a one-line hint after the capabilities block:
nono update to apply it, or nono outdated to check all installed packs at once. See Managing Packs for pinning and other options.
Skipping or Automating the Prompt
| Environment | Behaviour |
|---|---|
| Interactive TTY | Prompt fires; press Enter or Y to install. |
NONO_AUTO_MIGRATE=1 | Prompt skipped; pull runs immediately. Useful for CI/scripted setup. |
NONO_NO_MIGRATE=1 | Pull blocked; nono exits cleanly with a hint pointing at nono pull always-further/claude. |
| Non-TTY (no env override) | Same as NONO_NO_MIGRATE=1 — nono exits cleanly with a hint. |
User Profiles That Extend claude-code
If you have a custom profile under ~/.config/nono/profiles/ that does "extends": "claude-code", nono’s chain-aware resolver detects the missing base and triggers the same install prompt. You don’t need to migrate your existing profiles — they keep working after the upgrade.
nono run --profile claude-with-docs, the prompt fires for always-further/claude. After accept, the profile resolves cleanly via the pack-installed claude-code base.
Hook-Driven Sandbox Diagnostics
The pack’s Claude Code plugin registers two hooks viahooks/hooks.json:
PostToolUseFailure— fires when a Read/Write/Edit/Bash tool call hits a sandbox denial. Injects anadditionalContextblock that tells Claude this is a nono sandbox boundary (not macOS TCC, not a Unix permissions issue), lists the currently-allowed paths, and instructs Claude to runnono whyfor the precise diagnosis and present the user with two options.PostToolUsefor Bash — same idea, scoped to Bash output that contains a permission-denial signature.
What Claude Sees on a Sandbox Denial
When Claude attempts a blocked path, the hook injects roughly:nono why (you’ll be prompted to approve the first time — see Pre-approving Diagnostic Commands below), reads the diagnosis, and presents the user with both options.
Pre-approving Diagnostic Commands
The hook tells Claude to runnono why <path> for diagnosis and nono profile guide if the user picks Option B. Claude prompts for approval the first time it runs each — there are two ways to skip those prompts in future sessions:
- Per-session, on first prompt. Pick “Yes, and don’t ask again for: nono why *”. Claude saves the rule in your project-local settings.
- Globally, by editing your settings file. Add the patterns to
~/.claude/settings.json::permissions.allow:
Removing the Pack
known_marketplaces.json / installed_plugins.json, and the enabledPlugins["nono@always-further"] toggle. Claude Code’s view of the world is restored to what it was before the install. Re-running nono run --profile claude-code will prompt to re-install.