Configuration · Reference

Every field in config.yaml.

Nio reads ~/.nio/config.yaml (or $NIO_HOME/config.yaml). Two top-level sections: guard for the runtime pipeline + scan rules, collector for OTLP telemetry. The bundled template at config.default.yaml has inline comments for every field.

Where it lives

  • Default path: ~/.nio/config.yaml
  • Override directory: set NIO_HOME environment variable
  • Reset to defaults: /nio reset or setup.sh --reset-config

You can edit the file directly or use /nio config <level> to change the protection level without opening an editor.

guard

guard.protection_level

Type: string · Default: balanced · Values: strict | balanced | permissive

Decision aggressiveness. Determines the score thresholds for allow/confirm/deny. See Scoring → thresholds for the full matrix.

guard:
  protection_level: balanced

guard.confirm_action

Type: string · Default: allow · Values: allow | deny | ask

What to do when the guard's decision is confirm but the platform has no interactive prompt (e.g. OpenClaw). allow lets it through and writes a warning to the audit log; deny blocks; ask uses the platform confirm if available, else falls back to allow.

guard.file_scan_rules

Type: object · Default: all empty arrays

Extra regex patterns added to the named scan module. Keys are module names; values are arrays of regex pattern strings. Modules:

  • shell_exec — feeds SHELL_EXEC [high] and AUTO_UPDATE [critical]
  • remote_loader — feeds REMOTE_LOADER [critical]
  • secrets — feeds READ_ENV_SECRETS [med], READ_SSH_KEYS/READ_KEYCHAIN/PRIVATE_KEY_PATTERN [critical]
  • obfuscation — feeds OBFUSCATION [high]
  • prompt_injection — feeds PROMPT_INJECTION [critical]
  • exfiltration — feeds NET_EXFIL_UNRESTRICTED [high], WEBHOOK_EXFIL [critical]
  • trojan — feeds TROJAN_DISTRIBUTION [critical], SUSPICIOUS_PASTE_URL [high], SUSPICIOUS_IP/SOCIAL_ENGINEERING [med]
guard:
  file_scan_rules:
    shell_exec:
      - "my_unsafe_exec\\("
    remote_loader:
      - "loadFromCDN\\("

guard.action_guard_rules

Type: object

Extra patterns for Phase 2 runtime action analysis. Two field shapes:

  • String fields — plain strings, matched as described per field (case-insensitive substring / prefix / domain suffix). No regex metacharacters, no escaping.
  • Regex fields — names ending in _patterns. Each entry is either a bare regex string or the /pattern/flags form (flags: g i m s u y). YAML requires backslashes to be escaped — write \\d, \\/, \\. inside quotes.

All fields extend the built-in detection lists; they never replace them. Every match produces a Phase 2 finding at the severity shown in [brackets].

dangerous_commands [critical]

Case-insensitive substring match against the full command line. Use for fixed literal fragments to hard-block.

action_guard_rules:
  dangerous_commands: ["format c:", "shutdown -h now"]

dangerous_patterns [critical]

Regex against the full command line. Use when you need alternation, anchors, or character classes.

action_guard_rules:
  dangerous_patterns:
    - "/rm\\s+-rf\\s+\\//"
    - "/curl\\s+.*\\|\\s*(ba)?sh/i"

sensitive_commands [high]

Commands that read sensitive data. Case-insensitive substring match. Built-ins already cover /etc/passwd, ~/.ssh, ~/.aws, printenv, etc.

system_commands [high]

Word-boundary match on the program name (start of line or after a space). Add a trailing space for short names to prevent prefix hits (e.g. "su " not "su").

network_commands [medium]

Same word-boundary rule. Flags any use regardless of target.

webhook_domains [high]

Hostnames matched exactly OR as suffix (foo.example.com is covered by example.com). No scheme, no path, no port.

webhook_domains: ["webhook.site", "requestbin.net", "ngrok-free.app"]

sensitive_paths [critical]

Substring match with a specific convention: the matcher prepends / to each pattern and tests normalized.includes("/" + pattern) || normalized.endsWith(pattern). Input is normalized first: ~/ expands to /HOME/, backslashes become forward slashes.

Two supported shapes:

  • Directory fragment — DO NOT include a leading slash; the matcher adds one. Use etc/, raw_files/, .ssh/. Writing /etc/ is a common mistake — it becomes //etc/ internally and almost never matches.
  • Filename suffix — matched via endsWith (e.g. .env, id_rsa, credentials.json, .env.prod).
Known gap

A bare relative path like raw_files/foo.txt (no leading /) does NOT match a raw_files/ pattern. Use sensitive_path_patterns with "/^raw_files\\//" for that.

sensitive_path_patterns [critical]

Regex against the same normalized path. Use for dynamic segments, relative paths, or flag-based matching.

sensitive_path_patterns:
  - "/^\\/abc\\/[^/]+\\/def/"   # /abc/<id>/def
  - "/^raw_files\\//"           # bare relative paths
  - "/\\.env\\.[a-z]+$/i"       # .env.prod, .env.staging

secret_patterns [high]

Regex against HTTP request body previews on network_request actions. Use for internal token formats the built-in detector doesn't know about.

secret_patterns:
  - "/ACME_[A-Z0-9]{32}/"
  - "/internal-token:\\s*[\\w-]+/i"

guard.llm_analyser

Type: object

Phase 5 — semantic analysis using Claude. Disabled by default. Requires an Anthropic API key.

FieldTypeDefaultNote
enabledbooleanfalseMaster switch.
api_keystring""Anthropic API key.
modelstring"claude-sonnet-4-20250514"Model identifier.
max_input_tokensnumber50000Per-scan input budget.

guard.external_analyser

Type: object

Phase 6 — external scoring API. Disabled by default. Requires an endpoint.

FieldTypeDefaultNote
enabledbooleanfalseMaster switch.
endpointstring""HTTP URL. Use http://localhost:9090 for the bundled mock.
api_keystring""Bearer token (optional).
timeoutnumber3000Request timeout (ms).

guard.allowed_commands

Type: string[] · Default: []

Phase 1 allowlist. Command prefixes treated as safe — additional to the built-in list (ls, git status, npm test, etc.).

allowed_commands: ["npm run", "pnpm "]

guard.allowlist_mode

Type: string · Default: continue · Values: continue | exit

What happens when a command matches an allowlist entry. continue records a hint but still runs Phases 2–6 (so external/LLM policy can't be bypassed). exit returns immediately — fastest path, skips later phases.

guard.permitted_tools

Type: object · Default: { claude_code: [], codex: [], openclaw: [], hermes: [], mcp: [] }

Phase 0 strict allowlist, per platform. When non-empty for a namespace, ONLY listed tools pass on that platform; everything else is denied. The mcp key is platform-agnostic and applies whenever the incoming tool is an MCP tool.

MCP entries accept either a bare local name (HassTurnOn — matches any MCP server) or server-qualified (hass__HassTurnOn — that server only). Matching is case-insensitive.

The mcp list also catches indirect MCP invocations embedded in shell commands — Phase 0 unwraps the command (16 layers covering shells, heredocs, evals, encodings, package wrappers, ssh/docker exec, …) and runs detectors against every fragment (mcporter, curl/wget/HTTPie, language-runtime one-liners, TCP/socket multiplexers, stdio JSON-RPC pipes, package runners, etc.), mapping each hit back to {server, tool} via the MCP server registry. The full capture model lives at Phase 0 — Tool Gate · MCP Tool Routing.

guard.blocked_tools

Type: object · Default: { claude_code: [], codex: [], openclaw: [], hermes: [], mcp: [] }

Same shape as permitted_tools; listed tools are unconditionally blocked. mcp covers MCP tools on every platform in one place. Takes precedence over permitted_tools.

guard.mcp_servers

Type: object · Default: {}

Manual override / addition for the MCP server registry. Phase 0 auto-discovers servers from ~/.claude.json, Claude Desktop, ~/.hermes/config.yaml, ~/.codex/config.toml, and ~/.openclaw/openclaw.json; this field is for SaaS or otherwise non-discoverable servers. Each entry's key is the server name; its value indexes the server by every reachable handle so detectors can route indirect calls back to it.

guard:
  mcp_servers:
    hass:
      urls:        ['http://localhost:5173/mcp', 'wss://saas.example/mcp']
      sockets:     ['/tmp/mcp-hass.sock']
      binaries:    ['mcp-server-hass']
      cliPackages: ['@hass/mcp-cli']

All four arrays are optional. URLs are normalized (case-insensitive host, trailing slash stripped); origin matching covers any sub-path of the registered URL. Binary and CLI-package lookups are case-insensitive and basename-aware. See Phase 0 — Tool Gate · MCP Server Registry for full semantics.

guard.native_tool_mapping

Type: object

Native tool → action type classification, per platform. Not a third allow/deny list — this is the table that decides which Phase 1–6 rule set runs for each platform-native tool. Mapped tools enter Phase 1–6 with the chosen action type; tools absent from the map skip Phase 1–6 (auto-allow, log only). MCP tools are dynamic and not categorised here.

native_tool_mapping:
  claude_code:
    Bash: exec_command
    Write: write_file
    Edit: write_file
    WebFetch: network_request
    WebSearch: network_request
  codex:
    Bash: exec_command   # Codex's only native tool — writes/reads/fetches go through shell
  openclaw:
    exec: exec_command
    write: write_file
    web_fetch: network_request
    browser: network_request
  hermes:
    terminal: exec_command
    exec: exec_command
    shell: exec_command
    write_file: write_file
    patch: write_file
    read_file: read_file
    fetch: network_request
    http_request: network_request

guard.scoring_weights

Type: object

Per-phase weights for the final score aggregation. final = Σ(wi × si) / Σ(wi) across phases that ran.

FieldDefaultPhase
runtime1.0Phase 2 — pattern matching
static1.0Phase 3 — static rules on file content
behavioural2.0Phase 4 — AST dataflow
llm1.0Phase 5 — LLM semantic
external2.0Phase 6 — external scoring API

collector

Telemetry. Disabled when endpoint is empty AND all local options are off.

See Collector Signals for the full per-instrument / per-span / per-event schema that lands in OTLP and the local audit log.

collector.endpoint

Type: string · Default: "http://localhost:4318"

OTLP base URL. /v1/traces, /v1/metrics, /v1/logs are appended automatically per signal.

collector.api_key

Type: string · Default: ""

Bearer token for OTLP endpoint authentication.

collector.headers

Type: object · Default: {}

Extra request headers sent to the OTLP endpoint. Values are stringified before export.

collector:
  headers:
    x-event-pipeline-id: "a7f966c2-02a1-46f3-92cf-51d6889c52f4" 

collector.timeout

Type: number · Default: 5000

OTLP export timeout in milliseconds.

collector.protocol

Type: string · Default: http · Values: http | grpc

Transport. http uses HTTP/JSON on port 4318; grpc uses gRPC on port 4317.

collector.metrics

FieldTypeDefaultNote
enabledbooleantrueOTLP metrics export.

Metrics are exported via OTLP only — there is no local metrics file. Hook event audit records live in the audit log (see collector.logs).

collector.traces

FieldTypeDefaultNote
enabledbooleantrueSpans for tool calls, turns, tasks.

collector.logs

FieldTypeDefaultNote
enabledbooleantrueOTEL Logs export.
localbooleantrueLocal JSONL backup.
pathstring"~/.nio/audit.jsonl"The file /nio report reads.
max_size_mbnumber100Rotation threshold.

Environment variables

VariableDefaultEffect
NIO_HOME~/.nioWhere Nio reads config.yaml, writes audit log + metrics.
CLAUDE_CONFIG_DIR~/.claudeUsed by setup.sh --cc-home resolution.
OPENCLAW_STATE_DIR~/.openclawUsed by setup.sh --openclaw-home resolution.