Published on

Inside Clawdbot: Infrastructure, Security Subsystems, and the Plugin SDK

Authors

This is the third post in my Clawdbot deep dive series. The first covered memory architecture, the second covered the agent system and AI providers. Here I'm looking at everything that makes Clawdbot run reliably as infrastructure: scheduling, security, extensibility, device pairing, and service management.

The source analysis covers ~42K lines across the open-source codebase.


Cron: Scheduling with Isolation

Clawdbot has a built-in cron system that goes beyond simple timer-based execution. It handles three schedule types, runs jobs in isolated agent contexts, detects stuck jobs, and prunes run history.

Three Schedule Types

// One-shot (fire once, optionally delete after)
{ kind: "cron", expr: "0 8 28 1 *", tz: "Europe/Berlin" }

// Recurring (standard cron expressions)
{ kind: "cron", expr: "0 19 * * *", tz: "Europe/Berlin" }

// Interval (every N milliseconds)
{ kind: "interval", everyMs: 1800000 }  // Every 30 minutes

Cron expressions use standard 5-field format with timezone support. The tz field accepts IANA timezone names, so "Europe/Berlin" correctly handles CET/CEST transitions.

Isolated Agent Execution

Each cron job runs in its own agent context — separate from your main session. This means:

  • A cron job can't see your current conversation
  • Cron jobs don't pollute your session with tool calls and intermediate output
  • If a cron job fails or hangs, it doesn't block your main session
  • Jobs target a specific session via sessionTarget (usually "main") and deliver their output there

The payload is injected as a system event, and the agent processes it as if it received a message.

Stuck Job Detection

The cron scheduler tracks execution state per job:

{
  state: {
    nextRunAtMs: 1769583600000,
    lastRunAtMs: 1769490000000,
    lastStatus: "completed",
    lastDurationMs: 45000,
    lastError: null
  }
}

If a job exceeds its expected duration or fails repeatedly, the scheduler marks it and prevents cascading failures. Jobs can be configured with deleteAfterRun: true for one-shot reminders that clean up after themselves.

Run Log with Pruning

Each job maintains a run log tracking execution history — timestamps, durations, success/failure status, and error messages. Old entries are automatically pruned to prevent unbounded growth. You can inspect recent runs via:

clawdbot cron runs --job <jobId>

Security: 7 Subsystems

Clawdbot's security isn't a single layer — it's seven interlocking subsystems, each addressing a different threat vector.

1. Injection Detection

The first line of defense against prompt injection. Clawdbot's access control philosophy is: identity first, scope second, model last. The assumption is that the model can be manipulated, so the system is designed to limit blast radius when it is.

  • DM pairing gates who can even talk to the bot. Unknown senders get a pairing code — messages aren't processed until approved
  • Group allowlists restrict which groups the bot participates in
  • Mention gating prevents the bot from processing every message in a group — only @mentions trigger it
  • Tool policy (the 9-layer system) restricts what tools are available, so even a successful injection can't access tools the session doesn't have

The design treats the model as an untrusted component and enforces policy at the Gateway level, not through prompt instructions.

2. SSRF Prevention with DNS Pinning

When the agent fetches URLs (via web_fetch or browser tools), Clawdbot prevents Server-Side Request Forgery attacks. The web fetch implementation:

  • Resolves DNS before connecting and pins the resolved IP
  • Blocks private/internal IP ranges (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, link-local)
  • Prevents DNS rebinding — the resolved address is checked against blocklists after resolution, not before

This stops an attacker from crafting a URL that resolves to 127.0.0.1 or an internal service. The agent can fetch public URLs but cannot be tricked into accessing internal infrastructure.

3. Ed25519 Device Identity

Every client (CLI, macOS app, iOS/Android node) generates an Ed25519 keypair on first run. The device identity is derived from the public key fingerprint and used throughout the protocol:

{
  device: {
    id: "device_fingerprint",
    publicKey: "base64-ed25519-pubkey",
    signature: "base64-signature",
    signedAt: 1737264000000,
    nonce: "server-provided-nonce"
  }
}

During connection, the Gateway sends a connect.challenge with a random nonce. The client signs this nonce with its private key. The Gateway verifies the signature, confirming the client controls the claimed device identity.

This prevents:

  • Replay attacks — Nonces are single-use
  • Device impersonation — The private key never leaves the device
  • Token theft — Even if a device token is stolen, the attacker can't sign new challenges

Non-local connections (anything not from loopback) must sign the challenge. Local connections can be auto-approved for same-host UX.

4. Exec Safety

Command execution has its own security model beyond the 9-layer tool policy:

Exec approvals are enforced on the execution host (gateway or node):

SettingEffect
security: "deny"Block all host exec requests
security: "allowlist"Only allowlisted commands run
security: "full"Everything allowed
ask: "on-miss"Prompt the user for unlisted commands
ask: "always"Prompt on every command

Allowlists use glob patterns against resolved binary paths:

{
  allowlist: [
    { pattern: "~/Projects/**/bin/rg" },
    { pattern: "/opt/homebrew/bin/*" }
  ]
}

Each entry tracks last-used timestamps and resolved paths for audit. There's also a safe bins concept — stdin-only utilities like jq, grep, sort, head, tail that can run in allowlist mode without explicit entries, because they can only operate on piped input and reject file path arguments.

Shell chaining (&&, ||, ;) is allowed only when every segment satisfies the allowlist independently.

5. Audit Framework

The built-in security audit (clawdbot security audit) is a runtime scanner that checks 7 categories:

clawdbot security audit        # Quick scan
clawdbot security audit --deep  # Live Gateway probe
clawdbot security audit --fix   # Auto-apply safe defaults

What it checks:

  • Inbound access: DM policies, group policies, allowlists — can strangers trigger the bot?
  • Tool blast radius: Elevated tools + open rooms — could injection lead to shell access?
  • Network exposure: Gateway bind address, auth mode, Tailscale Serve/Funnel
  • Browser control: Remote nodes, relay ports, CDP endpoints
  • Disk hygiene: File permissions, symlinks, config includes
  • Plugins: Extensions loaded without explicit allowlists
  • Model hygiene: Legacy models that may be less instruction-hardened

The --fix flag applies safe guardrails: tightens open group policies to allowlists, re-enables sensitive log redaction, and sets restrictive file permissions (700 for ~/.clawdbot, 600 for config files).

6. Formal Verification (TLA+)

This is unusual for an open-source project: Clawdbot maintains formal security models in TLA+ with machine-checked proofs. Each security claim has:

  • A positive model that TLC verifies (the property holds across the state space)
  • A negative model that produces a counterexample trace (proving the model catches real bugs)

Currently verified claims:

PropertyWhat It Proves
Gateway exposureBinding beyond loopback without auth enables remote compromise
nodes.run pipelineRequires command allowlist + declared commands + live approval
Approval tokensTokenized to prevent replay attacks
Pairing storeRespects TTL and pending-request caps
Ingress gatingUnauthorized control commands can't bypass mention gating
Routing isolationDMs from distinct peers don't collapse into the same session

This isn't a proof that the TypeScript implementation is bug-free — the models abstract over implementation details. But they provide a machine-checked regression suite for the security-critical protocol logic.

7. Credential Isolation

Credentials are scattered across multiple locations by design — no single file contains everything:

CredentialLocation
WhatsApp session~/.clawdbot/credentials/whatsapp/*/creds.json
Telegram bot tokenConfig or env var
Discord bot tokenConfig or env var
Pairing allowlists~/.clawdbot/credentials/*-allowFrom.json
Model auth profiles~/.clawdbot/agents/*/agent/auth-profiles.json
Device tokens~/.clawdbot/devices/paired.json
Exec approvals~/.clawdbot/exec-approvals.json

File permissions are enforced (600 for sensitive files, 700 for directories). The security audit checks these on every run.


Plugin SDK

Clawdbot's plugin system uses a manifest-based architecture with JSON Schema validation and jiti transpilation (TypeScript → JS at runtime, no build step required).

Plugin Manifest

Every plugin has a clawdbot.plugin.json:

{
  "id": "voice-call",
  "version": "1.0.0",
  "configSchema": {
    "type": "object",
    "properties": {
      "provider": { "type": "string", "enum": ["twilio", "vonage"] },
      "apiKey": { "type": "string" }
    }
  },
  "uiHints": {
    "apiKey": { "label": "API Key", "sensitive": true },
    "provider": { "label": "Provider", "placeholder": "twilio" }
  }
}

The configSchema is JSON Schema — validated at Gateway startup. Invalid plugin configs prevent the Gateway from booting, same as invalid core config. The uiHints provide labels and sensitivity markers for the Control UI form renderer.

What Plugins Can Register

Plugins run in-process with the Gateway and can register:

  • Gateway RPC methods — Extend the WebSocket protocol with custom request types
  • HTTP handlers — Add REST endpoints on the Gateway
  • Agent tools — Custom tools the model can call
  • CLI commands — Extend clawdbot with new subcommands
  • Background services — Long-running tasks that start with the Gateway
  • Skills — Bundled SKILL.md folders loaded when the plugin is enabled
  • Auto-reply commands — Execute without invoking the AI agent (slash commands that bypass the model)

Discovery and Precedence

Plugins are discovered from multiple locations, in order:

1. Config paths (plugins.load.paths)
2. Workspace extensions (<workspace>/.clawdbot/extensions/)
3. Global extensions (~/.clawdbot/extensions/)
4. Bundled extensions (shipped with Clawdbot, disabled by default)

First match wins. Bundled plugins must be explicitly enabled via plugins.entries.<id>.enabled or clawdbot plugins enable <id>.

Package Packs

A single directory can contain multiple plugins via package.json:

{
  "name": "my-pack",
  "clawdbot": {
    "extensions": ["./src/safety.ts", "./src/tools.ts"]
  }
}

Each entry becomes a plugin. The id is derived from name/<fileBase>.

Plugin Slots (Exclusive Categories)

Some plugin categories are mutually exclusive. Only one plugin can own a slot:

{
  plugins: {
    slots: {
      memory: "memory-core"  // or "memory-lancedb" or "none"
    }
  }
}

If multiple plugins declare the same slot, only the selected one loads.

Security Considerations

Plugins are trusted code — they run in the Gateway process with full access. The SDK provides guardrails:

  • Allow/deny listsplugins.allow and plugins.deny control which plugins load
  • Config validation — JSON Schema prevents misconfiguration
  • NPM install isolationclawdbot plugins install uses npm pack + npm install --omit=dev, but lifecycle scripts can execute code during install
  • Explicit enable — Bundled plugins default to disabled

The recommendation: pin exact versions, inspect code before enabling, prefer official plugins.


Node Pairing

Nodes are companion devices (macOS, iOS, Android, headless Linux) that connect to the Gateway and provide capabilities the Gateway doesn't have — cameras, screens, local canvases, location services.

Crypto Pairing Flow

  1. Node connects to the Gateway WebSocket with role: "node" and presents its Ed25519 device identity
  2. Gateway sends a connect.challenge with a random nonce
  3. Node signs the nonce and returns it
  4. Gateway verifies the signature and creates a pairing request
  5. Owner approves via clawdbot devices approve <requestId>
  6. Gateway issues a device token scoped to the node role and its declared capabilities
# List pending pairing requests
clawdbot devices list

# Approve a node
clawdbot devices approve abc123

# Check connected nodes
clawdbot nodes status

Capability Declaration

Nodes declare what they can do at connect time:

{
  role: "node",
  caps: ["camera", "canvas", "screen", "location", "voice"],
  commands: ["camera.snap", "canvas.navigate", "screen.record", "location.get"],
  permissions: {
    "camera.capture": true,
    "screen.record": false
  }
}

The Gateway treats these as claims and enforces server-side allowlists. A node declaring screen.record doesn't automatically get access — the user must also approve screen recording permissions.

macOS Companion App

The macOS app can run in node mode, connecting to the Gateway's WebSocket and exposing local capabilities:

  • Canvas: WebView for rendering agent-generated HTML/A2UI
  • Camera: Front/back camera snapshots and video clips
  • Screen: Screen recording
  • system.run: Execute commands on the Mac (gated by exec approvals)

The app manages its own exec approval UI — a settings panel where you configure security policy, allowlists, and per-agent overrides.

Browser Proxy

Nodes can proxy browser control. The Gateway can invoke browser actions on a remote node's browser, enabling scenarios where:

  • The Gateway runs on a headless VPS
  • A macOS node provides the browser for web automation
  • CDP (Chrome DevTools Protocol) connections are proxied through the node

This is gated by sandbox policy — sandbox.browser.allowHostControl must be explicitly enabled, and custom control URLs go through allowlists.


Daemon Management

Clawdbot runs as a background service across macOS, Linux, and Windows. Each platform uses its native service manager.

Platform Support

PlatformService ManagerCommand
macOSlaunchd~/Library/LaunchAgents/com.clawdbot.gateway.plist
Linuxsystemd (user)~/.config/systemd/user/clawdbot-gateway.service
WindowsTask Scheduler (schtasks)Scheduled task at logon

Installation

# Install during onboarding (recommended)
clawdbot onboard --install-daemon

# Or standalone
clawdbot service install
clawdbot service start
clawdbot service status

On Linux, user services require linger to survive logout:

sudo loginctl enable-linger "$USER"

Multi-Profile Support

Multiple Gateway instances can run simultaneously with separate profiles:

clawdbot --profile work gateway
clawdbot --profile personal gateway

Each profile gets its own state directory, config, and service instance. This enables running separate agents for different contexts (work vs personal) on the same machine.

Health and Recovery

The daemon is configured for automatic restart on failure:

  • launchd: KeepAlive ensures restart on crash
  • systemd: Restart=on-failure with configurable restart delay
  • schtasks: Logon trigger re-launches after reboot

Health checks are available via CLI:

clawdbot status         # Quick overview
clawdbot health         # Gateway connectivity
clawdbot doctor         # Full diagnostic (config, deps, auth)
clawdbot gateway probe  # Live WebSocket probe

How It All Connects

These systems are independent but composable:

  • Cron jobs can trigger security audits, memory maintenance, or notification digests — each running in an isolated agent context
  • Plugins can register new tools that flow through the same 9-layer policy engine as built-in tools
  • Nodes extend the Gateway's capabilities while maintaining the same crypto identity and approval model
  • The daemon ensures everything stays running, with health checks that validate the full stack

The security subsystems work in layers: device identity authenticates connections, pairing gates access, tool policy restricts capabilities, exec approvals guard command execution, sandbox isolates untrusted sessions, the audit framework catches misconfigurations, and the TLA+ models verify the protocol logic is correct.


Series

  1. How Clawdbot Remembers: Memory Architecture
  2. Inside Clawdbot: Agent System & AI Providers
  3. Infrastructure, Security & Plugin SDK (this post)

Resources