Skip to content

feat: add Docker isolation mode for safer YOLO execution#26

Merged
johannesjo merged 6 commits intomainfrom
claude/docker-yolo-safety-5mwI8
Mar 20, 2026
Merged

feat: add Docker isolation mode for safer YOLO execution#26
johannesjo merged 6 commits intomainfrom
claude/docker-yolo-safety-5mwI8

Conversation

@johannesjo
Copy link
Owner

When Docker is available, tasks can opt into running inside a container.
Only the project directory is bind-mounted (read-write), so agents
cannot accidentally delete or modify files outside the project.

  • spawnAgent() wraps command in docker run when dockerMode is set
  • Mounts ~/.ssh and ~/.gitconfig read-only for git operations
  • Forwards API keys and git identity env vars into container
  • NewTaskDialog shows "Run in Docker container" toggle when Docker detected
  • SettingsDialog allows configuring the default Docker image
  • Docker availability checked at app startup via docker info
  • Full persistence of dockerMode/dockerImage per task

https://claude.ai/code/session_012QrKQQBc2hmjhPmkH2Fk6T

claude added 6 commits March 20, 2026 00:15
When Docker is available, tasks can opt into running inside a container.
Only the project directory is bind-mounted (read-write), so agents
cannot accidentally delete or modify files outside the project.

- spawnAgent() wraps command in `docker run` when dockerMode is set
- Mounts ~/.ssh and ~/.gitconfig read-only for git operations
- Forwards API keys and git identity env vars into container
- NewTaskDialog shows "Run in Docker container" toggle when Docker detected
- SettingsDialog allows configuring the default Docker image
- Docker availability checked at app startup via `docker info`
- Full persistence of dockerMode/dockerImage per task

https://claude.ai/code/session_012QrKQQBc2hmjhPmkH2Fk6T
- Container lifecycle: named containers (--name), docker stop on kill,
  --label for identification, --network host, resource limits (8g/512 pids)
- Env forwarding: switched from narrow allowlist to blocklist approach —
  all env vars forwarded except desktop/Electron/linker-specific ones
- Credential mounts: added ~/.config/gh, ~/.npmrc, ~/.netrc,
  GOOGLE_APPLICATION_CREDENTIALS (all read-only)
- UX: auto-enable Docker when skip-permissions toggled on, hint when
  YOLO without Docker, Docker unavailable suggestion, "Docker" badge
  on task headers, shell sessions inherit Docker mode from parent task

https://claude.ai/code/session_012QrKQQBc2hmjhPmkH2Fk6T
- Add docker/Dockerfile with git, Node 20, Python 3, gh CLI, ripgrep,
  fd, fzf, build-essential, and other common dev tools pre-installed
- Change default image from ubuntu:latest to parallel-code-agent:latest
- Add IPC channels: CheckDockerImageExists, BuildDockerImage
- Backend streams docker build output to renderer for progress display
- NewTaskDialog shows image status, "Build Image" button when image
  not found locally, and build progress/output
- Bundle Dockerfile in extraResources for production builds

https://claude.ai/code/session_012QrKQQBc2hmjhPmkH2Fk6T
Dockerfile:
- Replace curl|bash with proper apt keyring method for NodeSource
- Upgrade to Node 22 LTS (Node 20 EOL April 2026)
- Combine apt repo setup into single layer for cache efficiency
- Add git-lfs, procps, less; remove cmake
- Create non-root agent user (uid 1000)
- Pin npm@10 instead of npm@latest

Backend (pty.ts):
- Fix __dirname undefined in ESM — add fileURLToPath polyfill
- Block SSH_AUTH_SOCK, GPG_AGENT_INFO, KUBECONFIG, SUDO_* from
  leaking into containers
- Use 12-char agentId prefix for container names (was 8)
- Add --user flag to match host UID/GID
- Use synchronous docker kill in killAllAgents to prevent orphans
- Rename isDockerImageExists → dockerImageExists
- Add build deduplication guard (activeBuild singleton)

Frontend (NewTaskDialog.tsx):
- Reset docker state signals on dialog reopen
- Debounce image existence check (300ms)
- Auto-scroll build output <pre> to bottom

UI (TaskPanel.tsx):
- Move Docker badge before task name (consistent with directMode badge)
- Normalize badge padding/opacity to match directMode style

https://claude.ai/code/session_012QrKQQBc2hmjhPmkH2Fk6T
Replace `containerName!` with a local `const name = containerName as string`
inside the dockerMode guard where the value is guaranteed non-null.

https://claude.ai/code/session_012QrKQQBc2hmjhPmkH2Fk6T
…, plan watcher leak, docker defaults, notifications, async docker checks

1. Add missing check_docker_image_exists and build_docker_image to preload
   allowlist — these IPC calls were silently blocked at runtime.

2. Restore SpawnAgent input validation for command, args, taskId, agentId,
   cols, rows, dockerMode, and dockerImage.

3. Restore UUID suffix in branch names to prevent collisions when two tasks
   share the same name.

4. Add StopPlanWatcher IPC channel and call it from collapseTask/closeTask
   to prevent FSWatcher accumulation for closed/collapsed tasks.

5. Fix docker image default inconsistency: persistence.ts and
   SettingsDialog.tsx now use 'parallel-code-agent:latest' consistently
   instead of 'ubuntu:latest'.

6. Add Notification.isSupported() guard and 30-second Linux timeout fallback
   to ShowNotification handler.

7. Replace execFileSync with async execFile in isDockerAvailable and
   dockerImageExists to avoid blocking the Electron main thread.

https://claude.ai/code/session_012QrKQQBc2hmjhPmkH2Fk6T
@johannesjo johannesjo merged commit 511af86 into main Mar 20, 2026
1 of 2 checks passed
@johannesjo johannesjo deleted the claude/docker-yolo-safety-5mwI8 branch March 20, 2026 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants