coderClaw

Security đź”’

Quick check: coderclaw security audit

See also: Formal Verification (Security Models)

Run this regularly (especially after changing config or exposing network surfaces):

coderclaw security audit
coderclaw security audit --deep
coderclaw security audit --fix
coderclaw security audit --json

It flags common footguns (Gateway auth exposure, browser control exposure, elevated allowlists, filesystem permissions).

CoderClaw is both a product and an experiment: you’re wiring frontier-model behavior into real messaging surfaces and real tools. There is no “perfectly secure” setup. The goal is to be deliberate about:

Start with the smallest access that still works, then widen it as you gain confidence.

Hardened baseline in 60 seconds

Use this baseline first, then selectively re-enable tools per trusted agent:

{
  gateway: {
    mode: "local",
    bind: "loopback",
    auth: { mode: "token", token: "replace-with-long-random-token" },
  },
  session: {
    dmScope: "per-channel-peer",
  },
  tools: {
    profile: "messaging",
    deny: ["group:automation", "group:runtime", "group:fs", "sessions_spawn", "sessions_send"],
    fs: { workspaceOnly: true },
    exec: { security: "deny", ask: "always" },
    elevated: { enabled: false },
  },
  channels: {
    whatsapp: { dmPolicy: "pairing", groups: { "*": { requireMention: true } } },
  },
}

This keeps the Gateway local-only, isolates DMs, and disables control-plane/runtime tools by default.

Shared inbox quick rule

If more than one person can DM your bot:

What the audit checks (high level)

If you run --deep, CoderClaw also attempts a best-effort live Gateway probe.

Credential storage map

Use this when auditing access or deciding what to back up:

Security Audit Checklist

When the audit prints findings, treat this as a priority order:

  1. Anything “open” + tools enabled: lock down DMs/groups first (pairing/allowlists), then tighten tool policy/sandboxing.
  2. Public network exposure (LAN bind, Funnel, missing auth): fix immediately.
  3. Browser control remote exposure: treat it like operator access (tailnet-only, pair nodes deliberately, avoid public exposure).
  4. Permissions: make sure state/config/credentials/auth are not group/world-readable.
  5. Plugins/extensions: only load what you explicitly trust.
  6. Model choice: prefer modern, instruction-hardened models for any bot with tools.

Security audit glossary

High-signal checkId values you will most likely see in real deployments (not exhaustive):

checkId Severity Why it matters Primary fix key/path Auto-fix
fs.state_dir.perms_world_writable critical Other users/processes can modify full CoderClaw state filesystem perms on ~/.coderclaw yes
fs.config.perms_writable critical Others can change auth/tool policy/config filesystem perms on ~/.coderclaw/coderclaw.json yes
fs.config.perms_world_readable critical Config can expose tokens/settings filesystem perms on config file yes
gateway.bind_no_auth critical Remote bind without shared secret gateway.bind, gateway.auth.* no
gateway.loopback_no_auth critical Reverse-proxied loopback may become unauthenticated gateway.auth.*, proxy setup no
gateway.tools_invoke_http.dangerous_allow warn/critical Re-enables dangerous tools over HTTP API gateway.tools.allow no
gateway.tailscale_funnel critical Public internet exposure gateway.tailscale.mode no
gateway.control_ui.insecure_auth critical Token-only over HTTP, no device identity gateway.controlUi.allowInsecureAuth no
gateway.control_ui.device_auth_disabled critical Disables device identity check gateway.controlUi.dangerouslyDisableDeviceAuth no
hooks.token_too_short warn Easier brute force on hook ingress hooks.token no
hooks.request_session_key_enabled warn/critical External caller can choose sessionKey hooks.allowRequestSessionKey no
hooks.request_session_key_prefixes_missing warn/critical No bound on external session key shapes hooks.allowedSessionKeyPrefixes no
logging.redact_off warn Sensitive values leak to logs/status logging.redactSensitive yes
sandbox.docker_config_mode_off warn Sandbox Docker config present but inactive agents.*.sandbox.mode no
tools.profile_minimal_overridden warn Agent overrides bypass global minimal profile agents.list[].tools.profile no
plugins.tools_reachable_permissive_policy warn Extension tools reachable in permissive contexts tools.profile + tool allow/deny no
models.small_params critical/info Small models + unsafe tool surfaces raise injection risk model choice + sandbox/tool policy no

Control UI over HTTP

The Control UI needs a secure context (HTTPS or localhost) to generate device identity. If you enable gateway.controlUi.allowInsecureAuth, the UI falls back to token-only auth and skips device pairing when device identity is omitted. This is a security downgrade—prefer HTTPS (Tailscale Serve) or open the UI on 127.0.0.1.

For break-glass scenarios only, gateway.controlUi.dangerouslyDisableDeviceAuth disables device identity checks entirely. This is a severe security downgrade; keep it off unless you are actively debugging and can revert quickly.

coderclaw security audit warns when this setting is enabled.

Reverse Proxy Configuration

If you run the Gateway behind a reverse proxy (nginx, Caddy, Traefik, etc.), you should configure gateway.trustedProxies for proper client IP detection.

When the Gateway detects proxy headers (X-Forwarded-For or X-Real-IP) from an address that is not in trustedProxies, it will not treat connections as local clients. If gateway auth is disabled, those connections are rejected. This prevents authentication bypass where proxied connections would otherwise appear to come from localhost and receive automatic trust.

gateway:
  trustedProxies:
    - "127.0.0.1" # if your proxy runs on localhost
  auth:
    mode: password
    password: ${CODERCLAW_GATEWAY_PASSWORD}

When trustedProxies is configured, the Gateway will use X-Forwarded-For headers to determine the real client IP for local client detection. Make sure your proxy overwrites (not appends to) incoming X-Forwarded-For headers to prevent spoofing.

Local session logs live on disk

CoderClaw stores session transcripts on disk under ~/.coderclaw/agents/<agentId>/sessions/*.jsonl. This is required for session continuity and (optionally) session memory indexing, but it also means any process/user with filesystem access can read those logs. Treat disk access as the trust boundary and lock down permissions on ~/.coderclaw (see the audit section below). If you need stronger isolation between agents, run them under separate OS users or separate hosts.

Node execution (system.run)

If a macOS node is paired, the Gateway can invoke system.run on that node. This is remote code execution on the Mac:

Dynamic skills (watcher / remote nodes)

CoderClaw can refresh the skills list mid-session:

Treat skill folders as trusted code and restrict who can modify them.

The Threat Model

Your AI assistant can:

People who message you can:

Core concept: access control before intelligence

Most failures here are not fancy exploits — they’re “someone messaged the bot and the bot did what they asked.”

CoderClaw’s stance:

Command authorization model

Slash commands and directives are only honored for authorized senders. Authorization is derived from channel allowlists/pairing plus commands.useAccessGroups (see Configuration and Slash commands). If a channel allowlist is empty or includes "*", commands are effectively open for that channel.

/exec is a session-only convenience for authorized operators. It does not write config or change other sessions.

Control plane tools risk

Two built-in tools can make persistent control-plane changes:

For any agent/surface that handles untrusted content, deny these by default:

{
  tools: {
    deny: ["gateway", "cron", "sessions_spawn", "sessions_send"],
  },
}

commands.restart=false only blocks restart actions. It does not disable gateway config/update actions.

Plugins/extensions

Plugins run in-process with the Gateway. Treat them as trusted code:

Details: Plugins

DM access model (pairing / allowlist / open / disabled)

All current DM-capable channels support a DM policy (dmPolicy or *.dm.policy) that gates inbound DMs before the message is processed:

Approve via CLI:

coderclaw pairing list <channel>
coderclaw pairing approve <channel> <code>

Details + files on disk: Pairing

DM session isolation (multi-user mode)

By default, CoderClaw routes all DMs into the main session so your assistant has continuity across devices and channels. If multiple people can DM the bot (open DMs or a multi-person allowlist), consider isolating DM sessions:

{
  session: { dmScope: "per-channel-peer" },
}

This prevents cross-user context leakage while keeping group chats isolated.

Treat the snippet above as secure DM mode:

If you run multiple accounts on the same channel, use per-account-channel-peer instead. If the same person contacts you on multiple channels, use session.identityLinks to collapse those DM sessions into one canonical identity. See Session Management and Configuration.

Allowlists (DM + groups) — terminology

CoderClaw has two separate “who can trigger me?” layers:

Details: Configuration and Groups

Prompt injection (what it is, why it matters)

Prompt injection is when an attacker crafts a message that manipulates the model into doing something unsafe (“ignore your instructions”, “dump your filesystem”, “follow this link and run commands”, etc.).

Even with strong system prompts, prompt injection is not solved. System prompt guardrails are soft guidance only; hard enforcement comes from tool policy, exec approvals, sandboxing, and channel allowlists (and operators can disable these by design). What helps in practice:

Red flags to treat as untrusted:

Unsafe external content bypass flags

CoderClaw includes explicit bypass flags that disable external-content safety wrapping:

Guidance:

Prompt injection does not require public DMs

Even if only you can message the bot, prompt injection can still happen via any untrusted content the bot reads (web search/fetch results, browser pages, emails, docs, attachments, pasted logs/code). In other words: the sender is not the only threat surface; the content itself can carry adversarial instructions.

When tools are enabled, the typical risk is exfiltrating context or triggering tool calls. Reduce the blast radius by:

Model strength (security note)

Prompt injection resistance is not uniform across model tiers. Smaller/cheaper models are generally more susceptible to tool misuse and instruction hijacking, especially under adversarial prompts.

Recommendations:

Reasoning & verbose output in groups

/reasoning and /verbose can expose internal reasoning or tool output that was not meant for a public channel. In group settings, treat them as debug only and keep them off unless you explicitly need them.

Guidance:

Configuration Hardening (examples)

0) File permissions

Keep config + state private on the gateway host:

coderclaw doctor can warn and offer to tighten these permissions.

0.4) Network exposure (bind + port + firewall)

The Gateway multiplexes WebSocket + HTTP on a single port:

This HTTP surface includes the Control UI and the canvas host:

If you load canvas content in a normal browser, treat it like any other untrusted web page:

Bind mode controls where the Gateway listens:

Rules of thumb:

0.4.1) mDNS/Bonjour discovery (information disclosure)

The Gateway broadcasts its presence via mDNS (_coderclaw-gw._tcp on port 5353) for local device discovery. In full mode, this includes TXT records that may expose operational details:

Operational security consideration: Broadcasting infrastructure details makes reconnaissance easier for anyone on the local network. Even “harmless” info like filesystem paths and SSH availability helps attackers map your environment.

Recommendations:

  1. Minimal mode (default, recommended for exposed gateways): omit sensitive fields from mDNS broadcasts:

    {
      discovery: {
        mdns: { mode: "minimal" },
      },
    }
    
  2. Disable entirely if you don’t need local device discovery:

    {
      discovery: {
        mdns: { mode: "off" },
      },
    }
    
  3. Full mode (opt-in): include cliPath + sshPort in TXT records:

    {
      discovery: {
        mdns: { mode: "full" },
      },
    }
    
  4. Environment variable (alternative): set CODERCLAW_DISABLE_BONJOUR=1 to disable mDNS without config changes.

In minimal mode, the Gateway still broadcasts enough for device discovery (role, gatewayPort, transport) but omits cliPath and sshPort. Apps that need CLI path information can fetch it via the authenticated WebSocket connection instead.

0.5) Lock down the Gateway WebSocket (local auth)

Gateway auth is required by default. If no token/password is configured, the Gateway refuses WebSocket connections (fail‑closed).

The onboarding wizard generates a token by default (even for loopback) so local clients must authenticate.

Set a token so all WS clients must authenticate:

{
  gateway: {
    auth: { mode: "token", token: "your-token" },
  },
}

Doctor can generate one for you: coderclaw doctor --generate-gateway-token.

Note: gateway.remote.token is only for remote CLI calls; it does not protect local WS access. Optional: pin remote TLS with gateway.remote.tlsFingerprint when using wss://.

Local device pairing:

Auth modes:

Rotation checklist (token/password):

  1. Generate/set a new secret (gateway.auth.token or CODERCLAW_GATEWAY_PASSWORD).
  2. Restart the Gateway (or restart the macOS app if it supervises the Gateway).
  3. Update any remote clients (gateway.remote.token / .password on machines that call into the Gateway).
  4. Verify you can no longer connect with the old credentials.

0.6) Tailscale Serve identity headers

When gateway.auth.allowTailscale is true (default for Serve), CoderClaw accepts Tailscale Serve identity headers (tailscale-user-login) as authentication. CoderClaw verifies the identity by resolving the x-forwarded-for address through the local Tailscale daemon (tailscale whois) and matching it to the header. This only triggers for requests that hit loopback and include x-forwarded-for, x-forwarded-proto, and x-forwarded-host as injected by Tailscale.

Security rule: do not forward these headers from your own reverse proxy. If you terminate TLS or proxy in front of the gateway, disable gateway.auth.allowTailscale and use token/password auth (or Trusted Proxy Auth) instead.

Trusted proxies:

See Tailscale and Web overview.

If your Gateway is remote but the browser runs on another machine, run a node host on the browser machine and let the Gateway proxy browser actions (see Browser tool). Treat node pairing like admin access.

Recommended pattern:

Avoid:

0.7) Secrets on disk (what’s sensitive)

Assume anything under ~/.coderclaw/ (or $CODERCLAW_STATE_DIR/) may contain secrets or private data:

Hardening tips:

0.8) Logs + transcripts (redaction + retention)

Logs and transcripts can leak sensitive info even when access controls are correct:

Recommendations:

Details: Logging

1) DMs: pairing by default

{
  channels: { whatsapp: { dmPolicy: "pairing" } },
}

2) Groups: require mention everywhere

{
  "channels": {
    "whatsapp": {
      "groups": {
        "*": { "requireMention": true }
      }
    }
  },
  "agents": {
    "list": [
      {
        "id": "main",
        "groupChat": { "mentionPatterns": ["@coderclaw", "@mybot"] }
      }
    ]
  }
}

In group chats, only respond when explicitly mentioned.

3. Separate Numbers

Consider running your AI on a separate phone number from your personal one:

4. Read-Only Mode (Today, via sandbox + tools)

You can already build a read-only profile by combining:

We may add a single readOnlyMode flag later to simplify this configuration.

Additional hardening options:

5) Secure baseline (copy/paste)

One “safe default” config that keeps the Gateway private, requires DM pairing, and avoids always-on group bots:

{
  gateway: {
    mode: "local",
    bind: "loopback",
    port: 18789,
    auth: { mode: "token", token: "your-long-random-token" },
  },
  channels: {
    whatsapp: {
      dmPolicy: "pairing",
      groups: { "*": { requireMention: true } },
    },
  },
}

If you want “safer by default” tool execution too, add a sandbox + deny dangerous tools for any non-owner agent (example below under “Per-agent access profiles”).

Dedicated doc: Sandboxing

Two complementary approaches:

Note: to prevent cross-agent access, keep agents.defaults.sandbox.scope at "agent" (default) or "session" for stricter per-session isolation. scope: "shared" uses a single container/workspace.

Also consider agent workspace access inside the sandbox:

Important: tools.elevated is the global baseline escape hatch that runs exec on the host. Keep tools.elevated.allowFrom tight and don’t enable it for strangers. You can further restrict elevated per agent via agents.list[].tools.elevated. See Elevated Mode.

Browser control risks

Enabling browser control gives the model the ability to drive a real browser. If that browser profile already contains logged-in sessions, the model can access those accounts and data. Treat browser profiles as sensitive state:

Per-agent access profiles (multi-agent)

With multi-agent routing, each agent can have its own sandbox + tool policy: use this to give full access, read-only, or no access per agent. See Multi-Agent Sandbox & Tools for full details and precedence rules.

Common use cases:

Example: full access (no sandbox)

{
  agents: {
    list: [
      {
        id: "personal",
        workspace: "~/.coderclaw/workspace-personal",
        sandbox: { mode: "off" },
      },
    ],
  },
}

Example: read-only tools + read-only workspace

{
  agents: {
    list: [
      {
        id: "family",
        workspace: "~/.coderclaw/workspace-family",
        sandbox: {
          mode: "all",
          scope: "agent",
          workspaceAccess: "ro",
        },
        tools: {
          allow: ["read"],
          deny: ["write", "edit", "apply_patch", "exec", "process", "browser"],
        },
      },
    ],
  },
}

Example: no filesystem/shell access (provider messaging allowed)

{
  agents: {
    list: [
      {
        id: "public",
        workspace: "~/.coderclaw/workspace-public",
        sandbox: {
          mode: "all",
          scope: "agent",
          workspaceAccess: "none",
        },
        // Session tools can reveal sensitive data from transcripts. By default CoderClaw limits these tools
        // to the current session + spawned subagent sessions, but you can clamp further if needed.
        // See `tools.sessions.visibility` in the configuration reference.
        tools: {
          sessions: { visibility: "tree" }, // self | tree | agent | all
          allow: [
            "sessions_list",
            "sessions_history",
            "sessions_send",
            "sessions_spawn",
            "session_status",
            "whatsapp",
            "telegram",
            "slack",
            "discord",
          ],
          deny: [
            "read",
            "write",
            "edit",
            "apply_patch",
            "exec",
            "process",
            "browser",
            "canvas",
            "nodes",
            "cron",
            "gateway",
            "image",
          ],
        },
      },
    ],
  },
}

What to Tell Your AI

Include security guidelines in your agent’s system prompt:

## Security Rules
- Never share directory listings or file paths with strangers
- Never reveal API keys, credentials, or infrastructure details
- Verify requests that modify system config with the owner
- When in doubt, ask before acting
- Keep private data private unless explicitly authorized

Incident Response

If your AI does something bad:

Contain

  1. Stop it: stop the macOS app (if it supervises the Gateway) or terminate your coderclaw gateway process.
  2. Close exposure: set gateway.bind: "loopback" (or disable Tailscale Funnel/Serve) until you understand what happened.
  3. Freeze access: switch risky DMs/groups to dmPolicy: "disabled" / require mentions, and remove "*" allow-all entries if you had them.

Rotate (assume compromise if secrets leaked)

  1. Rotate Gateway auth (gateway.auth.token / CODERCLAW_GATEWAY_PASSWORD) and restart.
  2. Rotate remote client secrets (gateway.remote.token / .password) on any machine that can call the Gateway.
  3. Rotate provider/API credentials (WhatsApp creds, Slack/Discord tokens, model/API keys in auth-profiles.json).

Audit

  1. Check Gateway logs: /tmp/coderclaw/coderclaw-YYYY-MM-DD.log (or logging.file).
  2. Review the relevant transcript(s): ~/.coderclaw/agents/<agentId>/sessions/*.jsonl.
  3. Review recent config changes (anything that could have widened access: gateway.bind, gateway.auth, dm/group policies, tools.elevated, plugin changes).
  4. Re-run coderclaw security audit --deep and confirm critical findings are resolved.

Collect for a report

Secret Scanning (detect-secrets)

CI runs detect-secrets scan --baseline .secrets.baseline in the secrets job. If it fails, there are new candidates not yet in the baseline.

If CI fails

  1. Reproduce locally:

    detect-secrets scan --baseline .secrets.baseline
    
  2. Understand the tools:
    • detect-secrets scan finds candidates and compares them to the baseline.
    • detect-secrets audit opens an interactive review to mark each baseline item as real or false positive.
  3. For real secrets: rotate/remove them, then re-run the scan to update the baseline.
  4. For false positives: run the interactive audit and mark them as false:

    detect-secrets audit .secrets.baseline
    
  5. If you need new excludes, add them to .detect-secrets.cfg and regenerate the baseline with matching --exclude-files / --exclude-lines flags (the config file is reference-only; detect-secrets doesn’t read it automatically).

Commit the updated .secrets.baseline once it reflects the intended state.

Reporting Security Issues

Found a vulnerability in CoderClaw? Please report responsibly:

  1. Email: [email protected]
  2. Don’t post publicly until fixed
  3. We’ll credit you (unless you prefer anonymity)