aidc — short for AI Dev Container — is a one-command devcontainer wrapper for AI coding agents (claude, codex, opencode, grok, omp, cursor-agent). It scaffolds a hardened Linux container per repo, mounts your code at /workspace, persists agent state in named Docker volumes (so agents don't read your ~/.ssh or your shell history), and bakes in always-on security scanners and supply-chain guardrails.
If you're already running these agents directly on your Mac and have been quietly nervous about it, this is for you.
Pre-1.0, rolling-release, personal-ish. The author uses it daily; the API may still shift. Issues welcome; PRs at the maintainer's discretion. See SECURITY.md for vuln disclosure.
- macOS (host-side bits assume Mac — Keychain, LaunchAgent,
pbpaste,~/.local/binaliases) - Docker running (Docker Desktop / OrbStack / Colima) — or, experimentally, Apple's native
containerruntime via socktainer (see docs/apple-container.md) - git
- (optional, high-security mode) Lima on macOS or Firecracker on Linux — only needed if you enable
--isolate-vm
git clone https://github.com/cyfinoid/aidc.git
cd aidc
./install.shMake sure ~/.local/bin is on your PATH.
cd /path/to/cloned/repo
aidc init # one-time scaffold; writes .devcontainer/, .ai-container/, CLAUDE.md, AGENTS.md, CHANGELOG.md, DETAILED_CHANGELOG.md, logs/
aidc claude # auto-runs `aidc up` if needed, then drops you into Claude Code in the containerTool commands (aidc claude / codex / opencode / grok / omp / cursor-agent) auto-bootstrap the container on first run.
aidc claude needs a Claude credential inside the container. The recommended, durable setup is a long-lived OAuth token kept in your macOS Keychain — aidc reads it on demand at runtime, so the token is never exported into every shell and never written to a dotfile.
# Mint a long-lived token and store it in the Keychain in one step.
# `tr -d '[:space:]'` strips the surrounding whitespace/newline so only the token is stored.
security add-generic-password -U -a "$USER" -s claude-code-oauth-token \
-w "$(claude setup-token | rg sk-ant | tr -d '[:space:]')"No ripgrep? Swap rg sk-ant for grep sk-ant. You can also run the two steps separately — claude setup-token prints the sk-ant-oat01-… value, then pass it to security add-generic-password … -w '<token>'.
That's it — this is a one-time setup that survives across repos and aidc destroy. aidc claude resolves CLAUDE_CODE_OAUTH_TOKEN from the Keychain on every run (no per-repo login, no ~/.zshrc export). The first run prompts macOS to allow security to read the item — click Always Allow.
Don't use the short-lived
accessTokenfrom Claude Code's ownClaude Code-credentialsKeychain item. That token expires within hours and is only refreshed when you run Claude Code on the host, so it goes stale for container-only use. Theclaude setup-tokenvalue above is long-lived and built for exactly this.
Already exporting CLAUDE_CODE_OAUTH_TOKEN in your shell still works (it takes precedence). Override the Keychain service name — or disable the lookup — with AIDC_CLAUDE_OAUTH_KEYCHAIN_SERVICE in ~/.config/aidc/config.env. Alternate API targets (Z.ai, OpenRouter, local models) use profiles instead — see docs/claude-profiles.md.
- creates local-only
.devcontainer/,.ai-container/,CLAUDE.md,AGENTS.md, and.cursor/rules/00-core-logics.mdc - mounts project code only at
/workspace; overlays/workspace/.devcontainerread-only inside the container - installs the coding agents (
claude,codex,opencode,grok,omp) as native prebuilt binaries — no npm-global, no Node runtime dependency for the agents themselves - persists tool state in per-repo Docker volumes instead of mounting whole host homes
- seeds selected config from host read-only mounts on first startup
- creates one
CORE_LOGICSgit worktree per repo and mounts it at/opt/CORE_LOGICSfor shared cross-repo notes - detects the project's toolchains (Go, Rust, Ruby, Java, PHP, Node, Python — plus shell scripts) and installs them automatically;
aidc rescanre-detects later for a repo that started empty - shares one
aidc-baseimage (OS, Python, scanners, agents) across all projects — each project's image is a thin toolchain delta on top — and keeps Go/Rust/JDK in one read-onlyaidc_toolchainsvolume (aidc tools install) instead of a full copy per project - bakes always-on security scanners (
semgrep,gitleaks,trufflehog) plus per-toolchain linters (gosec,bandit,cargo-audit,bundler-audit,shellcheck) into the image - seeds non-negotiable guidance into
CLAUDE.md/AGENTS.mdfor every project — security guardrails, test-coverage discipline, and changelog/session-log conventions - seeds committed project docs once, never overwriting your edits —
CHANGELOG.md,DETAILED_CHANGELOG.md, and alogs/session journal - auto-syncs in-container agent session transcripts back to the host on container start and exit, so the host's
/insightsstays current - ships SafeDep's
pmg/vetfor supply-chain interception andrtkfor token-saving CLI proxying — rtk is wired into every agent it supports (claude, opencode, cursor-agent, omp experimentally), prints a savings summary when a Claude session ends, persists its history across rebuilds, and merges container savings into the host's own rtk db so a plain hostrtk gainshows the combined total - offers an opt-in default-deny egress firewall with a sane allowlist
docs/install.md— prereqs, platform matrix, install, daily commands, what lives where, per-project customisation, cleanupdocs/claude-profiles.md— alternate Claude API targets, local-model profiles, one-time OAuth login, session syncdocs/cursor.md— host Cursor GUI with container-side execution ("Reopen in Container"),aidc cursor-agent,CURSOR_API_KEYauthdocs/security.md—aidc scan, scanners, image supply chain, container hardening, agent guardrails (rtk), opt-in egress firewalldocs/troubleshooting.md— symptom → cause → fix for the common failures (aidc doctorfirst)docs/uninstall.md— per-project and host-wide removaldocs/releasing.md— how releases are cutdocs/clipboard-bridge.md— host-clipboard → container PNG paste bridgedocs/local-ci.md—aidc ci: replaying the wrapped project's GitHub workflows locally (opt-in)CHANGELOG.md— high-level release notes;DETAILED_CHANGELOG.md— long-form per-change rationaleSECURITY.md— how to report vulnerabilities in aidc itself
aidc init [path]
aidc up [--clipboard] [--isolate-vm]
aidc down
aidc rebuild [--clipboard] [--isolate-vm]
aidc rescan
aidc tools <install [go|rust|java|all]|status>
aidc status [--global]
aidc destroy [-f] [--purge-worktree] [--purge-scaffold]
aidc shell
aidc exec -- <command>...
aidc claude [--profile NAME] [--provider NAME] [--list-profiles] [-- ...]
aidc codex [-- ...]
aidc opencode [-- ...]
aidc opencode-web [--port N] [--no-auth] [--username NAME] [-- ...]
aidc grok [-- ...]
aidc omp [-- ...]
aidc cursor-agent [-- ...]
aidc cursor
aidc sync-claude-aliases
aidc sync-config <claude|codex|opencode|grok|omp|all>
aidc sync-sessions [claude|codex|opencode|grok|omp|all]
aidc sbom
aidc licenses [--fail]
aidc scan [--all|--staged|paths...] [--json]
aidc ci [--list|--workflow <glob>|--job <id>|--all|--strict|-- ...]
aidc doctor
aidc insights [--since DATE]
aidc update
aidc upgrade [--dry-run|--diff] [-y]
aidc versionaidc status shows the container + mounts/config for the current folder. --global lists every aidc container on the host with disk/CPU/memory and a totals line.
aidc rescan re-detects the project's languages and rebuilds, so a repo that started empty (or single-language) picks up the matching toolchains and security scanners once code lands. shellcheck installs automatically when shell scripts are present.
Session transcripts auto-sync from the container to the host on container start, agent exit, aidc down, and aidc destroy (before its volumes are removed), so /insights on the host stays current without a manual aidc sync-sessions. The start sync is the safety net for ungraceful exits (crash / docker kill) that the on-exit hooks miss — it catches up anything left in the volume.
Toggle it with AIDC_AUTO_SYNC_SESSIONS: set it host-wide in ~/.config/aidc/config.env (universal default for every project) or per project in .ai-container/project.env (overrides the global default). 0 disables auto-sync; manual aidc sync-sessions always works regardless.
aidc opencode-web gives opencode its "desktop feeling" inside the container: it runs opencode's browser UI (opencode web) and publishes it on the host loopback at http://127.0.0.1:4096/ (change with --port N or AIDC_OPENCODE_WEB_PORT), so a host browser drives an agent that lives in the reproducible container — while the LAN never sees it (opencode binds 0.0.0.0 inside the container; the host publish is 127.0.0.1-only). Auth is on by default: a random OPENCODE_SERVER_PASSWORD is generated and printed (set your own by exporting it first; --username overrides the default opencode user). --no-auth disables it — safe only because the port is loopback-only. Opting in (re)creates the container to add the port, exactly like the firewall/hardened overrides; a later plain aidc <tool> recreates it back without the port.
--provider remains as a compatibility alias for --profile.
You can keep the IDE running on your host and have all the actual work — editing, terminals, builds, agents, scanners — happen inside the aidc container:
aidc cursor(or open the folder in Cursor/VS Code).- Command Palette → Dev Containers: Reopen in Container.
aidc's scaffolded devcontainer.json handles the setup the Dev Containers extension doesn't do on its own: its initializeCommand runs aidc up on the host first, which writes .devcontainer/.env (so the extension's own docker compose up resolves the same AIDC_* bind sources and COMPOSE_PROJECT_NAME aidc uses), builds the shared base image, and creates the toolchain volume. Then the extension attaches to that same container. The integrated terminal is the aidc zsh, /workspace is your repo, and the security guardrails all apply.
aidc cursor-agent runs Cursor's CLI agent inside the container instead. Its config/login live under ~/.cursor (persisted in a named volume; seed host settings with aidc sync-config cursor, or authenticate with cursor-agent login in the container / a CURSOR_API_KEY).
macOS note:
initializeCommandusesbash -lc 'aidc up'so~/.local/bin/aidcis found even when the IDE is launched from the GUI. If it still isn't found, editinitializeCommandin.devcontainer/devcontainer.jsonto aidc's absolute path.
aidc runs in one of two isolation modes. Normal mode is the default and is what most people should use.
Runs your project inside a Docker container. On macOS, Docker Desktop/OrbStack/Colima already wraps that container inside a Linux VM — your code is isolated from the host by both the container boundary and the VM boundary. All aidc containers share the same Docker VM, so they're isolated from each other by standard container namespacing (PID, network, filesystem, IPC) but not by a hypervisor boundary.
This is fine for practically everyone. The container + VM double boundary on macOS, combined with aidc's always-on scanners, read-only mounts, named volumes (no host home directory access), and optional egress firewall, already provides strong isolation between the AI agent and your host system.
Experimental: Apple
containerprovider. On macOS 26 + Apple Silicon you can point aidc at Apple's nativecontainerruntime (via the socktainer Docker-API shim) withAIDC_DOCKER_PROVIDER=apple. It runs each container in its own lightweight VM, so it provides per-container VM isolation without--isolate-vm. Unverified end-to-end — see docs/apple-container.md.
Spawns each project in its own lightweight VM instead of sharing a single Docker VM.
| Normal | High-security | |
|---|---|---|
| macOS | Docker container inside shared Docker VM | Dedicated Lima VM per project |
| Linux | Docker container (shared kernel) | Dedicated Firecracker microVM per project |
| Isolation boundary | Container namespaces | Hypervisor (hardware-enforced) |
| Per-project overhead | ~50–100 MB RAM | ~512 MB–1 GB RAM + ~1 GB disk per VM |
| Startup time | ~2–5 s | ~10–30 s (VM boot + container init) |
| Use when | Everyday development | See below |
Enable it per-project:
aidc up --isolate-vm
# or persist it:
echo "AIDC_ISOLATE_VM=1" >> .ai-container/project.envResource warning: Each isolated VM consumes significantly more CPU, RAM, and disk than a shared Docker container. On a machine with 8 GB RAM, running more than 2–3 isolated projects simultaneously will be uncomfortable. Use this mode only when you have a clear reason.
- You're running AI agents against proprietary or regulated codebases (e.g., financial, healthcare, defense) where a container escape — even theoretical — is unacceptable.
- You don't trust the Docker VM shared-tenant model and want hardware-enforced hypervisor boundaries between projects.
- You're on Linux where normal Docker containers share the host kernel directly (no nested VM), so a kernel exploit in one container could affect the host and sibling containers.
- You're running untrusted or third-party agent code (custom MCP servers, community tool plugins) and want an additional containment layer.
- You're a solo developer on a personal machine. The attacker model here is "the AI agent goes rogue" — and aidc's default container isolation, volume architecture, scanner enforcement, and optional egress firewall already handle that scenario well.
- You're on macOS and already trust Docker Desktop / OrbStack. Your containers are already inside a VM. A breakout requires two independent escapes (container → VM, then VM → host). Adding a per-project VM adds a third boundary, but the incremental security gain is small compared to the resource cost.
- You're just trying aidc out. Start with normal mode. You can always switch later with
aidc rebuild --isolate-vm.
Note: Linux + Firecracker support is included in the codebase but is not yet enabled by default. aidc currently ships as macOS-first. If you're on Linux and want to experiment, set
AIDC_ISOLATE_VM=1and ensurefirecrackeris installed — but expect rough edges.
- Generated files are added to
.git/info/excludewhen the target directory is a git repo, so your project stays clean. The seeded project docs (CHANGELOG.md,DETAILED_CHANGELOG.md,logs/) are not excluded — they belong to your repo and are meant to be committed. - Updating:
aidc updatepulls the latest aidc (ff-only) and re-runs the installer;aidc upgradethen brings an existing project's scaffold up to the new templates — it shows a diff first, backs up anything it rewrites (to.ai-container/backup/), and never touches user-owned files. Implicit commands (aidc up,aidc claude, …) only create missing scaffold files and print a one-line notice when the scaffold is out of date; they never rewrite your files.aidc doctordiagnoses common setup problems. - Settings can be set host-wide in
~/.config/aidc/config.env(universal defaults for every project) or per project in.ai-container/project.env, which overrides the global default. Both files are sourced for env vars likeAIDC_AUTO_SYNC_SESSIONS,AIDC_ENABLE_EGRESS_FIREWALL, andAIDC_ISOLATE_VM. - Container egress is open by default; set
AIDC_ENABLE_EGRESS_FIREWALL=1in.ai-container/project.envfor a default-deny allowlist. Seedocs/security.md. - The host-clipboard bridge is off by default — no host clipboard socket is mounted into the container. Opt in per (re)create with
aidc up --clipboard/aidc rebuild --clipboard, or persistAIDC_ENABLE_CLIPBOARD=1in.ai-container/project.env. Seedocs/clipboard-bridge.md. - Per-project VM isolation (
--isolate-vm) is off by default due to resource cost. Opt in per (re)create withaidc up --isolate-vm/aidc rebuild --isolate-vm, or persistAIDC_ISOLATE_VM=1in.ai-container/project.env. See Isolation modes above. - Generated Claude alias wrappers are
aidc-managed and live in~/.local/binby default.
This project was developed with the assistance of AI tools, most notably Cursor IDE and Claude Code. These tools helped accelerate development and improve velocity. All AI-generated code has been carefully reviewed and validated through human inspection to ensure it aligns with the project's intended functionality and quality standards.
GPL-3.0-only. See LICENSE.