Configuration · node9 documentation
Configuration Guide
How to map AI requests to security rules.
Layered Configuration
Node9 merges settings from
~/.node9/config.json (Global/Machine level) with ./node9.config.json (Project level). Project rules add to Global rules, they do not replace them.Two-layer protection model
Node9 has two layers. Layer 1 (built-in smart rules) is always on — zero config needed.Layer 2 (shields) is opt-in per service via
node9 shield enable. Custom rules in node9.config.json are for power users who need something beyond what shields provide.Policy Config Fields
smartRulesPrimary
The main way to define policy. Matches on raw tool arguments using structured conditions — regex, substring, or existence checks on any field. Supports complex logic like "block DELETE only when no WHERE clause is present."
Three verdicts:
Three verdicts:
review (approval prompt), block (hard deny), or allow (skip all further checks). First matching rule wins.Built-in defaults (force push, SQL without WHERE, curl-pipe-shell) are always active. Shield rules and your custom rules are appended after them.
toolInspectionRequired for shells
AI agents send JSON, not raw strings. Node9 needs to know which field holds the actual command.
Example: Claude sends
Common mappings are pre-configured for
Example: Claude sends
{"command": "npm install"} for the bash tool. Configure: {"bash": "command"} so Node9 knows where to look.Common mappings are pre-configured for
bash, shell, and postgres:query.ignoredToolsFast path
Tools in this list bypass all policy checks and are approved instantly. Evaluated first — nothing else runs if the tool matches.
Supports glob patterns:
Supports glob patterns:
list_*, read_*, get_*. Read-only tools are pre-configured as ignored. Add your own as needed.dangerousWordsSafety net
A last-resort keyword blocklist. If a command contains any of these words and no smart rule allowed it first, it is flagged for review.
Best used sparingly for truly catastrophic words (
Best used sparingly for truly catastrophic words (
mkfs, shred). For context-aware blocking (e.g. only flag DROP inside SQL), use a smart rule instead.Unlike other fields, a project config replaces the global list rather than extending it.
sandboxPathsSafe zones
If all path tokens in a tool call are inside a sandboxed directory, the call is approved immediately without triggering dangerousWords.
Example:
Example:
["/tmp/**", "**/test-results/**"] — anything under /tmpis safe for the AI to write to freely.Why do I need 'toolInspection'?
Without
toolInspection, Node9 doesn't know how to read the AI's custom tool format. If Node9 can't find the command string, it can't apply your Rules or check for Dangerous Words! Always map your agent's shell tool to its payload parameter.Config Hierarchy
Config merge order (lowest → highest priority)
- Hardcoded defaults — built-in safe base, always present
~/.node9/config.json— global config./node9.config.json— project config- Shields layer —
~/.node9/shields.json(active shields only) - Advisory defaults —
allow-rm-safe-paths,review-rm(appended last) NODE9_MODEenv var — overridessettings.modeonly
Per-field merge behavior:
| Field | How it merges |
|---|---|
| settings.* | Override — project wins over global wins over default |
| approvers | Merge — field-by-field (project can override individual approvers) |
| sandboxPaths, ignoredTools | Additive — all layers concatenated, deduplicated |
| dangerousWords | Replace — project/global replaces defaults entirely |
| toolInspection | Merge — later layers add/overwrite individual keys |
| smartRules | Additive + ordered — defaults → global → project → shields → advisory; first match wins |
| snapshot.* | Additive — all layers concatenated |
smartRules security guarantee
Because built-in defaults are first in the array, built-in
block rules (like block-rm-rf-home) always fire before any user rule. A project allow rule can never bypass Layer 1 protection. Advisory rules (allow-rm-safe-paths, review-rm) are last so user rules can override default rm behaviour.Cloud Policy — a second gate, not a merged layer
The Cloud Policy Studio is not merged into your local config. It runs per-request on the backend, but only for calls that local policy did not already allow.
Local policy has two outcomes — allow or flag for review. There is no local "block". The cloud only ever sees flagged calls:
For flagged calls, the cloud can approve or deny — it is a full organizational override in both directions. If
Local policy has two outcomes — allow or flag for review. There is no local "block". The cloud only ever sees flagged calls:
Local → allow → Done. Cloud is never contacted.
Local → review → Cloud → approve → call goes through
Cloud → deny → call is rejected
Cloud → pending → human approval (Slack / dashboard)
Local → review → Cloud → approve → call goes through
Cloud → deny → call is rejected
Cloud → pending → human approval (Slack / dashboard)
For flagged calls, the cloud can approve or deny — it is a full organizational override in both directions. If
approvers.cloud is false or there is no API key, this gate is skipped and local approvers (native, browser, terminal) handle the flagged call instead.Config File Reference
node9.config.json — Full Example
{
"settings": {
"mode": "standard",
"approvalTimeoutMs": 0,
"approvers": {
"native": true,
"browser": true,
"cloud": false,
"terminal": true
}
},
"policy": {
"sandboxPaths": [
"/tmp/**", "**/sandbox/**"
],
"ignoredTools": [
"my_custom_read_tool"
],
"toolInspection": {
"my_shell_tool": "command"
},
"smartRules": [
{
"name": "block-prod-deploy",
"tool": "bash",
"conditions": [
{ "field": "command", "op": "matches", "value": "kubectl.*--namespace=production" }
],
"verdict": "block",
"reason": "Production deploys require a manual release process"
}
]
}
}You only need to configure what differs from the defaults. Built-in rules (git, SQL, shell safety) and shield rules (node9 shield enable postgres) are applied automatically.Global Settings (~/.node9/config.json)
Machine-level settings
The global config at
~/.node9/config.json supports all policy fields above, plus these machine-level settings:{
"settings": {
"mode": "standard",
"autoStartDaemon": true,
"approvalTimeoutMs": 0,
"approvers": {
"native": true,
"browser": true,
"cloud": false,
"terminal": true
}
}
}approvers controls which channels are allowed to approve flagged tool calls. Set cloud to true (via node9 login <key>) to enable Mission Control approvals. Set it to false (via node9 login --local) to keep all decisions on-machine.approvalTimeoutMs — auto-deny after N milliseconds if no human responds. Set to 0 (default) to wait indefinitely. Example: 30000 for a 30-second timeout. Useful for unattended runs where a hung approval would block the agent forever.Approvers
nativeOS Dialog
Shows a native OS approval dialog (macOS/Linux). Fast and non-blocking. Disable if you prefer terminal or browser approvals only.
browserDashboard
Routes flagged calls to the local Node9 browser dashboard (
http://localhost:7391). The daemon auto-starts when needed. Good for reviewing with full context.cloudMission Control
Streams flagged calls to Mission Control for remote or team approval. Requires a valid API key (
node9 login <key>). Set by node9 login --local to false.terminalCLI Prompt
Falls back to an interactive
y/n prompt in the terminal when no other approver is available and the process is attached to a TTY.