Hardening
The security model covers what netclaw enforces by default: default-deny, audience scoping, a four-layer invocation stack. This page covers what you should do on top of that, especially if you’re running it for a team, exposing it to the internet, or leaving it unattended.
Sections are independent. Work through the ones that apply to your deployment.
All configuration lives in ~/.netclaw/config/netclaw.json unless noted otherwise. Values merge with built-in defaults, so you only need to specify what you’re changing.
File Permissions
Section titled “File Permissions”Netclaw stores credentials and webhook secrets in plaintext JSON files. Lock them down:
# Encrypted secrets — only the daemon user should read thischmod 600 ~/.netclaw/config/secrets.json
# Webhook route files contain HMAC secretschmod 700 ~/.netclaw/config/webhooks/
# Encryption keys for secrets at restchmod 700 ~/.netclaw/keys/netclaw doctor checks secrets.json permissions and flags unencrypted values. Run it after any manual config edits.
Shell Access
Section titled “Shell Access”Shell access is the biggest risk surface you can control. Three modes in ~/.netclaw/config/netclaw.json:
| Mode | Behavior |
|---|---|
Off | Shell completely disabled. No shell_execute tool available. |
SandboxOnly | Shell runs in a sandboxed environment. |
HostAllowed | Shell runs directly on the host. Approval gates are your only guardrail. |
Set the mode explicitly:
{ "Security": { "ShellExecutionMode": "Off" }}For Team and Public postures, shell is off by default. Only Personal posture enables it, and only with approval gates on shell_execute. If you’re running Personal posture but don’t need shell, turn it off.
Custom Hard-Deny Patterns
Section titled “Custom Hard-Deny Patterns”The built-in hard-deny list blocks sudo, rm -rf ~/, kill, fork bombs, and commands that would stop the daemon. Add your own patterns for anything dangerous in your environment:
{ "Tools": { "HardDenyPatterns": [ "docker rm", "kubectl delete namespace", "terraform destroy" ] }}Custom patterns augment the defaults, they don’t replace them. Netclaw tokenizes compound commands (&&, ||, ;, |) and checks each segment independently, so echo hello && docker rm foo still triggers the deny.
MCP Tool Permissions
Section titled “MCP Tool Permissions”New MCP servers start with zero tool grants for all audiences, safe by default. The risk comes from granting too much.
Audit your grants per audience:
netclaw mcp permissions
Personal audience with all tools granted. Compare against the locked-down Team and Public defaults:

Team audience: server disabled, no tools granted.
Grant the minimum tools each audience actually needs. Don’t enable everything for Team because it’s faster to configure. For destructive MCP tools (delete, drop, write), set approval mode to Approval or Deny. Per-tool overrides let you keep the server default on Auto while gating the dangerous ones:
{ "Tools": { "AudienceProfiles": { "Personal": { "ApprovalPolicy": { "DefaultMode": "Auto", "ToolOverrides": { "shell_execute": "Approval", "notion/notion-delete-page": "Approval" } } } } }}Tool override keys use the format {serverName}/{toolName}.
Review ~/.netclaw/config/tool-approvals.json periodically and prune stale “approve always” decisions.
See netclaw mcp for full details on the permissions TUI and CLI.
Network Exposure
Section titled “Network Exposure”The daemon binds to 127.0.0.1:5199 by default. Keep it that way unless you have a reason not to.
| Mode | Scope | Risk |
|---|---|---|
local | Loopback only | Minimal — only local processes can connect |
tailscale-serve | Your tailnet | Low — Tailscale identity gates access |
tailscale-funnel | Public internet | High — anyone on the internet can reach it via Tailscale Funnel |
cloudflare-tunnel | Public internet | High — requires a Cloudflare Access policy |

The exposure selection during netclaw init. Internet-facing modes force explicit confirmation.
If you need remote access, prefer tailscale-serve. It limits access to your tailnet. Funnel exposes you to the public internet — a completely different threat model.
For Docker deployments, bind to loopback explicitly:
docker run -p 127.0.0.1:5199:5199 ...Omitting 127.0.0.1 binds to all interfaces. Any machine on your network can reach the daemon.
Changing exposure mode requires a daemon restart. It’s excluded from hot-reload on purpose.
netclaw daemon stop && netclaw daemon startSlack Channel Restrictions
Section titled “Slack Channel Restrictions”Out of the box, Slack uses MentionOnly: true (netclaw only responds when @mentioned) and AllowDirectMessages: false. Lock down channel and user access on top of that.
Channel Allowlist
Section titled “Channel Allowlist”With no channel allowlist and no default channel set, netclaw denies all channel traffic. Explicitly list the channels it should respond in:
{ "Slack": { "AllowedChannelIds": ["C0123ABCDEF", "C0456GHIJKL"], "MentionOnly": true }}To find a Slack channel ID: right-click the channel name in Slack, select “View channel details,” and look at the bottom of the panel. Or see Slack’s guide to finding IDs.
User Allowlist
Section titled “User Allowlist”Restrict which users can invoke netclaw:
{ "Slack": { "AllowedUserIds": ["U0123ABCDEF", "U0456GHIJKL"] }}User IDs follow the same pattern — click a user’s profile in Slack and find the ID under “More.”
DMs and Per-Channel Audiences
Section titled “DMs and Per-Channel Audiences”Keep DMs off unless you need them. If you enable DMs, restrict which users can DM:
{ "Slack": { "AllowDirectMessages": true, "AllowedUserIds": ["U0123ABCDEF"] }}You can also override the audience per channel. Useful if you want a specific Slack channel to get Personal-level tool access while everything else stays at Team:
{ "Slack": { "ChannelAudiences": { "C0123ABCDEF": "personal", "dm": "team" } }}The "dm" key is reserved. It maps all direct messages to the specified audience.
Invalid audience values in ChannelAudiences result in a deny (fail-closed).
netclaw doctor warns if Slack is enabled with no channel allowlist and no default channel configured, or if DMs are enabled without an AllowedUserIds list.
Approval Gates
Section titled “Approval Gates”The last layer before a tool actually runs. Configure per audience:
{ "Tools": { "AudienceProfiles": { "Personal": { "ApprovalPolicy": { "DefaultMode": "Auto", "ToolOverrides": { "shell_execute": "Approval" } } }, "Team": { "ApprovalPolicy": { "DefaultMode": "Approval" } }, "Public": { "ApprovalPolicy": { "DefaultMode": "Deny" } } } }}- Headless sessions (reminders, webhooks,
netclaw chat -p "prompt") auto-deny all gated tools. There’s no human to ask. - No response within 5 minutes means deny.
- “Approve always” persists to
~/.netclaw/config/tool-approvals.json. Revoke by editing the file directly.
If nobody is watching the approval prompts in production, set DefaultMode: "Deny" for Team and Public. Auto-deny is safer than a 5-minute timeout nobody sees.
Webhook Security
Section titled “Webhook Security”Each webhook route has its own HMAC secret, audience, body size limit, and rate limit:
{ "Verification": { "Kind": "Hmac", "HmacAlgorithm": "Sha256", "Secret": "whsec_...", "SignatureHeaderName": "X-Hub-Signature-256", "SignaturePrefix": "sha256=" }, "Audience": "Public", "MaxBodyBytes": 1048576, "RateLimitPerMinute": 10}Set the audience to the minimum the webhook actually needs. Most should run as Public (fewest tools, session-scoped filesystem, wiped on end). Only use Team or Personal if the webhook prompt genuinely requires those tools.
RateLimitPerMinute applies per webhook route, not globally across all routes. Lower it from the default 30 if the source won’t fire that often — GitHub sends roughly one webhook per event, so 10/min is plenty. Keep MaxBodyBytes at 1 MB or lower unless you know the payloads are larger.
Route files contain secrets in plaintext. Keep ~/.netclaw/config/webhooks/ at mode 700 (see File Permissions).
See netclaw webhooks for route setup and HMAC verification details.
Doctor Checks
Section titled “Doctor Checks”After making changes, validate everything:
netclaw doctor
Security-relevant checks include:
| Check | What it catches |
|---|---|
| Security Policy | Missing DeploymentPosture |
| Secrets JSON | Wrong file permissions, unencrypted values |
| Slack ACL | No channel allowlist, DMs enabled without user allowlist |
| Tool Audience Profiles | Overly permissive tool grants |
| exposure-mode | Internet-reachable exposure without valid auth policy |
| Inbound Webhook Routes | Schema errors in route files |
Wire it into your deployment pipeline:
netclaw doctor --format json | jq -e '.exitCode == 0'Limitations
Section titled “Limitations”- Prompt injection detection is regex-based. It catches known patterns (role resets, exfiltration attempts, invisible Unicode) but novel phrasings, Base64 encoding, synonym substitution, and non-English attacks can evade it. Treat it as a tripwire, not a firewall.
- Tool grants are per-audience, not per-channel.
ChannelAudiencesoverrides which audience a channel maps to, but you can’t give one Slack channel different tools than another channel with the same audience. - Secret redaction catches
sk-*, Slack tokens (xox[baprs]-*),ghp_*, AWS access keys (AKIA*), JWTs, PEM blocks, and common JSON key names. Custom secret formats won’t be redacted. Use hard-deny path rules to block file access instead. - Approval gates only work on interactive channels. Headless mode, reminders, and webhooks auto-deny all gated tools. Design automation workflows with that in mind.
Related Pages
Section titled “Related Pages”- Security Model — default-deny architecture, audiences, trust layers
netclaw init— posture and exposure selection wizardnetclaw mcp— MCP tool permissions TUInetclaw doctor— configuration diagnosticsnetclaw webhooks— webhook route management and HMAC verificationnetclaw secrets— encrypted credential storage
Further Reading
Section titled “Further Reading”- OWASP LLM Top 10 — prompt injection, insecure output handling, and other LLM attack vectors
- Tailscale ACLs — network-level access control for
tailscale-servedeployments - Cloudflare Access policies — IdP-based access control for
cloudflare-tunneldeployments - GitHub webhook security — best practices for HMAC verification and secret rotation
- Slack: Locate your URL or ID — finding channel and user IDs for allowlist configuration