Skip to content

aroonavm/dcx

Repository files navigation

dcx

Dynamic workspace mounting wrapper for Colima devcontainers.

Why dcx?

Problem: Colima mounts are static (configured at startup). Running devcontainer up requires workspaces to already exist in the VM, but they're dynamic local paths. Mounting $HOME broadly exposes all projects to every container — a security risk, especially when running autonomous agents.

Solution: dcx dynamically mounts only the workspace you need, only while in use. It uses bindfs (a FUSE userspace tool) to project your workspace into a pre-mounted relay directory (~/.colima-mounts), then starts the devcontainer there.

Benefits:

  • Security: Only the active workspace is mounted; other projects remain private
  • Performance: No need to mount $HOME broadly; multiple isolated workspaces can coexist
  • Convenience: One command (dcx up) handles mount creation, devcontainer startup, and cleanup

Installation

curl -fsSL https://raw.githubusercontent.com/aroonavm/dcx/main/install.sh | sh

Installs to /usr/local/bin by default. Override with DCX_BIN_DIR=~/.local/bin.

Prerequisites: bindfs, devcontainer CLI, and Colima.

Quick Start

# Setup (one time)
sudo apt install bindfs        # Linux — see specs/guides/setup.md for macOS
# Edit ~/.config/colima/default/colima.yaml to mount ~/.colima-mounts
colima stop && colima start

# Usage
dcx up                                       # Start devcontainer with mount
dcx exec --workspace-folder . /bin/zsh       # Run command in container
dcx down                                     # Stop container and cleanup

Environment Variables

Set DCX_DEVCONTAINER_CONFIG_DIR_PATH to avoid passing --config-dir on every invocation:

export DCX_DEVCONTAINER_CONFIG_DIR_PATH=~/.dcx/
dcx up --workspace-folder ~/project-a       # Uses env var config directory
dcx up --workspace-folder ~/project-b       # Uses env var config directory
dcx up --workspace-folder ~/project-c --config-dir /other/config  # Flag overrides env var

The directory must contain a devcontainer.json file and optionally a dcx_config.yaml file.

Per-Project File Mounting

Mount specific host files into containers via a dcx_config.yaml file or the --file CLI flag.

Option 1: Per-project config file (dcx_config.yaml)

Create .devcontainer/dcx_config.yaml (or dcx_config.yaml in your project root):

up:
  network: minimal
  files:
    - path: ~/.gitconfig                    # Hardlinked: static config, writes propagate to host
    - path: ~/.claude.json                  # Live-synced: keeps file in sync via inotify/FSEvents
      sync: true

Then:

dcx up --workspace-folder .

File mounting strategies:

  • Standard files (sync: false or omitted): Hardlinked into the container (writes propagate back to host), or copied as readonly if on a different filesystem. Works well for config files that don't change frequently.

  • Live-synced files (sync: true): Content is copied to a staging directory and kept in sync bidirectionally via a background daemon. Perfect for authentication files like ~/.claude.json that may be updated atomically by the host application. The daemon uses inotify (Linux) / FSEvents (macOS) for immediate change detection, with SHA256-based debouncing to avoid spurious syncs.

When to use sync: true:

  • Authentication/credential files that apps write atomically (temp+rename)
  • Files that change while the container is running and the container needs the latest version
  • Example: ~/.claude.json (Claude Code writes atomically on auth changes)

When to use standard mounting:

  • Static config files (git, ssh config)
  • Files that rarely change after container start
  • Files that should be bidirectionally linked (hard/soft links)

For the full configuration reference including merge behavior and discovery rules, see Configuration Reference.

Option 2: CLI flag

Mount files ad-hoc with --file:

dcx up --workspace-folder . --file ~/.gitconfig --file ~/.ssh/id_ed25519

Git and Claude Config in Containers

Add these mounts to your Colima config file (~/.config/colima/default/colima.yaml on Linux, ~/.colima/default/colima.yaml on macOS):

mounts:
  - location: ~/.gitconfig
    writable: false
  - location: ~/.claude
    writable: true
  - location: ~/.colima-mounts
    writable: true

dcx reads this file at startup and automatically injects any listed mounts (except ~/.colima-mounts itself) into every container it starts. For ~/.gitconfig and ~/.claude, it also sets GIT_CONFIG_GLOBAL and CLAUDE_CONFIG_DIR so the tools find their config at the right path.

This means git identity and Claude login are available in every container without any per-project configuration.

Network Isolation

Control network access per container with --network:

# Default: minimal — dev tools only (GitHub, npm, Anthropic APIs, VSCode, Sentry)
dcx up --workspace-folder ~/my-project

# Restricted: no external network access
dcx up --workspace-folder ~/my-project --network restricted

# Host-only: connect to services on host machine (localhost:*)
dcx up --workspace-folder ~/my-project --network host

# Open: unrestricted access (use for local dev only)
dcx up --workspace-folder ~/my-project --network open

Each container gets its own network mode. Useful for:

  • Security: Autonomous agents with restricted network (can't exfiltrate data)
  • Sandboxing: Different projects with different trust levels
  • Testing: Host-only mode to test against local services
# Example: Shared dev environment with mixed trust levels
dcx up --config-dir ~/.dcx --workspace-folder ~/trusted-project --network minimal
dcx up --config-dir ~/.dcx --workspace-folder ~/untrusted-agent --network restricted
dcx up --config-dir ~/.dcx --workspace-folder ~/testing-project --network host

# The base image is shared: same devcontainer.json content → same image.
# Changing devcontainer.json (e.g. bumping a package version) triggers a new build.

Automatic Colima Startup

Configure Colima to start automatically on system boot using dcx autostart:

# Enable autostart (writes systemd service on Linux, launchd plist on macOS)
dcx autostart enable

# Check current status
dcx autostart status

# Disable autostart later
dcx autostart disable

The service file location depends on your platform:

  • Linux: ~/.config/systemd/user/colima.service (user-level systemd service)
  • macOS: ~/Library/LaunchAgents/io.colima.autostart.plist (launchd agent)

This eliminates the need to manually start Colima after system boot — it will start automatically and be ready for your first dcx up command.

Documentation

Development Containers

Three Dockerfiles are provided for different use cases:

File Base Purpose
.devcontainer/slim/Dockerfile rust:slim-bookworm Slim dev image (git + bindfs). Default for contributors.
.devcontainer/full/Dockerfile node:latest Full image (Claude Code, zsh, git-delta). For a more complete dev environment.
tests/e2e/Dockerfile.test rust:slim-bookworm Minimal test image (git + node + devcontainer CLI, no bindfs).
# Slim image (default) — start with:
dcx up --workspace-folder . --config-dir .devcontainer/slim

# Full image — for the complete dev environment:
dcx up --workspace-folder . --config-dir .devcontainer/full

The .devcontainer/full/ directory includes a dcx_config.yaml that stages:

  • ~/.gitconfig — hardlinked into staging (standard mount, static config). Also auto-injected by dcx via colima.yaml mounts.
  • ~/.claude.json — live-synced (sync: true, via inotify/FSEvents daemon)

This means Claude Code in the container will see authentication updates from the host in real-time as you log in/out, without needing to restart the container. Note: ~/.gitconfig appears in both the dcx_config.yaml and colima.yaml; dcx automatically deduplicates these at runtime. Git config remains hardlinked for efficiency since it rarely changes during container runtime.

Building

cargo build --release
cargo install --path .

This installs dcx to ~/.cargo/bin/. Ensure ~/.cargo/bin is in your PATH (add export PATH="$HOME/.cargo/bin:$PATH" to your shell rc file if needed).

Releasing

The release process builds binaries for all platforms (Linux x86_64/aarch64, macOS x86_64/aarch64) and publishes them as GitHub Release assets, along with a Debian package.

Release Checklist

  1. Update version in Cargo.toml:

    # Edit Cargo.toml, change version = "0.1.0" to version = "0.1.2"
  2. Update Cargo.lock by building:

    cargo build --release
  3. Commit both files:

    git add Cargo.toml Cargo.lock
    git commit -m "Bump version to 0.1.2"
  4. Create a git tag (tag must match version in Cargo.toml):

    git tag v0.1.2
  5. Push commits and tag (in two steps to avoid race conditions):

    git push origin main
    git push origin v0.1.2

The tag push triggers three workflows:

  • validate-release.yml — Checks that tag, Cargo.toml, and Cargo.lock all match
  • release.yml — Builds binaries for 4 platforms + Debian package + creates GitHub Release
  • If validation fails, release is skipped (CI shows error)

Installation Methods

Once the GitHub Release is published, users can install from:

# Install script (auto-detects platform, downloads binary)
curl -fsSL https://raw.githubusercontent.com/aroonavm/dcx/main/install.sh | sh

# Debian/Ubuntu
sudo dpkg -i dcx_<version>_amd64.deb

# Manual (download from releases, extract, move to PATH)
tar -xzf dcx-v<version>-<platform>.tar.gz
sudo mv dcx /usr/local/bin/

About

Rust CLI tool for managing dynamic devcontainer workspace mounts on Colima via bindfs

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Contributors

Languages