Container Isolation Model
NanoClaw Pro runs all agents in isolated Linux containers (lightweight VMs). Each group’s agent can only access explicitly mounted directories.
Containers provide true OS-level isolation, not just application-level permissions. Bash commands run safely inside the container, not on your host.
Default Mounts
Every agent container has these mounts:
Host Path Container Path Purpose Read-only groups/{name}//workspace/groupGroup-specific memory and files No groups/global//workspace/global/Shared global memory (non-main groups) Yes data/sessions/{group}/.claude//home/node/.claude/Session transcripts No data/env/env/workspace/env-dir/envEnvironment variables Yes data/ipc//workspace/ipc/Host communication (tasks, messages) No
The global memory mount (groups/CLAUDE.md) is read-only for non-main groups. Only the main channel can write to global memory.
Additional Mounts
Give specific groups access to external directories using containerConfig in the group registration.
Mount Configuration Structure
interface ContainerConfig {
additionalMounts ?: Array <{
hostPath : string ; // Absolute path or ~/ for home
containerPath : string ; // Relative to /workspace/extra/
readonly ?: boolean ; // Default: false
}>;
timeout ?: number ; // Override CONTAINER_TIMEOUT for this group
}
Example: Mount a Project Directory
Register a group with access to a codebase:
import { registerGroup } from './src/db.js' ;
registerGroup ( "[email protected] " , {
name: "Dev Team" ,
folder: "whatsapp_dev-team" ,
trigger: "@Andy" ,
added_at: new Date (). toISOString (),
containerConfig: {
additionalMounts: [
{
hostPath: "~/projects/webapp" ,
containerPath: "webapp" ,
readonly: false ,
},
],
},
});
Now agents in that group can access the project at /workspace/extra/webapp/:
# Inside container
cd /workspace/extra/webapp
ls -la
Multiple Mounts
containerConfig : {
additionalMounts : [
{
hostPath: "~/Obsidian/Personal" ,
containerPath: "notes" ,
readonly: true , // Agent can read but not modify
},
{
hostPath: "~/Downloads" ,
containerPath: "downloads" ,
readonly: false ,
},
],
}
Mounted at:
/workspace/extra/notes/ (read-only)
/workspace/extra/downloads/ (read-write)
Mount Security
Allowlist
All additional mounts must be in the mount allowlist . The allowlist is stored outside the project root to prevent agents from modifying it.
Location: ~/.config/nanoclaw/mount-allowlist.json
{
"allowedRoots" : [
"/Users/yourname/projects" ,
"/Users/yourname/Obsidian" ,
"/Users/yourname/Downloads"
],
"blockedPatterns" : [
".* \\ .env$" ,
".*credentials \\ .json$" ,
".*/ \\ .ssh/.*"
],
"nonMainReadOnly" : true
}
Absolute directory paths that can be mounted Mounts must be within these roots (subdirectories allowed).
Regex patterns for paths that can never be mounted Even if inside an allowed root, paths matching these patterns are rejected.
Force all additional mounts to read-only for non-main groups Only the main channel can have read-write access to additional mounts.
Setting Up the Allowlist
During setup (/setup), you’ll be asked to configure the allowlist:
npx tsx setup/index.ts --step mounts -- --json '{"allowedRoots":["/Users/yourname/projects"],"blockedPatterns":[],"nonMainReadOnly":true}'
Or create it manually:
mkdir -p ~/.config/nanoclaw
cat > ~/.config/nanoclaw/mount-allowlist.json << 'EOF'
{
"allowedRoots": ["/Users/yourname/projects"],
"blockedPatterns": [".*\\.env$"],
"nonMainReadOnly": true
}
EOF
Mount Validation
Before spawning a container, NanoClaw validates all mounts against the allowlist in src/mount-security.ts:
export function validateMount (
mount : Mount ,
allowlist : MountAllowlist ,
isMain : boolean ,
) : { allowed : boolean ; reason ?: string } {
// 1. Expand ~ to absolute path
const absoluteHost = expandPath ( mount . hostPath );
// 2. Check blocked patterns
for ( const pattern of allowlist . blockedPatterns ) {
if ( new RegExp ( pattern ). test ( absoluteHost )) {
return { allowed: false , reason: "Path matches blocked pattern" };
}
}
// 3. Check allowed roots
const inAllowedRoot = allowlist . allowedRoots . some (( root ) =>
absoluteHost . startsWith ( expandPath ( root ))
);
if ( ! inAllowedRoot ) {
return { allowed: false , reason: "Path not in allowed roots" };
}
// 4. Enforce read-only for non-main
if ( allowlist . nonMainReadOnly && ! isMain && ! mount . readonly ) {
return { allowed: false , reason: "Non-main groups cannot mount read-write" };
}
return { allowed: true };
}
Never mount sensitive directories like:
.ssh/ (SSH keys)
.aws/ (AWS credentials)
.env files (secrets)
Even though agents run in containers, prompt injection could cause them to leak data.
Container Timeout
Global Timeout
Set via environment variable (applies to all groups):
CONTAINER_TIMEOUT = 1800000 # 30 minutes (default)
Agents that exceed this timeout are terminated.
Per-Group Timeout
Override timeout for specific groups:
registerGroup ( "[email protected] " , {
name: "Long Tasks" ,
folder: "whatsapp_long-tasks" ,
trigger: "@Andy" ,
added_at: new Date (). toISOString (),
containerConfig: {
timeout: 3600000 , // 1 hour (overrides global CONTAINER_TIMEOUT)
},
});
Use longer timeouts for groups that run complex agent swarms or scheduled tasks that process large datasets.
Container Resource Limits
By default, containers have no CPU or memory limits. For production use, consider adding limits:
Docker
Modify src/container-runner.ts to add resource flags:
const spawnArgs = [
'run' ,
'--rm' ,
'-i' ,
'--cpus' , '2' , // Limit to 2 CPU cores
'--memory' , '4g' , // Limit to 4GB RAM
'--memory-swap' , '4g' , // No swap
// ... existing args
];
Apple Container
Apple Container uses macOS resource management by default. For explicit limits:
const spawnArgs = [
'run' ,
'--rm' ,
'-i' ,
'--cpu-limit' , '2' , // Limit to 2 CPU cores
'--memory-limit' , '4096' , // 4GB in MB
// ... existing args
];
Practical Examples
Example 1: Sales Team with CRM Access
registerGroup ( "[email protected] " , {
name: "Sales Team" ,
folder: "whatsapp_sales" ,
trigger: "@Andy" ,
added_at: new Date (). toISOString (),
containerConfig: {
additionalMounts: [
{
hostPath: "~/Dropbox/Sales" ,
containerPath: "sales-data" ,
readonly: true , // Agents can read but not modify
},
],
},
});
Agent can read sales data:
@Andy summarize this month's pipeline from sales-data/pipeline.csv
Example 2: Dev Group with Code Access
registerGroup ( "[email protected] " , {
name: "Dev Team" ,
folder: "whatsapp_dev" ,
trigger: "@Andy" ,
added_at: new Date (). toISOString (),
containerConfig: {
additionalMounts: [
{
hostPath: "~/repos/backend" ,
containerPath: "backend" ,
readonly: false , // Agent can modify code
},
{
hostPath: "~/repos/frontend" ,
containerPath: "frontend" ,
readonly: false ,
},
],
timeout: 3600000 , // 1 hour for complex tasks
},
});
Agent can modify code:
@Andy refactor the auth module in backend/src/auth.ts
Example 3: Personal Assistant with Document Access
registerGroup ( "[email protected] " , {
name: "Main" ,
folder: "whatsapp_main" ,
trigger: "@Andy" ,
isMain: true ,
triggerRequired: false ,
added_at: new Date (). toISOString (),
containerConfig: {
additionalMounts: [
{
hostPath: "~/Obsidian/Vault" ,
containerPath: "notes" ,
readonly: false ,
},
{
hostPath: "~/Documents" ,
containerPath: "docs" ,
readonly: true ,
},
],
},
});
Agent can manage notes and read documents:
Create a summary of docs/quarterly-report.pdf and save it to notes/summaries/q4-2024.md
Mount Syntax Reference
Different container runtimes use different mount syntax. NanoClaw Pro handles this automatically in src/container-runner.ts:
Docker
-v /host/path:/container/path
Docker’s :ro suffix may not work on all runtimes. Use --mount with readonly flag for cross-platform compatibility.
Apple Container
-v /host/path:/container/path
Debugging Container Mounts
To see what’s mounted in a running container:
# List running containers
docker ps # or: container list
# Inspect mounts
docker inspect < container-i d > | jq '.[0].Mounts'
# Execute shell inside container
docker exec -it < container-i d > /bin/sh
ls -la /workspace/extra/
Container logs are in:
tail -f groups/{folder}/logs/container- * .log
Best Practices
Use read-only mounts for sensitive data
If agents only need to read files (documents, notes, reports), mount read-only to prevent accidental modification.
Restrict non-main groups
Set nonMainReadOnly: true in the allowlist to ensure only your main channel can modify external files.
Avoid mounting credentials
Never mount .ssh/, .aws/, .env, or other credential directories. Prompt injection could leak secrets.
Use specific paths, not home directory
Mount ~/projects/webapp instead of ~/. Narrower mounts reduce attack surface.
Test mounts in dev mode first
Before modifying production service: launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
npm run dev
# Send test message
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
Next Steps
Configuration Reference Environment variables and runtime settings
Security Model Understanding container isolation and security