* Add secure mount allowlist validation Addresses arbitrary host mount vulnerability by validating additional mounts against an external allowlist stored at ~/.config/nanoclaw/. This location is never mounted into containers, making it tamper-proof. Security measures: - Allowlist cached in memory (edits require process restart) - Real path resolution (blocks symlink and .. traversal attacks) - Blocked patterns for sensitive paths (.ssh, .gnupg, .aws, etc.) - Non-main groups forced to read-only when nonMainReadOnly is true - Container path validation prevents /workspace/extra escape https://claude.ai/code/session_01BPqdNy4EAHHJcdtZ27TXkh * Add mount allowlist setup to /setup skill Interactive walkthrough that asks users: - Whether they want agents to access external directories - Which directories to allow (with paths) - Read-write vs read-only for each - Whether non-main groups should be restricted to read-only Creates ~/.config/nanoclaw/mount-allowlist.json based on answers. https://claude.ai/code/session_01BPqdNy4EAHHJcdtZ27TXkh --------- Co-authored-by: Claude <noreply@anthropic.com>
11 KiB
name, description
| name | description |
|---|---|
| setup | Run initial NanoClaw setup. Use when user wants to install dependencies, authenticate WhatsApp/Gmail, register their main channel, or start the background services. Triggers on "setup", "install", "configure nanoclaw", or first-time setup requests. |
NanoClaw Setup
Run all commands automatically. Only pause when user action is required (scanning QR codes).
1. Install Dependencies
npm install
2. Install Apple Container
Check if Apple Container is installed:
which container && container --version || echo "Not installed"
If not installed, tell the user:
Apple Container is required for running agents in isolated environments.
- Download the latest
.pkgfrom https://github.com/apple/container/releases- Double-click to install
- Run
container system startto start the serviceLet me know when you've completed these steps.
Wait for user confirmation, then verify:
container system start
container --version
Note: NanoClaw automatically starts the Apple Container system when it launches, so you don't need to start it manually after reboots.
3. Configure Claude Authentication
Ask the user:
Do you want to use your Claude subscription (Pro/Max) or an Anthropic API key?
Option 1: Claude Subscription (Recommended)
Ask the user:
Want me to grab the OAuth token from your current Claude session?
If yes:
TOKEN=$(cat ~/.claude/.credentials.json 2>/dev/null | jq -r '.claudeAiOauth.accessToken // empty')
if [ -n "$TOKEN" ]; then
echo "CLAUDE_CODE_OAUTH_TOKEN=$TOKEN" > .env
echo "Token configured: ${TOKEN:0:20}...${TOKEN: -4}"
else
echo "No token found - are you logged in to Claude Code?"
fi
If the token wasn't found, tell the user:
Run
claudein another terminal and log in first, then come back here.
Option 2: API Key
Ask if they have an existing key to copy or need to create one.
Copy existing:
grep "^ANTHROPIC_API_KEY=" /path/to/source/.env > .env
Create new:
echo 'ANTHROPIC_API_KEY=' > .env
Tell the user to add their key from https://console.anthropic.com/
Verify:
KEY=$(grep "^ANTHROPIC_API_KEY=" .env | cut -d= -f2)
[ -n "$KEY" ] && echo "API key configured: ${KEY:0:10}...${KEY: -4}" || echo "Missing"
4. Build Container Image
Build the NanoClaw agent container:
./container/build.sh
This creates the nanoclaw-agent:latest image with Node.js, Chromium, Claude Code CLI, and agent-browser.
Verify the build succeeded (the container images command may not work due to a plugin issue, so we verify by running a simple test):
echo '{}' | container run -i --entrypoint /bin/echo nanoclaw-agent:latest "Container OK" || echo "Container build failed"
5. WhatsApp Authentication
USER ACTION REQUIRED
Run the authentication script:
npm run auth
Tell the user:
A QR code will appear. On your phone:
- Open WhatsApp
- Tap Settings → Linked Devices → Link a Device
- Scan the QR code
Wait for the script to output "Successfully authenticated" then continue.
If it says "Already authenticated", skip to the next step.
6. Configure Assistant Name
Ask the user:
What trigger word do you want to use? (default:
Andy)Messages starting with
@TriggerWordwill be sent to Claude.
If they choose something other than Andy, update it in these places:
groups/CLAUDE.md- Change "# Andy" and "You are Andy" to the new namegroups/main/CLAUDE.md- Same changes at the topdata/registered_groups.json- Use@NewNameas the trigger when registering groups
Store their choice - you'll use it when creating the registered_groups.json and when telling them how to test.
7. Register Main Channel
Ask the user:
Do you want to use your personal chat (message yourself) or a WhatsApp group as your main control channel?
For personal chat:
Send any message to yourself in WhatsApp (the "Message Yourself" chat). Tell me when done.
For group:
Send any message in the WhatsApp group you want to use as your main channel. Tell me when done.
After user confirms, start the app briefly to capture the message:
timeout 10 npm run dev || true
Then find the JID from the database:
# For personal chat (ends with @s.whatsapp.net)
sqlite3 store/messages.db "SELECT DISTINCT chat_jid FROM messages WHERE chat_jid LIKE '%@s.whatsapp.net' ORDER BY timestamp DESC LIMIT 5"
# For group (ends with @g.us)
sqlite3 store/messages.db "SELECT DISTINCT chat_jid FROM messages WHERE chat_jid LIKE '%@g.us' ORDER BY timestamp DESC LIMIT 5"
Create/update data/registered_groups.json using the JID from above and the assistant name from step 5:
{
"JID_HERE": {
"name": "main",
"folder": "main",
"trigger": "@ASSISTANT_NAME",
"added_at": "CURRENT_ISO_TIMESTAMP"
}
}
Ensure the groups folder exists:
mkdir -p groups/main/logs
8. Configure External Directory Access (Mount Allowlist)
Ask the user:
Do you want the agent to be able to access any directories outside the NanoClaw project?
Examples: Git repositories, project folders, documents you want Claude to work on.
Note: This is optional. Without configuration, agents can only access their own group folders.
If no, create an empty allowlist to make this explicit:
mkdir -p ~/.config/nanoclaw
cat > ~/.config/nanoclaw/mount-allowlist.json << 'EOF'
{
"allowedRoots": [],
"blockedPatterns": [],
"nonMainReadOnly": true
}
EOF
echo "Mount allowlist created - no external directories allowed"
Skip to the next step.
If yes, ask follow-up questions:
8a. Collect Directory Paths
Ask the user:
Which directories do you want to allow access to?
You can specify:
- A parent folder like
~/projects(allows access to anything inside)- Specific paths like
~/repos/my-appList them one per line, or give me a comma-separated list.
For each directory they provide, ask:
Should
[directory]be read-write (agents can modify files) or read-only?Read-write is needed for: code changes, creating files, git commits Read-only is safer for: reference docs, config examples, templates
8b. Configure Non-Main Group Access
Ask the user:
Should non-main groups (other WhatsApp chats you add later) be restricted to read-only access even if read-write is allowed for the directory?
Recommended: Yes - this prevents other groups from modifying files even if you grant them access to a directory.
8c. Create the Allowlist
Create the allowlist file based on their answers:
mkdir -p ~/.config/nanoclaw
Then write the JSON file. Example for a user who wants ~/projects (read-write) and ~/docs (read-only) with non-main read-only:
cat > ~/.config/nanoclaw/mount-allowlist.json << 'EOF'
{
"allowedRoots": [
{
"path": "~/projects",
"allowReadWrite": true,
"description": "Development projects"
},
{
"path": "~/docs",
"allowReadWrite": false,
"description": "Reference documents"
}
],
"blockedPatterns": [],
"nonMainReadOnly": true
}
EOF
Verify the file:
cat ~/.config/nanoclaw/mount-allowlist.json
Tell the user:
Mount allowlist configured. The following directories are now accessible:
~/projects(read-write)~/docs(read-only)Security notes:
- Sensitive paths (
.ssh,.gnupg,.aws, credentials) are always blocked- This config file is stored outside the project, so agents cannot modify it
- Changes require restarting the NanoClaw service
To grant a group access to a directory, add it to their config in
data/registered_groups.json:"containerConfig": { "additionalMounts": [ { "hostPath": "~/projects/my-app", "containerPath": "my-app", "readonly": false } ] }
9. Gmail Authentication (Optional)
Ask the user:
Do you want to enable Gmail integration for reading/sending emails?
Note: This requires setting up Google Cloud Platform OAuth credentials, which involves:
- Creating a GCP project
- Enabling the Gmail API
- Creating OAuth 2.0 credentials
- Downloading a credentials file
This takes about 5-10 minutes. Skip if you don't need email integration.
If yes, guide them through the prerequisites:
- Go to https://console.cloud.google.com
- Create a new project (or use an existing one)
- Enable the Gmail API (APIs & Services → Enable APIs → search "Gmail API")
- Create OAuth 2.0 credentials (APIs & Services → Credentials → Create Credentials → OAuth client ID → Desktop app)
- Download the JSON file and save to
~/.gmail-mcp/gcp-oauth.keys.json
Then run:
npx -y @gongrzhe/server-gmail-autoauth-mcp
This will open a browser for OAuth consent. After authorization, credentials are cached.
10. Configure launchd Service
Get the actual paths:
which node
pwd
Create the plist file at ~/Library/LaunchAgents/com.nanoclaw.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.nanoclaw</string>
<key>ProgramArguments</key>
<array>
<string>NODE_PATH_HERE</string>
<string>PROJECT_PATH_HERE/dist/index.js</string>
</array>
<key>WorkingDirectory</key>
<string>PROJECT_PATH_HERE</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:HOME_PATH_HERE/.local/bin</string>
<key>HOME</key>
<string>HOME_PATH_HERE</string>
</dict>
<key>StandardOutPath</key>
<string>PROJECT_PATH_HERE/logs/nanoclaw.log</string>
<key>StandardErrorPath</key>
<string>PROJECT_PATH_HERE/logs/nanoclaw.error.log</string>
</dict>
</plist>
Replace the placeholders with actual paths from the commands above.
Build and start the service:
npm run build
mkdir -p logs
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
Verify it's running:
launchctl list | grep nanoclaw
11. Test
Tell the user (using the assistant name they configured):
Send
@ASSISTANT_NAME helloin your registered chat.
Check the logs:
tail -f logs/nanoclaw.log
The user should receive a response in WhatsApp.
Troubleshooting
Service not starting: Check logs/nanoclaw.error.log
Container agent fails with "Claude Code process exited with code 1":
- Ensure Apple Container is running:
container system start - Check container logs:
cat groups/main/logs/container-*.log | tail -50
No response to messages:
- Verify the trigger pattern matches (e.g.,
@AssistantNameat start of message) - Check that the chat JID is in
data/registered_groups.json - Check
logs/nanoclaw.logfor errors
WhatsApp disconnected:
- The service will show a macOS notification
- Run
npm run authto re-authenticate - Restart the service:
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
Unload service:
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist