Skip to content

Repository files navigation

aidc

License: GPL v3 Status: alpha macOS only

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.

Status

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.

Prereqs

  • macOS (host-side bits assume Mac — Keychain, LaunchAgent, pbpaste, ~/.local/bin aliases)
  • Docker running (Docker Desktop / OrbStack / Colima) — or, experimentally, Apple's native container runtime 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

Install

git clone https://github.com/cyfinoid/aidc.git
cd aidc
./install.sh

Make sure ~/.local/bin is on your PATH.

Quick start

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 container

Tool commands (aidc claude / codex / opencode / grok / omp / cursor-agent) auto-bootstrap the container on first run.

Claude authentication

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 accessToken from Claude Code's own Claude Code-credentials Keychain 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. The claude setup-token value 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.

What aidc actually does

  • 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/.devcontainer read-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_LOGICS git worktree per repo and mounts it at /opt/CORE_LOGICS for shared cross-repo notes
  • detects the project's toolchains (Go, Rust, Ruby, Java, PHP, Node, Python — plus shell scripts) and installs them automatically; aidc rescan re-detects later for a repo that started empty
  • shares one aidc-base image (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-only aidc_toolchains volume (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.md for 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 a logs/ session journal
  • auto-syncs in-container agent session transcripts back to the host on container start and exit, so the host's /insights stays current
  • ships SafeDep's pmg / vet for supply-chain interception and rtk for 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 host rtk gain shows the combined total
  • offers an opt-in default-deny egress firewall with a sane allowlist

Documentation

Commands

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 version

aidc 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.

Cursor / VS Code as the UI, aidc as the container

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:

  1. aidc cursor (or open the folder in Cursor/VS Code).
  2. 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: initializeCommand uses bash -lc 'aidc up' so ~/.local/bin/aidc is found even when the IDE is launched from the GUI. If it still isn't found, edit initializeCommand in .devcontainer/devcontainer.json to aidc's absolute path.

Isolation modes

aidc runs in one of two isolation modes. Normal mode is the default and is what most people should use.

Normal mode (default)

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 container provider. On macOS 26 + Apple Silicon you can point aidc at Apple's native container runtime (via the socktainer Docker-API shim) with AIDC_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.

High-security mode (--isolate-vm)

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.env

Resource 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.

When high-security mode might make sense

  • 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.

When high-security mode is almost certainly overkill

  • 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=1 and ensure firecracker is installed — but expect rough edges.

Notes

  • Generated files are added to .git/info/exclude when 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 update pulls the latest aidc (ff-only) and re-runs the installer; aidc upgrade then 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 doctor diagnoses 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 like AIDC_AUTO_SYNC_SESSIONS, AIDC_ENABLE_EGRESS_FIREWALL, and AIDC_ISOLATE_VM.
  • Container egress is open by default; set AIDC_ENABLE_EGRESS_FIREWALL=1 in .ai-container/project.env for a default-deny allowlist. See docs/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 persist AIDC_ENABLE_CLIPBOARD=1 in .ai-container/project.env. See docs/clipboard-bridge.md.
  • Per-project VM isolation (--isolate-vm) is off by default due to resource cost. Opt in per (re)create with aidc up --isolate-vm / aidc rebuild --isolate-vm, or persist AIDC_ISOLATE_VM=1 in .ai-container/project.env. See Isolation modes above.
  • Generated Claude alias wrappers are aidc-managed and live in ~/.local/bin by default.

🤖 AI-Assisted Development

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.

License

GPL-3.0-only. See LICENSE.

About

A.I. Dev Containers

Resources

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages