Skip to content

Security Model

Netclaw is default-deny. Every tool call, file access, and shell command starts blocked and must be explicitly permitted. Any policy failure — invalid config, engine exception, unrecognized channel — results in deny.

Audiences control what’s allowed. A four-layer invocation stack enforces it. You pick a deployment posture during netclaw init, and it governs every permission decision the daemon makes.

Netclaw classifies each inbound message into one of three audiences based on the channel it arrived on:

ChannelAudience
TUI (netclaw chat), SignalR (real-time web transport), headless (no UI, daemon-only), console, manualPersonal
Slack, Discord, reminders, timersTeam
Unknown channel typePublic

Personal is the most permissive, Public the most restrictive. Unknown channels resolve to Public. If netclaw can’t identify who’s talking, it assumes the worst.

During netclaw init, you pick a deployment posture that sets the baseline trust level:

Security posture selection during netclaw init

The posture selection screen during netclaw init. Personal mode enables shell access with approval gates; Team and Public disable shell entirely.

PostureShell AccessDefault Behavior
PersonalEnabled (with approval gates)All tools available, all MCP servers, unrestricted filesystem
TeamOffAllowlisted tools only, no MCP servers, session-scoped filesystem (temp directory per session, wiped on end)
PublicOffMinimal tool set, no MCP servers, session-scoped filesystem (temp directory per session, wiped on end)

With no config file at all, netclaw defaults to Public posture with shell disabled — the most restrictive option.

Posture and audience are related but distinct. Posture is a deployment-wide setting you choose once. Audience is resolved per-message based on channel. A Team-posture deployment still classifies TUI sessions as Personal audience.

Tools, filesystem access, and attachment policies differ by audience:

AudienceToolsMCP ServersMemoryFilesystemAttachment Types
PersonalAllAllFullUnrestrictedImage, PDF, Document, Archive, Media, Other
Teamfile_read, file_list, file_write, file_edit, attach_file, web search/fetch, skills, reminders, set_working_directoryNoneFullSession-scoped onlyImage, PDF, Document, Archive, Media
Publicfile_read, file_list, attach_fileNoneDisabledSession-scoped onlyImages only

Public is read-only — it reads, lists, and attaches files, but can’t write, search the web, or manage skills. Team adds writes and edits, web search and fetch, skill management, and reminders. The two grants cascade, so everything Public can do, Team can too. Filesystem scope is the hard boundary underneath: Public and Team both confine every read and write to a session-scoped temp directory that’s wiped on session end, so even a re-granted write tool can’t widen what the agent reaches. Personal gets the full tool surface and unrestricted filesystem access. The split is a per-audience default in netclaw.json — adjust each audience’s AllowedTools list if it doesn’t suit your deployment.

Memory is hard-disabled for Public — not just defaulted off. Public sessions get no automatic recall before a turn, write no new memories after one, and never see the memory tools, so they can’t pull in cross-session context or leave anything behind. Team and Personal get the full memory subsystem. See Memory Model for how stored memories carry audience and boundary context.

MCP server permissions are managed separately per audience through netclaw mcp permissions. New MCP servers start with zero tool grants for all audiences — you must explicitly enable them.

Tool calls pass through four layers in sequence. If any layer rejects, execution stops.

┌─────────────────────────────────┐
│ 1. Operation Hard Deny │ ← blocks dangerous commands
├─────────────────────────────────┤
│ 2. Resource Hard Deny │ ← blocks protected file paths
├─────────────────────────────────┤
│ 3. Tool Access Grant │ ← audience-based allowlist
├─────────────────────────────────┤
│ 4. Approval Gate │ ← human confirms execution
└─────────────────────────────────┘

Shell commands hit a hard-deny list first. Blocked regardless of audience or approval status:

CategoryBlocked
Self-destructivenetclaw daemon stop, netclaw daemon kill, systemctl stop netclaw, systemctl kill netclaw, kill, killall, pkill
Privilege escalationsudo, su, doas
System destructionrm -rf /, rm -rf ~/, mkfs*
Fork bombs:(){ :|:& };:, :(){:|:&};:

Add your own patterns in ~/.netclaw/config/netclaw.json:

{
"Tools": {
"HardDenyPatterns": ["docker rm", "kubectl delete namespace"]
}
}

File access is checked against protected paths. Read and write have separate deny lists:

OperationDenied paths
Read~/.netclaw/config/secrets.json, ~/.netclaw/keys/, ~/.netclaw/config/webhooks/
Write~/.netclaw/config/secrets.json, ~/.netclaw/keys/, SQLite DB, PID file, lock file, restart manifest

~/.netclaw/config/netclaw.json is intentionally not denied — the agent can read (but not write) the main config file.

Netclaw resolves symlinks before checking, so ln -s ~/.netclaw/keys/ ./sneaky won’t bypass the policy.

The audience profile determines which tools exist at all. Public and Team audiences only see allowlisted tools (see the per-audience table). Ungranted tools are invisible to the model — they don’t appear in the tool list at all.

MCP tool grants are configured separately per server and per audience through netclaw mcp permissions.

Tools that pass layers 1-3 hit the approval gate, which prompts the operator for confirmation:

Approval gate prompt in a Slack thread

OptionBehavior
OnceJust this invocation
This chatThe rest of the current session
Always hereThe command’s verb, scoped to the current directory — persisted to ~/.netclaw/config/tool-approvals.json
Always anywhereThe command’s verb, everywhere — a global grant persisted to the same file. The broadest option; use it sparingly.
DenyBlocks this invocation

Grants are keyed by the command’s verb (e.g. git, npm), not the full command string; a compound command records one entry per verb. The full prompt shows all five options, but netclaw shows fewer when some don’t apply — a command it can’t cleanly parse (shell control flow, or unbalanced quotes) drops to just Once and Deny. Manage saved approvals with netclaw approvals.

Approval timeouts work differently depending on the channel:

  • Interactive channels (TUI, Slack, Discord, SignalR) — no response within 5 minutes triggers auto-deny. The LLM gets an error message but has no idea an approval gate rejected it.
  • Non-interactive channels (headless, reminders, webhooks) — approval gates are structurally unsupported, so all gated tools are auto-denied immediately.

Compound commands (cmd1 && cmd2 | cmd3) are split into segments, each checked independently. Unapproved patterns are batched into a single prompt.

ChannelApproval Support
TUI, Slack, Discord, SignalRYes
Headless, reminders, webhooksNo — auto-deny

The redactor strips secrets from command stdout before the model sees them. It catches:

  • API key prefixes: sk-*, Slack API tokens (xox[baprs]-...), ghp_*, AKIA*
  • Authorization: Bearer headers
  • JSON fields matching sensitive names: api_key, api-key, apikey, token, secret, password, authorization, access_token, refresh_token, client_secret, signing_key, private_key, connection_string, credential
  • Connection strings with Password= or Pwd=
  • JWT tokens and PEM private key blocks

For encryption at rest, see netclaw secrets.

Netclaw scans inbound content — tool output, file contents, MCP server responses — for injection patterns. Detection returns a risk level per match; callers (the invocation stack layers) decide whether to reject or warn.

CategoryExamplesRisk Level
Prompt injection”ignore previous instructions”High
Role resetsYouAreNow-style patternsMedium
Data exfiltration”exfiltrate secrets via curl”High
Privilege escalationAccess control list (ACL) modification, admin grantsHigh
Destructive operationsrm -rf, DROP TABLEHigh
Invisible unicodeZero-width chars, BiDi control charactersMedium
Private Use Area charsPUA codepointsLow

High-risk matches are rejected outright. Medium-risk matches generate warnings. Low-risk matches are allowed through.

Before accepting a file upload, netclaw validates:

  • MIME type must be on the audience’s allowlist
  • Maximum file size: 25 MiB
  • File headers are checked against declared MIME types (magic byte validation) — renaming malware.exe to photo.png won’t work

Exposure mode controls network reachability. It’s separate from audience and posture:

ModeScopeRequires
localLoopback onlyNothing (default)
reverse-proxyWhatever your proxy exposesTrusted proxy config + non-loopback final hop
tailscale-serveYour tailnettailscaled
tailscale-funnelPublic internettailscaled
cloudflare-tunnelPublic internetcloudflared

Internet-reachable modes force explicit confirmation during netclaw init and trigger high-risk diagnostic warnings. Changing exposure mode requires a daemon restart — not hot-reloaded.

reverse-proxy has an extra trust-boundary rule: loopback auto-auth is not inherited through the proxy path. A reverse proxy can front netclaw, but the final hop into the daemon must be a non-loopback internal address and the proxy source must be explicitly trusted.

A Personal-posture deployment exposed via Tailscale Funnel is reachable from the internet but still applies Personal audience rules to TUI sessions and Team rules to Slack. Exposure and audience are orthogonal.

When in doubt, netclaw denies:

  • Invalid ACL schema in config → daemon refuses to start
  • Policy engine throws an exception → deny
  • Unknown channel type → Public audience (most restrictive)
  • No config file → Public posture, shell off
  • Approval gate timeout → auto-deny
  • MCP server with no tool grants → all tools blocked

There is no permissive mode — access must be explicitly granted.

On the Personal audience, most tools auto-approve out of the box. Two things always prompt, regardless of config:

  • shell_execute — every shell command
  • file_write / file_edit targeting netclaw’s own config directory (~/.netclaw/config) — the control plane

These are fail-closed on Personal: the gate ignores your default mode and forces a prompt unless you explicitly opt the tool into Auto. Everything else on Personal is auto-approved by default.

You tune this per-audience under Tools.AudienceProfiles.<audience>.ApprovalPolicy in ~/.netclaw/config/netclaw.json. Each tool resolves to one of three modes:

ModeBehavior
AutoRuns immediately, no prompt
ApprovalPrompts the operator before each run (the Layer 4 gate)
DenyAlways blocked — same as not granting the tool

Set an explicit override for shell_execute:

{
"Tools": {
"AudienceProfiles": {
"Personal": {
"ApprovalPolicy": {
"ToolOverrides": {
"shell_execute": "Auto"
}
}
}
}
}
}

Then restart the daemon:

Terminal window
netclaw daemon stop && netclaw daemon start

The same applies to control-plane writes — opt them in with "file_write:control-plane": "Auto" (and "file_edit:control-plane"). Think hard before you do: that lets the agent rewrite netclaw’s own config without asking.

A tool’s mode is resolved in this order, first match wins:

  1. Exact ToolOverrides entry (shell_execute, file_write:control-plane, or an MCP server/tool key)
  2. McpServerDefaults entry for an MCP tool’s server
  3. Built-in fail-closed default — shell and control-plane writes on Personal
  4. DefaultMode

Relaxing an approval gate only removes the confirmation prompt. The other layers still apply:

  • Layer 1 hard-deny still blocks rm -rf /, sudo, fork bombs, and the rest of the operation hard-deny list — no override reaches it.
  • Layer 3 tool grants still decide which tools exist for each audience.
  • Team and Public are untouched — this is the Personal profile only.

The tradeoff is real: with shell_execute on Auto, a prompt-injected agent can run any non-hard-denied command without a human in the loop. The hard-deny list is your remaining backstop.

  • Prompt injection detection uses regex pattern matching, not semantic analysis — novel phrasings can evade it
  • Approval gates require an interactive channel — headless, reminder, and webhook sessions auto-deny all gated tools
  • Secret redaction catches known patterns only — custom secret formats need custom hard-deny path rules
  • Content validation checks magic bytes but doesn’t deep-scan file contents for embedded threats
  • Per-channel audience overrides exist (ChannelAudiences config) but require manual channel ID mapping — there’s no UI for it yet
  • netclaw init — posture selection and network exposure configuration
  • netclaw secrets — encrypted credential storage and output redaction
  • netclaw mcp — per-audience MCP tool permissions
  • netclaw webhooks — HMAC verification and audience assignment for inbound webhooks
  • Hardening — additional lockdown recommendations for production deployments