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: 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 {"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: 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 (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: ["/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)
  1. Hardcoded defaults — built-in safe base, always present
  2. ~/.node9/config.json — global config
  3. ./node9.config.json — project config
  4. Shields layer~/.node9/shields.json (active shields only)
  5. Advisory defaultsallow-rm-safe-paths, review-rm (appended last)
  6. NODE9_MODE env var — overrides settings.mode only

Per-field merge behavior:

FieldHow it merges
settings.*Override — project wins over global wins over default
approversMerge — field-by-field (project can override individual approvers)
sandboxPaths, ignoredToolsAdditive — all layers concatenated, deduplicated
dangerousWordsReplace — project/global replaces defaults entirely
toolInspectionMerge — later layers add/overwrite individual keys
smartRulesAdditive + 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:

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)

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.