Skip to content

Commands

Global flags

These flags apply to all md commands:

FlagDescription
--runtime <runtime>Select container runtime: docker or podman (default: auto-detect)
--control-masterEnable SSH ControlMaster connection multiplexing (disabled on Windows)
-v, --verboseVerbose output

Container lifecycle

CommandDescription
md startCreate and start a container for the current branch; SSH in automatically
md start -displayStart with X11/VNC desktop environment
md start -tailscaleStart with Tailscale networking
md start -usbStart with USB device passthrough (for ADB, Linux only)
md start -no-sshStart container without opening an SSH session
md run <cmd>Start a temporary container, run a command, then clean up
md forkSnapshot a running container and create a new one on fresh branches
md listList all md containers
md list --jsonList containers as JSON
md list --statsInclude resource usage stats (CPU, memory, disk, network)
md sudo-passwordPrint the container's random sudo password
md stopStop the container (preserves filesystem for later revival)
md start (on stopped container)Revive a stopped container (re-queries SSH port, restores SSH config)
md purgeStop and permanently remove the container

Re-running md start from a repo whose container is stopped or exited is the supported way to revive it. md prints Reviving stopped container and reuses the existing container with a fresh SSH port; it does not create a duplicate. If the container is already running, md start returns an error and tells you to ssh in instead.

List output

md list --json emits one object per container. The name, state, uptime, and ssh_port fields are always present. The remaining fields appear only when they apply:

FieldWhen present
nameAlways
stateAlways
uptimeAlways
ssh_portAlways
vncPortContainer started with --display
displayContainer started with --display
tailscaleContainer started with --tailscale
fqdnTailscale is enabled (the container's tailnet name)
sudoContainer started with --sudo
usbContainer started with --usb
reposContainer has one or more mapped repos
stats--stats was passed (CPU, memory, network, disk)

Start flags

FlagDescription
-b, -branch <name>Branch to use (default: current branch)
-r, -repo <path>Path to git repository (default: current directory)
--image <ref>Full base Docker image (e.g. ghcr.io/caic-xyz/md-user:2026-05-09)
--tag <tag>Tag for the default base image (ghcr.io/caic-xyz/md-user:<tag>)
--platform <platform>Build and run for linux/amd64 or linux/arm64 (default: host architecture)
--display, -dEnable X11/VNC virtual desktop (port 5901)
--tailscaleEnable Tailscale networking
--usbPass through USB devices (/dev/bus/usb, Linux only)
--githubInject GITHUB_TOKEN into the container
--no-sshDon't SSH into the container after starting
--cpus <n>Max CPU cores (default: NumCPU - 2, minimum 2; 0 = no limit)
--label key=value, -lAdd Docker container label (repeatable)
--extra-repo path[:branch], -eMap an additional git repository (repeatable)
--docker-flag "arg1 arg2"Extra flags passed verbatim to docker run (shell-quoted, repeatable)
--cache <name>Add a well-known cache or a custom host:container[:ro] directory (repeatable)
--mount host:container[:ro]Bind-mount a host directory into the running container (repeatable)
--sudoEnable root access with a random password; grants SYS_ADMIN and /dev/fuse
-q, --quietSuppress informational output
--control-masterEnable SSH ControlMaster connection multiplexing

Access

CommandDescription
ssh md-<repo>-<branch>SSH into the container directly
md vncOpen VNC connection to the container desktop

md vnc auto-detects the container from your current repository and branch, the same way md stop and md purge do. Run it from inside the repo directory with no arguments. You can also pass a container name as a positional argument, or target a specific container with -b/--branch and -r/--repo.

Syncing

CommandDescription
md diffShow changes vs. base branch (git diff base). Extra args forwarded to git, e.g. md diff --stat
md diff --allShow diffs for all repos in the container

md diff is non-mutating. It leaves the container's git index untouched and runs with git locks disabled, so it is safe to run while an agent is working in the container. Untracked files still appear in the diff. | md pull | Pull changes from container and integrate into local branch | | md pull --all | Pull changes for all repos | | md fetch | Fetch changes from container without integrating (updates remote-tracking ref) | | md push | Force-push local state into the container; creates a backup branch inside |

Fork

md fork snapshots a running container's entire filesystem (installed packages, build artifacts, agent state) and creates a new container where each repo is checked out on a fresh branch.

bash
# Fork the current container
md fork

# Fork a specific container by name
md fork -source md-myrepo-main

# Fork with extra repos and VNC
md fork -e ../other-project -display

Fork flags mirror md start plus:

FlagDescription
-s, --source <name>Source container name (default: auto-detect from repo)
--sudoEnable root access with a random password; grants SYS_ADMIN and /dev/fuse
-q, --quietSuppress informational output

Each repo (source and extra) gets a unique destination branch derived from its source branch (e.g. mainmain-0, then main-1, etc.).

Run

md run <cmd> starts a temporary container, runs a command, and cleans up everything:

bash
md run -- npm test
md run -- cargo check
md run --image ghcr.io/caic-xyz/md-user:2025-05-09 -- npm ci

Supports: --image, --tag, --platform, --repo, --branch, --github, --cpus, --docker-flag, --mount, and all cache flags.

Workflows

Multi-repo containers

Map multiple repositories into a single container with --extra-repo:

bash
# Start with main project + dependency
md start -r ~/src/backend -e ~/src/shared-lib

# Specify branches explicitly
md start -b feature-x -e ~/src/shared-lib:feature-x

All repos are cloned into ~/src/<name> inside the container. The first repo is primary; extras are pushed alongside it. md diff, md pull, and md push operate on the repo matching your current directory. Use --all to operate on all repos:

bash
md diff --all      # show diffs for every repo
md pull --all      # pull changes from every repo

Fork workflow

Fork a running agent's container to try a different approach without losing progress:

bash
# Agent is working in md-myrepo-feature-x
# You want to try a different approach:
md fork              # creates md-myrepo-feature-x-0
ssh md-myrepo-feature-x-0
# Work on the fork. If it works out:
md pull              # pull changes back from the fork
md purge             # clean up the fork
# If the original approach was better:
md purge md-myrepo-feature-x-0  # discard the fork

The fork preserves the entire filesystem (installed packages, build artifacts, agent history) so you don't lose context. Each repo gets a unique divergent branch.

CI / one-shot runs

md run starts a container, runs a command, and cleans up. Ideal for CI:

bash
# Run tests in a clean environment
md run -- npm test
md run -- cargo test
md run -- go test ./...

# Run a linter on a specific branch without checking out
md run -b feature-x -- golangci-lint run ./...

# Custom image + specific version
md run --image ghcr.io/caic-xyz/md-user:2026-05-09 -- npm ci && npm test

# Pass through GitHub token for private repos
md run --github -- npm install

AI commit messages

md pull can generate commit messages using an LLM. It analyzes the diff and recent commit history to produce a conventional commit message.

Setup

Set the provider and model via environment variables; the full list of acceptable ASK_PROVIDER values is from genai:

bash
export ASK_PROVIDER=deepseek
export ASK_MODEL=deepseek-v4-flsah

If not set, md auto-discovers available providers on your system. It prefers CLI-based providers in this order: pi, codex, opencode, claudecode, then any others alphabetically. The model defaults to a cheap/fast option.

For large diffs, md uses a progressive reduction pipeline to fit within the LLM's context window. See Design for details.

Build

md build-image

Builds the md-root-local and md-user-local Docker images from the embedded source. Use this when:

  • You're contributing changes to the container Dockerfiles or setup scripts
  • You want to test a change to the container's preinstalled tools
  • You want fully local images without pulling from ghcr.io
bash
md build-image

The first build takes several minutes (two full Docker builds: root image, then user image on top). Subsequent builds are faster thanks to layer caching.

A GITHUB_TOKEN is recommended to avoid rate limits when installing tools like Neovim and rust-analyzer:

bash
export GITHUB_TOKEN=ghp_...
md build-image

Once built, use the local images:

bash
md start -image md-user-local

By default md build-image targets your host architecture. Pass --platform linux/amd64 or --platform linux/arm64 to build for a different architecture. The platform is part of the specialized image hash, so cross-architecture builds and runs produce distinct images.

md prune

Removes unused md-specialized-* and md-fork-* images no longer referenced by any container. Also cleans the Docker build cache.

Cache flags

FlagDescription
--no-cache <name>Exclude a specific default cache (repeatable)
--no-cachesDisable all default caches
--cache <name>Add a well-known cache (use with --no-caches) or custom host:container[:ro]

Caches are baked into the image at build time. Only caches whose host directories exist are included; missing directories are silently skipped. The image rebuilds only when the active cache set changes.

For custom caches, append :ro to make the copied directory read-only inside the container. The container path accepts ~ or a leading ~/, which expand to /home/user. For a live bind-mount that reflects host changes at runtime, use --mount instead of --cache. See Configuration for the difference.

Well-known caches

Cache nameHost pathDescription
bun~/.bun/install/cacheBun package manager
cargo~/.cargo/registry, ~/.cargo/gitRust cargo registry and git checkouts
go-mod~/go/pkg/modGo module cache
gradle~/.gradle/caches, ~/.gradle/wrapper/distsGradle caches and wrapper
maven~/.m2/repositoryMaven repository
npm~/.npmnpm cache
pip~/.cache/pipPython pip cache
pnpm~/.local/share/pnpm/storepnpm store
uv~/.cache/uvUV Python package manager
android-keys~/.android (shallow)Android debug keystore and ADB keys

Remote GUI (VNC)

Enable at startup, then open:

bash
md start -display
md vnc

The DISPLAY environment variable is automatically set in SSH sessions, so X11 apps launched from SSH appear on the VNC desktop.

Recommended VNC clients:

  • Windows: RealVNC Viewer, TightVNC, UltraVNC
  • macOS: Built-in VNC or RealVNC Viewer
  • Linux: tigervnc-viewer, vinagre, or vncviewer