The nono policy command lets you inspect the group-based security policy, explore profiles, compare configurations, and validate user-defined profiles. Every subcommand supports --json for programmatic consumption.
Commands
nono policy groups
List all policy groups or show details for a specific group.
# List groups for the current platform
nono policy groups
# List groups for all platforms
nono policy groups --all-platforms
# Show details for a specific group
nono policy groups deny_credentials
# Machine-readable output
nono policy groups --json
Example output:
nono policy: 21 groups
claude_code_macos Claude Code macOS-specific state and credential paths macos
deny_credentials Block access to cryptographic keys, tokens, and cloud credentials cross-platform required
deny_shell_history Block access to shell command history files cross-platform required
node_runtime Node.js runtime and package manager paths cross-platform
system_read_macos macOS system paths required for executables to function macos
...
Detailed view shows every path in the group, with variables expanded to their actual values:
nono policy: group 'deny_credentials'
Description: Block access to cryptographic keys, tokens, and cloud credentials
Platform: cross-platform
Required: yes
deny.access:
~/.ssh -> /Users/luke/.ssh
~/.aws -> /Users/luke/.aws
~/.config/gcloud -> /Users/luke/.config/gcloud
~/.docker -> /Users/luke/.docker
~/.git-credentials -> /Users/luke/.git-credentials
...
Options
| Option | Description |
|---|
[NAME] | Group name to show details for (omit to list all) |
--json | Output as JSON |
--all-platforms | Show groups for all platforms, not just the current one |
nono policy profiles
List all available profiles, showing built-in and user-defined profiles separately. User profiles that shadow a built-in name are flagged.
nono policy profiles
nono policy profiles --json
Example output:
nono policy: 15 profiles
Built-in:
claude-code Anthropic Claude Code CLI agent extends default
codex OpenAI Codex CLI agent extends default
default Default conservative base profile
opencode OpenCode AI coding assistant extends default
python-dev Python SDK development profile extends default
...
User (~/.config/nono/profiles/):
my-agent Custom agent profile extends default
...
Malformed user profiles are surfaced with their error instead of being silently hidden:
User (~/.config/nono/profiles/):
broken-profile [error: Profile error: missing field `meta`]
Load precedence is user-first. If a user profile file exists at ~/.config/nono/profiles/claude-code.json, it shadows the built-in claude-code profile. The profiles command reports the actual source: "user (overrides built-in)" in JSON output, and lists the profile under the User section.
Options
| Option | Description |
|---|
--json | Output as JSON |
nono policy show
Display a fully resolved profile, including all inherited groups, filesystem grants, network configuration, hooks, and rollback settings.
# Show a built-in profile
nono policy show default
# Show a user profile
nono policy show my-agent
# Show a profile by file path
nono policy show ./profiles/custom.json
# Show raw paths (before variable expansion)
nono policy show claude-code --raw
# Machine-readable output
nono policy show claude-code --json
Example output:
nono policy: profile 'claude-code'
Description: Anthropic Claude Code CLI agent
Extends: default
Security groups:
deny_credentials
deny_keychains_macos
node_runtime
python_runtime
rust_runtime
unlink_protection
...
Filesystem:
allow (r+w):
$HOME/.claude -> /Users/luke/.claude
read_file:
$HOME/.gitconfig -> /Users/luke/.gitconfig
Workdir access: ReadWrite
Rollback exclusions:
node_modules
target
__pycache__
glob: *.tmp.[0-9]*.[0-9]*
Open URLs:
localhost allowed
https://claude.ai
The --raw flag shows path variables as written in the profile (e.g., $HOME/.claude) rather than their expanded values. This is useful for understanding what a profile declares versus what it resolves to on a specific machine.
Options
| Option | Description |
|---|
<PROFILE> | Profile name or file path |
--json | Output as JSON |
--raw | Show raw paths before variable expansion |
nono policy diff
Compare two profiles side-by-side. Shows additions, removals, and changes across all profile sections: groups, filesystem, policy patches, security settings, network, hooks, rollback, open URLs, env credentials, and custom credentials.
# Compare two built-in profiles
nono policy diff default claude-code
# Compare a user profile against a built-in
nono policy diff default ./my-profile.json
# Machine-readable output
nono policy diff default claude-code --json
Example output:
nono policy: diff 'default' vs 'claude-code'
Groups:
+ claude_code_macos
+ node_runtime
+ python_runtime
+ rust_runtime
+ unlink_protection
+ user_caches_macos
Filesystem:
+ allow $HOME/.claude
+ read_file $HOME/.gitconfig
+ read_file $HOME/.gitignore_global
Workdir:
- access: None
+ access: ReadWrite
Open URLs:
+ allow_origins https://claude.ai
- allow_localhost: false
+ allow_localhost: true
Hooks:
+ claude-code
For custom credentials and hooks, the diff detects value-level changes when two profiles share the same key but differ in configuration (e.g., different upstream URL or inject_mode):
Custom credentials:
~ my-api (changed)
- upstream: https://old-api.example.com
+ upstream: https://new-api.example.com
When profiles are identical, the output shows (no differences).
Options
| Option | Description |
|---|
<PROFILE1> | First profile name or path |
<PROFILE2> | Second profile name or path |
--json | Output as JSON |
nono policy validate
Validate a user profile JSON file against the embedded policy. Catches common mistakes before they cause runtime failures.
nono policy validate ~/my-profile.json
nono policy validate ./profiles/custom.json --json
Checks performed:
| Check | Severity | Description |
|---|
| JSON syntax | Error | File must be valid JSON and deserialize into a profile |
| Inheritance | Error | extends target must be a loadable profile |
| Group references | Error | Every group in security.groups must exist in policy.json |
| Required group exclusion | Error | policy.exclude_groups cannot exclude required groups |
| Empty paths | Warning | Empty strings in filesystem path arrays |
Example output for a valid profile:
nono policy: validating /Users/luke/my-profile.json
[ok] JSON syntax valid
[ok] All 3 group references valid
Result: valid
Example output for an invalid profile:
nono policy: validating /Users/luke/bad-profile.json
[ok] JSON syntax valid
[err] Group 'nonexistent_group' not found in policy.json
Result: invalid (1 error)
The command exits with a non-zero status when validation fails, making it suitable for CI pipelines.
Options
| Option | Description |
|---|
<FILE> | Profile JSON file to validate |
--json | Output as JSON |
JSON Output
All subcommands support --json for integration with scripts, CI pipelines, and tools like jq.
# List groups and extract names
nono policy groups --json | jq '.[].name'
# Check if a profile has network blocked
nono policy show my-profile --json | jq '.network.block'
# Get added groups between two profiles
nono policy diff default claude-code --json | jq '.groups.added'
# Validate in CI and check result
nono policy validate ./profile.json --json | jq '.valid'
Use Cases
Auditing security posture
Review exactly what a profile grants before deploying it:
# What does this profile actually allow?
nono policy show claude-code
# What groups protect sensitive data?
nono policy groups deny_credentials
# How does our custom profile differ from the built-in?
nono policy diff claude-code ./our-custom-profile.json
Debugging sandbox failures
When a sandboxed process fails because a path is blocked, trace the policy:
# What groups apply to the current platform?
nono policy groups
# Does the profile include the needed runtime group?
nono policy show my-profile | grep python_runtime
# Compare against a working profile
nono policy diff my-profile claude-code
CI/CD validation
Validate custom profiles before they ship:
# Validate all profile files in a directory
for f in profiles/*.json; do
nono policy validate "$f" --json || exit 1
done
Detecting user overrides
Check whether built-in profiles have been shadowed by user files:
# JSON output flags the actual source
nono policy profiles --json | jq '.[] | select(.source | contains("overrides"))'