Skip to main content

Overview

NanoClaw Pro runs as a macOS launchd service (com.nanoclaw) that starts automatically on login and restarts if it crashes. The service manages the main Node.js process, which handles channel connections, message polling, and spawning containerized agents.

Prerequisites

Node.js 20 or later
Claude Code CLI authenticated
Container runtime (Apple Container or Docker) installed and running
Build tools (Xcode Command Line Tools)
Install Xcode Command Line Tools if you haven’t already:
xcode-select --install

Installation

1

Clone and set up the project

git clone https://github.com/andrewsiah/nanoclaw-pro.git
cd nanoclaw-pro
npm install
npm run build
The setup script handles Node.js detection, dependency installation, and native module compilation (better-sqlite3).
2

Build the agent container image

./container/build.sh
This creates the nanoclaw-agent:latest image with Node.js 20, Claude Code CLI, Chromium (for browser automation), and the agent-runner code.
3

Configure authentication

Create a .env file in the project root with one of these options:Option 1: Claude Subscription (OAuth)
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...
Extract from ~/.claude/.credentials.json if logged into Claude Code.Option 2: Pay-per-use API Key
ANTHROPIC_API_KEY=sk-ant-api03-...
Only authentication variables are extracted and mounted into containers. Other .env variables stay on the host for security.
4

Configure the launchd service

Edit launchd/com.nanoclaw.plist and replace placeholders:
<key>ProgramArguments</key>
<array>
    <string>/usr/local/bin/node</string>          <!-- {{NODE_PATH}} -->
    <string>/Users/you/nanoclaw-pro/dist/index.js</string>  <!-- {{PROJECT_ROOT}} -->
</array>
<key>WorkingDirectory</key>
<string>/Users/you/nanoclaw-pro</string>  <!-- {{PROJECT_ROOT}} -->
<key>EnvironmentVariables</key>
<dict>
    <key>PATH</key>
    <string>/Users/you/.local/bin:/usr/local/bin:/usr/bin:/bin</string>  <!-- {{HOME}} -->
    <key>HOME</key>
    <string>/Users/you</string>  <!-- {{HOME}} -->
</dict>
<key>StandardOutPath</key>
<string>/Users/you/nanoclaw-pro/logs/nanoclaw.log</string>  <!-- {{PROJECT_ROOT}} -->
<key>StandardErrorPath</key>
<string>/Users/you/nanoclaw-pro/logs/nanoclaw.error.log</string>  <!-- {{PROJECT_ROOT}} -->
Use which node to find your Node.js path and echo $HOME for your home directory.
5

Install and start the service

# Copy to LaunchAgents directory
cp launchd/com.nanoclaw.plist ~/Library/LaunchAgents/

# Load the service (starts immediately and on every login)
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist

Service Management

Check Service Status

# Check if service is loaded
launchctl list | grep nanoclaw

# View recent logs
tail -f logs/nanoclaw.log

# View error logs
tail -f logs/nanoclaw.error.log

Start/Stop/Restart

launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
Stopping the service does not kill running containers. Agent containers are spawned with --rm (auto-cleanup) but remain detached. They’ll finish their work or timeout naturally.

Uninstall

# Stop and unload service
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist

# Remove service file
rm ~/Library/LaunchAgents/com.nanoclaw.plist

# Optional: Clean up data
rm -rf store/ data/ logs/

Log Files

FileContents
logs/nanoclaw.logMain process stdout (message polling, channel connections, agent spawns)
logs/nanoclaw.error.logMain process stderr (errors, warnings)
groups/{name}/logs/container-*.logPer-agent execution logs (one per container invocation)
Container logs are named with timestamps: container-20260307-143022.log

Service Configuration

The launchd plist includes these key settings:
<key>RunAtLoad</key>
<true/>  <!-- Start immediately when loaded -->

<key>KeepAlive</key>
<true/>  <!-- Restart if process crashes -->

<key>ASSISTANT_NAME</key>
<string>Andy</string>  <!-- Trigger word: @Andy -->

Changing the Assistant Name

Edit the plist’s EnvironmentVariables section:
<key>ASSISTANT_NAME</key>
<string>YourName</string>
Then restart:
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
Messages must now start with @YourName to trigger the assistant.

Container Runtime

NanoClaw Pro auto-detects and starts your container runtime on launch. It supports:
  • Docker (default): CONTAINER_RUNTIME_BIN=docker in src/container-runtime.ts
  • Apple Container: Switch with /convert-to-apple-container skill
The service automatically:
  1. Checks if runtime is running (docker info)
  2. Kills orphaned nanoclaw-* containers from previous runs
  3. Spawns fresh containers for each agent invocation

Apple Container Networking

If using Apple Container, configure networking for internet access:
# Enable IP forwarding
sudo sysctl -w net.inet.ip.forwarding=1

# Enable NAT (requires natpmpd or pf rules)
# See docs/APPLE-CONTAINER-NETWORKING.md for details
Docker handles networking automatically. Apple Container requires manual NAT setup.

Startup Sequence

When the service starts, NanoClaw:
  1. Ensures container runtime is running (auto-starts if needed)
  2. Cleans up orphaned containers from crashed runs
  3. Initializes SQLite database (store/messages.db)
  4. Loads state: registered groups, sessions, router state
  5. Connects channels (loops through registry, instantiates those with credentials)
  6. Starts scheduler loop (checks for due tasks every 60s)
  7. Starts IPC watcher (monitors data/ipc/ for container messages)
  8. Recovers unprocessed messages from before shutdown
  9. Starts message polling loop (checks SQLite every 2s)

Environment Variables

Set in the plist’s EnvironmentVariables dict:
VariableDefaultPurpose
ASSISTANT_NAMEAndyTrigger word for messages
CONTAINER_IMAGEnanoclaw-agent:latestAgent container image name
CONTAINER_TIMEOUT1800000 (30 min)Max container runtime (ms)
IDLE_TIMEOUT1800000 (30 min)Keep container alive after last result
MAX_CONCURRENT_CONTAINERS5Max parallel agent containers
PATHStandard pathsInclude ~/.local/bin for Claude Code CLI

Security

The service runs as your user (not root) and all agents execute in containers:
  • Filesystem isolation: Agents only access mounted directories
  • Safe Bash: Commands run in container, not on host
  • Credential isolation: Per-group .claude/ sessions
  • Mount shadowing: Main group’s project root mount shadows .env with /dev/null to prevent secret access
Review src/mount-security.ts for the allowlist of directories agents can access.

Next Steps

Linux Deployment

Deploy with systemd on Linux servers

Troubleshooting

Common issues and solutions

Build docs developers (and LLMs) love