Skip to main content
Profiles are pre-configured capability sets that define what a sandboxed process can access. Groups are the composable building blocks that profiles are made from. Together they codify security policy so you don’t have to specify flags manually every time.
Use nono policy to inspect, compare, and validate profiles and groups from the command line. See Policy Introspection for details.

Profiles

Why Profiles?

Manually specifying capabilities for every tool is tedious and error-prone:
# Without profiles - verbose and easy to misconfigure
nono run --allow-cwd --read ~/.claude --read-file ~/.claude/config.json -- claude
Profiles simplify this:
# With profiles - concise and auditable
nono run --profile claude-code -- claude

Profile Sources

Profiles can come from three sources, in order of precedence:
SourceLocationTrust Level
CLI flagsCommand lineHighest - explicit user intent
User profiles~/.config/nono/profiles/Medium - user-defined
Built-in profilesCompiled into binaryBase - audited defaults
CLI flags always override profile settings.

Profile Format

Profiles use JSON format:
{
  "meta": {
    "name": "my-agent",
    "version": "1.0.0",
    "description": "Profile for my custom agent"
  },
  "workdir": {
    "access": "readwrite"
  },
  "security": {
    "groups": ["node_runtime", "python_runtime", "git_config"]
  },
  "filesystem": {
    "allow": ["$HOME/.config/my-agent"],
    "read": [],
    "write": [],
    "allow_file": [],
    "read_file": [],
    "write_file": []
  },
  "policy": {
    "exclude_groups": [],
    "add_allow_read": [],
    "add_allow_write": [],
    "add_allow_readwrite": [],
    "add_deny_access": [],
    "add_deny_commands": [],
    "override_deny": []
  },
  "network": {
    "block": false
  }
}

Working Directory

The workdir section controls whether and how the current working directory is automatically shared with the sandboxed process.
ValueMeaning
"none"No automatic CWD access (default if section omitted)
"read"Read-only access to CWD
"write"Write-only access to CWD
"readwrite"Full read+write access to CWD
When a profile specifies a workdir access level, nono will prompt the user to confirm CWD sharing (unless --allow-cwd is used to skip the prompt).

Policy Overrides

The policy section provides fine-grained, additive and subtractive composition on top of inherited groups and filesystem configuration. This is the primary mechanism for surgically customizing inherited profiles without redefining everything from scratch.
FieldDescription
exclude_groupsRemove groups from the resolved group set (exclusion wins over addition)
add_allow_readAdditional read-only directories to allow
add_allow_writeAdditional write-only directories to allow
add_allow_readwriteAdditional read+write directories to allow
add_deny_accessAdditional deny rules to apply (block read and write content). On macOS, Unix socket paths also emit a network-outbound deny so that connect(2) to the socket is blocked.
add_deny_commandsCommand names (basename only) to block execution of. Complements add_deny_access for defense-in-depth against tools like docker, kubectl, etc.
override_denyPaths to exempt from deny groups (must also have a matching grant)

Adding a Deny Rule to an Inherited Profile

Block a specific path that a base profile would otherwise allow:
{
  "meta": {
    "name": "claude-deny-git",
    "version": "1.0.0"
  },
  "extends": "claude-code",
  "policy": {
    "add_deny_access": ["$HOME/.gitconfig"]
  }
}
nono run --profile claude-deny-git -- claude

Blocking Container Access (Docker, Podman, kubectl)

Use add_deny_access and add_deny_commands together for defense-in-depth:
{
  "extends": "claude-code",
  "meta": { "name": "no-docker", "version": "1.0.0" },
  "policy": {
    "add_deny_access": ["/var/run/docker.sock"],
    "add_deny_commands": ["docker", "docker-compose", "podman", "kubectl"]
  }
}
On macOS, add_deny_access on a socket path also emits a network-outbound deny — Seatbelt classifies connect(2) as a network operation, so a file deny alone won’t block it. add_deny_commands blocks the CLI tools directly as defense-in-depth.

Adding Write-Only Access

Grant write-only access to a directory without granting read:
{
  "meta": {
    "name": "write-only-cache",
    "version": "1.0.0"
  },
  "extends": "default",
  "policy": {
    "add_allow_write": ["$HOME/.cache/my-agent-cache"]
  },
  "workdir": {
    "access": "none"
  }
}

Removing Groups from an Inherited Profile

Remove the dangerous command blocking group to allow rm, chmod, etc.:
{
  "meta": {
    "name": "no-dangerous-commands",
    "version": "1.0.0"
  },
  "extends": "default",
  "policy": {
    "exclude_groups": [
      "dangerous_commands",
      "dangerous_commands_linux",
      "dangerous_commands_macos"
    ]
  }
}

Overriding a Deny Rule for a Specific Path

Some deny groups (like deny_credentials) are marked as required and cannot be excluded. Use override_deny to punch a targeted hole through a deny group for a specific path. The path must also be explicitly granted via filesystem or policy.add_allow_*.
{
  "meta": {
    "name": "docker-agent",
    "version": "1.0.0"
  },
  "extends": "opencode",
  "filesystem": {
    "allow": ["$HOME/.docker"]
  },
  "policy": {
    "override_deny": ["$HOME/.docker"]
  }
}
This allows access to ~/.docker even though deny_credentials blocks it by default. The deny override does not implicitly grant access — the matching filesystem.allow entry is required. This is equivalent to the CLI flag --override-deny:
nono run --profile opencode --allow ~/.docker --override-deny ~/.docker -- opencode
policy fields are additive across inheritance. A child profile’s add_deny_access is merged with the base profile’s add_deny_access, and exclude_groups from both levels are combined. The filesystem section works the same way - use policy when you need deny rules or write-only access that filesystem cannot express.

Network Configuration

The network section controls network access and credential injection.
{
  "network": {
    "block": false,
    "network_profile": "claude-code",
    "allow_domain": ["my-internal-api.example.com"],
    "open_port": [3000],
    "listen_port": [8080],
    "credentials": ["openai", "anthropic"],
    "upstream_proxy": "squid.corp:3128",
    "upstream_bypass": ["git.internal.corp", "*.dev.local"],
    "custom_credentials": {
      "telegram": {
        "upstream": "https://api.telegram.org",
        "credential_key": "telegram_bot_token",
        "inject_header": "Authorization",
        "credential_format": "Bearer {}"
      }
    }
  }
}
FieldDescription
blockBlock all network access (default: false)
network_profileNetwork profile name for host filtering (e.g., minimal, claude-code, enterprise). Set to null in a child profile to clear an inherited value.
allow_domainAdditional domains to allow through the proxy
open_portLocalhost TCP ports to allow bidirectional IPC (equivalent to --open-port)
listen_portTCP ports the sandboxed child may listen on (equivalent to --listen-port)
credentialsCredential services to enable via reverse proxy (e.g., openai, anthropic)
custom_credentialsCustom credential service definitions for APIs not in the built-in list
upstream_proxyUpstream (enterprise) proxy address, e.g., squid.corp:3128
upstream_bypassDomains to bypass the upstream proxy (exact hostnames and *. wildcards)

Custom Credentials

The custom_credentials field lets you define credential services for any API:
FieldRequiredDefaultDescription
upstreamYes-Upstream URL (must be HTTPS, or HTTP for localhost)
credential_keyYes-Keystore account name
inject_headerNoAuthorizationHeader to inject credential into
credential_formatNoBearer {}Format string ({} replaced with credential)
See Credential Injection for complete documentation.

Hooks

The hooks section defines hooks that nono will automatically install for specific applications.
{
  "hooks": {
    "claude-code": {
      "event": "PostToolUseFailure",
      "matcher": "Read|Write|Edit|Bash",
      "script": "nono-hook.sh"
    }
  }
}
Hook installation is idempotent - nono only installs or updates when needed.

Interactive Mode (deprecated)

The interactive field is parsed for backward compatibility but ignored. Supervised mode (the default) preserves the TTY, making this field unnecessary.
{
  "meta": { "name": "my-agent" },
  "interactive": true
}

Rollback Exclusions

The rollback section controls which files are excluded from atomic rollback snapshots:
{
  "rollback": {
    "exclude_patterns": ["node_modules", ".next", "__pycache__", "target"],
    "exclude_globs": ["*.tmp.[0-9]*.[0-9]*"]
  }
}
These exclusions are combined with gitignore patterns from the working directory.

Skip Directories

Use skipdirs to extend the built-in heavy-directory skip list for pre-exec trust scanning and rollback preflight:
{
  "skipdirs": ["generated", "vendor-cache"]
}
Entries are matched as exact path component names. They do not grant or deny sandbox access; they only prune traversal during trust discovery and rollback heuristics.

Environment Variables

Profiles support these environment variables in path values:
VariableExpands To
$WORKDIRCurrent working directory (from --workdir or cwd)
$HOMEUser’s home directory
$XDG_CONFIG_HOMEXDG config directory (default: ~/.config)
$XDG_DATA_HOMEXDG data directory (default: ~/.local/share)
$XDG_STATE_HOMEXDG state directory (default: ~/.local/state)
$XDG_CACHE_HOMEXDG cache directory (default: ~/.cache)
$XDG_RUNTIME_DIRXDG runtime directory (no default; left unexpanded when unset)
$TMPDIRSystem temporary directory
$UIDCurrent user ID

Creating User Profiles

Use nono profile init to scaffold a new profile:
# Scaffold a profile that inherits from default with the node_runtime group
nono profile init my-agent --extends default --groups node_runtime

# Validate the generated profile
nono policy validate ~/.config/nono/profiles/my-agent.json

# Use the profile
nono run --profile my-agent -- my-agent-command
See Profile Authoring for the full workflow, including JSON Schema integration, editor autocomplete, and the LLM authoring guide. You can also load a profile by file path:
nono run --profile ./profiles/my-agent.json -- my-command

Overriding Built-in Profiles

CLI flags always take precedence over profile settings:
# Use claude-code profile but block network
nono run --profile claude-code --block-net -- claude

# Use claude-code profile but add a custom domain
nono run --profile claude-code --allow-domain custom-api.example.com -- claude

# Add extra directory access
nono run --profile claude-code --allow ~/other-project -- claude
To keep a base profile but clear an inherited network profile, set network.network_profile to null in the child:
{
  "meta": { "name": "claude-code-netopen" },
  "extends": "claude-code",
  "network": { "network_profile": null }
}
You can also create a user profile with the same name to override a built-in profile entirely.

Groups

Groups are named, composable collections of security rules. Profiles reference groups by name in their security.groups field.

How Groups Compose

Every profile’s effective capability set is built through composition:
((default_profile_groups + profile.security.groups) - profile.policy.exclude_groups) + profile.policy.add_* + profile.filesystem - deny_groups + profile.policy.override_deny + CLI flags
  1. default_profile_groups come from the built-in default profile
  2. profile.security.groups add additional groups on top
  3. profile.policy.exclude_groups removes groups from the composed set (exclusion wins)
  4. profile.policy.add_allow_*, profile.policy.add_deny_access, and profile.policy.add_deny_commands apply additive overrides
  5. profile.filesystem entries are additive
  6. profile.policy.override_deny punches targeted holes through deny groups (requires matching grant)
  7. CLI overrides (--allow, --read, --override-deny, etc.) are applied last
Exclusions are applied after group addition. If the same group appears in both security.groups and policy.exclude_groups, the exclusion wins.

Group Taxonomy

Groups use a structured allow/deny taxonomy:

Allow Operations

FieldMeaning
allow.readRead-only access to listed paths
allow.writeWrite-only access (no read)
allow.readwriteBoth read and write access

Deny Operations

FieldMeaning
deny.accessBlock both read and write content (metadata like stat still allowed)
deny.unlinkBlock file deletion globally
deny.commandsBlock execution of listed commands

Other

FieldMeaning
symlink_pairsmacOS symlink-to-target path mappings (e.g., /etc to /private/etc)
platformRestrict group to "macos" or "linux" only

Built-in Groups

nono ships with 32 built-in groups: Deny groups (block sensitive content):
  • deny_credentials - SSH keys, cloud credentials, GPG keys, container and package manager tokens
  • deny_keychains_macos - macOS Keychain databases, 1Password, password-store
  • deny_keychains_linux - Linux keyring, 1Password, password-store
  • deny_browser_data_macos - Browser data on macOS (Chrome, Firefox, Safari, Edge, Arc, Brave)
  • deny_browser_data_linux - Browser data on Linux (Chrome, Firefox, Edge, Brave)
  • deny_macos_private - macOS Messages, Mail, Cookies, MobileSync
  • deny_shell_history - Shell history files (.bash_history, .zsh_history, .python_history)
  • deny_shell_configs - Shell config files that may contain API keys (.bashrc, .zshrc, .profile, .env)
Protection groups:
  • unlink_protection - Prevents file deletion, with override for user-writable paths
  • dangerous_commands - Blocks rm, dd, chmod, sudo, mkfs, pip, npm, kill, etc.
  • dangerous_commands_macos - macOS-specific: srm, brew, launchctl
  • dangerous_commands_linux - Linux-specific: shred, mkfs.*, fdisk, systemctl, apt, yum, dnf, pacman
System path groups (grant necessary system access):
  • system_read_macos - macOS system libraries, frameworks, dyld cache
  • system_read_linux - Linux system libraries, locale data
  • system_write_macos - macOS temp directories, cache paths
  • system_write_linux - Linux temp directories
Cache groups (user-level caches and state):
  • user_caches_macos - ~/Library/Caches, ~/Library/Logs, ~/Library/Preferences
  • user_caches_linux - ~/.cache, ~/.local/state
Runtime groups (language toolchain paths):
  • node_runtime - nvm, fnm, npm, pnpm, volta
  • rust_runtime - rustup, cargo
  • python_runtime - pyenv, conda, pip, uv
  • go_runtime - ~/go, /usr/local/go
  • nix_runtime - Nix profile paths, /nix/var (Linux only)
  • user_tools - Local bins, .desktop files, man pages, shell completions
  • homebrew - /opt/homebrew, /usr/local/Cellar, /usr/local/opt (macOS only)
Tool-specific groups (paths for specific applications):
  • claude_code_macos - macOS Keychain for Claude Code credential storage
  • claude_code_linux - ~/.local/share/claude
  • claude_cache_linux - ~/.cache/claude-cli-nodejs
  • codex_macos - macOS Keychain for Codex credential storage
  • opencode_linux - ~/.opencode/bin (Linux only, needed for Landlock exec)
  • vscode_macos - ~/.vscode, ~/Library/Application Support/Code
  • vscode_linux - ~/.vscode, ~/.config/Code
Command blocking is a best-effort surface-level control. It matches against the executable name being invoked directly. A process can bypass this by calling the equivalent syscall from within an allowed interpreter (e.g., os.remove() in Python, fs.unlinkSync() in Node.js) or by invoking a renamed copy of the binary. For hard filesystem protection, rely on the kernel-enforced deny groups and unlink_protection, which cannot be bypassed from userspace regardless of how the operation is invoked.

Platform-Specific Groups

Groups with a platform field only apply on that OS:
{
  "system_read_macos": {
    "platform": "macos",
    "allow": {
      "read": ["/System/Library", "/usr/lib"]
    }
  }
}
Groups without a platform field (like deny_credentials) apply on all platforms. Platform-specific groups use _macos or _linux suffixes by convention.

Platform Differences

macOS (Seatbelt) supports full deny-within-allow semantics. A group can allow /Users but deny /Users/luke/.ssh and the deny takes precedence. Linux (Landlock) is strictly allow-list. Deny groups are implemented as exclusion filters - broad allow groups that overlap deny paths will generate warnings. Avoid granting access to parent directories of deny paths on Linux.

Built-in Profiles

default

The base profile that all other profiles extend. Provides system path access, deny groups for sensitive content, and dangerous command blocking. Does not grant working directory access or any user-specific paths. Groups: deny_credentials, deny_keychains_macos, deny_keychains_linux, deny_browser_data_macos, deny_browser_data_linux, deny_macos_private, deny_shell_history, deny_shell_configs, system_read_macos, system_read_linux, system_write_macos, system_write_linux, user_tools, homebrew, dangerous_commands, dangerous_commands_macos, dangerous_commands_linux Network: Allowed CWD: None

claude-code

nono run --profile claude-code -- claude
Groups: claude_code_macos, claude_code_linux, user_caches_macos, claude_cache_linux, node_runtime, rust_runtime, python_runtime, vscode_macos, vscode_linux, nix_runtime, git_config, unlink_protection (plus default groups) Filesystem: ~/.claude (read+write), ~/.claude.json and ~/.claude.json.lock (read+write), plus platform-specific Claude Code and VS Code paths from the matching _macos or _linux groups, and git config files from git_config group Network: Allowed CWD: Read+write Special: Auto-installs Claude Code hooks. OAuth2 login support via open_urls (allows https://claude.ai and localhost). Profile opts into allow_launch_services for macOS browser opening.

codex

nono run --profile codex -- codex
Groups: codex_macos, node_runtime, rust_runtime, python_runtime, nix_runtime, git_config, unlink_protection (plus default groups) Filesystem: ~/.codex (read+write), plus git config files from git_config group Network: Allowed CWD: Read+write Special: OAuth2 login support via open_urls (allows https://auth.openai.com and localhost). Profile opts into allow_launch_services.

opencode

nono run --profile opencode -- opencode
Groups: user_caches_macos, user_caches_linux, node_runtime, opencode_linux, git_config, unlink_protection (plus default groups) Filesystem: ~/.config/opencode, ~/.cache/opencode, ~/.local/share/opencode, ~/.local/share/opentui (all read+write), plus git config files from git_config group Network: Allowed CWD: Read+write

openclaw

nono run --profile openclaw -- openclaw
Groups: node_runtime (plus default groups) Filesystem: ~/.openclaw, ~/.config/openclaw, ~/.local, $TMPDIR/openclaw-$UID (all read+write) Network: Allowed CWD: Read-only

swival

nono run --profile swival -- swival
Groups: python_runtime, node_runtime, user_caches_macos, user_caches_linux, git_config, unlink_protection (plus default groups) Filesystem: ~/.config/swival, ~/.local/share/swival (read+write), plus git config files from git_config group Network: Allowed CWD: Read+write

python-dev

nono run --profile python-dev -- my-python-app
Groups: python_runtime (plus default groups) Network: Allowed, with developer network profile for host filtering CWD: Read+write

node-dev

nono run --profile node-dev -- npm start
Groups: node_runtime (plus default groups) Network: Allowed, with developer network profile for host filtering CWD: Read+write

go-dev

nono run --profile go-dev -- go run .
Groups: go_runtime (plus default groups) Network: Allowed, with developer network profile for host filtering CWD: Read+write

rust-dev

nono run --profile rust-dev -- cargo run
Groups: rust_runtime (plus default groups) Network: Allowed, with developer network profile for host filtering CWD: Read+write

Requesting New Built-in Profiles

If you’d like a built-in profile for a tool not listed here:
  1. Open an issue on the nono GitHub repository
  2. Include:
    • Tool name and repository URL
    • Required filesystem access patterns
    • Network requirements
    • Any special considerations
Built-in profiles are reviewed for security before inclusion.