Container execution architecture and runtime details
NanoClaw runs agents in isolated Linux containers to provide security through OS-level process and filesystem isolation. The container runtime is abstracted to support multiple backends.
The slim Node.js base keeps the image small while providing the necessary runtime.
Browser configuration
# Set Chromium path for agent-browserENV AGENT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromiumENV PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium# Install agent-browser and claude-code globallyRUN npm install -g agent-browser @anthropic-ai/claude-code
Browser automation is available to all agents via the agent-browser command.
Agent runner compilation
# Copy package files first for better cachingCOPY agent-runner/package*.json ./RUN npm install# Copy source codeCOPY agent-runner/ ./RUN npm run build
The agent runner is pre-built during image creation for faster startup.
Workspace setup
# Create workspace directoriesRUN mkdir -p /workspace/group /workspace/global /workspace/extra \ /workspace/ipc/messages /workspace/ipc/tasks /workspace/ipc/input# Set ownership to node user (non-root) for writable directoriesRUN chown -R node:node /workspace && chmod 777 /home/node# Switch to non-root userUSER node# Set working directory to group workspaceWORKDIR /workspace/group
Containers run as the unprivileged node user (uid 1000) for security.
Rebuild the container image after modifying the Dockerfile or agent-runner:
./container/build.sh
The container buildkit caches the build context aggressively. --no-cache alone does NOT invalidate COPY steps. To force a clean rebuild, prune the builder:
Secrets are passed via stdin (never written to disk or mounted as files).
5
Stream output
The host parses output markers (---NANOCLAW_OUTPUT_START--- and ---NANOCLAW_OUTPUT_END---) from stdout and calls the onOutput callback for each complete message.
6
Handle completion
When the container exits, logs are written to groups/{name}/logs/container-{timestamp}.log and the promise resolves with the final output.
Mounts are built per-group in src/container-runner.ts:57:
Path
Main Group
Non-Main Group
Mode
Project root
/workspace/project
Not mounted
Read-only
Group folder
/workspace/group
/workspace/group
Read-write
Global memory
Implicit (in project)
/workspace/global
Read-only
Claude sessions
/home/node/.claude
/home/node/.claude
Read-write
IPC namespace
/workspace/ipc
/workspace/ipc
Read-write
Agent runner src
/app/src
/app/src
Read-write
Additional mounts
Configurable
Configurable
Per-config
Each group gets its own copy of the agent-runner source in data/sessions/{group}/agent-runner-src/, allowing per-group customization without affecting other groups.
The idle timeout is currently set equal to the hard timeout (both 30 minutes), causing containers to always exit via SIGKILL instead of graceful shutdown. This is a known issue tracked in docs/DEBUG_CHECKLIST.md:8.