Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
This file provides guidance to codex, Claude Code when working with code in this repository.

## Deva Architecture: Container-Based Agent Sandboxing

**CRITICAL DESIGN PATTERN**: Deva purposely runs ALL agents inside Docker containers. The container IS the sandbox.

- Each agent (claude, codex, gemini) runs in isolated container environment
- Agent internal sandboxes/permission systems are DISABLED:
- claude: `--dangerously-skip-permissions`
- gemini: `--yolo` flag
- codex: equivalent unrestricted mode
- Container provides security boundary instead of agent-level prompts
- Result: No interactive permission prompts while maintaining isolation

**Why**: Avoids permission fatigue in trusted workspaces while keeping agents containerized for safety.

## We're following Issue-Based Development (IBD) workflow
1. Before running any Git/GitHub CLI `Bash` command (`git commit`, `gh issue create`, `gh pr create`, etc.), open the corresponding file in @workflows to review required steps.
2. Always apply the exact templates or conventions from the following files:
Expand Down Expand Up @@ -121,6 +135,50 @@ Model aliases are automatically converted to appropriate formats (API model name
- Requires explicit confirmation (`yes`) to proceed
- Protects users from accidentally giving Claude access to all personal files

**Docker Socket Warning** (SECURITY-SENSITIVE):
By default, `/var/run/docker.sock` is auto-mounted if present. This grants full Docker API access to the container - effectively equivalent to root on the host. The "container as sandbox" model is weakened when Docker socket is mounted.

Implications:
- Agent can start/stop any container on host
- Agent can mount any host path into new containers
- Agent can escape to host via privileged container creation

Mitigations:
- Use `--no-docker` flag to disable auto-mount
- Set `DEVA_NO_DOCKER=1` environment variable
- Only mount when Docker-in-Docker workflows are required

## Bridges (privileged)

Deva's container IS the sandbox. Bridges punch controlled holes back to the host for specific integrations. Each bridge has TWO components: host-side and container-side.

| Bridge | Host Command | Container Command | Risk |
|--------|--------------|-------------------|------|
| Docker | (auto-mount `/var/run/docker.sock`) | `docker ...` | Root-equivalent on host |
| tmux | `deva-bridge-tmux-host` | `deva-bridge-tmux` | Host command execution |

**tmux bridge**: Connect container tmux client to host tmux server.
- Problem: Unix socket mount fails across macOS<->Linux kernel boundary
- Solution: socat TCP proxy (host) + socat Unix socket (container)
- Security: Container gains full tmux control (send-keys, run-shell, scrollback)

Usage:
```bash
# Host (macOS)
./scripts/deva-bridge-tmux-host

# Container
deva-bridge-tmux
tmux -S /tmp/host-tmux.sock list-sessions
```

Environment variables:
- `DEVA_BRIDGE_BIND`: host bind address (default: 127.0.0.1)
- `DEVA_BRIDGE_PORT`: TCP port (default: 41555)
- `DEVA_BRIDGE_HOST`: container's host address (default: host.docker.internal)
- `DEVA_BRIDGE_SOCKET`: host tmux socket path (default: auto-detected)
- `DEVA_BRIDGE_SOCK`: container local socket (default: /tmp/host-tmux.sock)

## Docker Architecture Details

### Volume Mounting Strategy
Expand Down
11 changes: 11 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Deva Architecture: Container-Based Agent Sandboxing

**CRITICAL DESIGN PATTERN**: Deva purposely runs ALL agents inside Docker containers. The container IS the sandbox.

- Each agent (claude, codex, gemini) runs in isolated container environment
- Agent internal sandboxes/permission systems are DISABLED (e.g., claude --dangerously-skip-permissions, GEMINI_SANDBOX=false)
- Container provides security boundary instead of agent-level prompts
- Result: No interactive permission prompts while maintaining isolation

**Why**: Avoids permission fatigue in trusted workspaces while keeping agents containerized for safety.

## We're following Issue-Based Development (IBD) workflow
1. Before running any Git/GitHub CLI `Bash` command (`git commit`, `gh issue create`, `gh pr create`, etc.), open the corresponding file in @workflows to review required steps.
2. Always apply the exact templates or conventions from the following files:
Expand Down
18 changes: 18 additions & 0 deletions DEV-LOGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@
- Reference issue numbers in the format `#<issue-number>` for easy linking.


# [2026-01-07] Dev Log: Fix version-upgrade build resilience
- Why: `make versions-up` exited 56 during GitHub API changelog fetch - GitHub API 403 rate limit (60/hour) from unauthenticated curl
- What:
- Changed `fetch_github_releases()` and `fetch_recent_github_releases()` in `scripts/release-utils.sh` from `curl` to `gh api` for authenticated requests
- All changelog fetch functions now fail gracefully with `{ echo "(fetch failed)"; return 0; }` instead of `|| return` (was causing `set -e` script abort)
- Added fallback in `load_versions()` - network fetch failure uses current image version instead of empty string
- Added pre-build version check in `scripts/version-upgrade.sh` - warns about missing versions but proceeds with build
- Result: Build script resilient to transient network failures and GitHub rate limits. Changelog display is best-effort, won't block builds.

**Files changed**:
- `scripts/release-utils.sh` (lines 175, 221, 452, 480)
- `scripts/version-upgrade.sh` (lines 82-95)

# [2025-11-27] Dev Log: Docker-in-Docker auto-mount support
- Why: Common dev workflow need - testing containers, building images, CI/CD simulation inside deva environments
- What: Auto-mount Docker socket (`/var/run/docker.sock`) by default with graceful detection, opt-out via `--no-docker` flag or `DEVA_NO_DOCKER=1`, quick permission fix (chmod 666) for deva user access
- Result: DinD works out-of-box on Linux/macOS/WSL2, no manual socket mounting needed, aligns with YOLO philosophy (make it work, container is the boundary)

# [2025-10-26] Dev Log: Custom credential files via --auth-with
- Why: Users have multiple credential files, needed direct path support beyond predefined auth methods
- What: `--auth-with /path/to/creds.json` now works, auto-backup existing credentials, workspace session tracking in `~/.config/deva/sessions/*.json`
Expand Down
119 changes: 79 additions & 40 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
openssh-client rsync \
shellcheck bat fd-find silversearcher-ag \
vim \
procps psmisc zsh
procps psmisc zsh socat \
libevent-dev libncurses-dev bison

RUN git lfs install --system

Expand All @@ -48,23 +49,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
RUN curl -fsSL https://bun.sh/install | bash && \
ln -s /root/.bun/bin/bun /usr/local/bin/bun

# Install Copilot API branch with GPT-5 Codex responses support (caozhiyuan fork)
# feature/responses-api adds: GPT-5-Codex support, model reasoning, token caching, enhanced streaming
ARG COPILOT_API_REPO=https://github.com/caozhiyuan/copilot-api.git
ARG COPILOT_API_BRANCH=feature/responses-api
ARG COPILOT_API_COMMIT=83cdfde17d7d3be36bd2493cc7592ff13be4928d

RUN --mount=type=cache,target=/root/.npm,sharing=locked \
npm install -g npm@latest pnpm && \
git clone --branch "${COPILOT_API_BRANCH}" "${COPILOT_API_REPO}" /tmp/copilot-api && \
cd /tmp/copilot-api && \
git checkout "${COPILOT_API_COMMIT}" && \
git log --oneline -5 && \
bun install --frozen-lockfile && bun run build && \
cd /tmp && npm install -g --ignore-scripts /tmp/copilot-api && \
rm -rf /tmp/copilot-api && \
npm cache clean --force

# Install stable runtimes BEFORE volatile packages to maximize cache reuse
RUN curl -LsSf https://astral.sh/uv/install.sh | sh

# Pre-install Python 3.14t (free-threaded) for uv
Expand All @@ -77,6 +62,26 @@ RUN --mount=type=cache,target=/tmp/go-cache,sharing=locked \
wget -q https://go.dev/dl/go1.22.0.linux-${GO_ARCH}.tar.gz && \
tar -C /usr/local -xzf go1.22.0.linux-${GO_ARCH}.tar.gz

# Install Copilot API (ericc-ch fork with latest features)
# Placed at end of runtimes stage to avoid invalidating cache for stable runtimes
ARG COPILOT_API_REPO=https://github.com/ericc-ch/copilot-api.git
ARG COPILOT_API_BRANCH=master
ARG COPILOT_API_COMMIT=master
ARG COPILOT_API_VERSION

LABEL org.opencontainers.image.copilot_api_version=${COPILOT_API_VERSION}

RUN --mount=type=cache,target=/root/.npm,sharing=locked \
npm install -g npm@latest pnpm && \
git clone --branch "${COPILOT_API_BRANCH}" "${COPILOT_API_REPO}" /tmp/copilot-api && \
cd /tmp/copilot-api && \
git checkout "${COPILOT_API_COMMIT}" && \
git log --oneline -5 && \
bun install --frozen-lockfile && bun run build && \
cd /tmp && npm install -g --ignore-scripts /tmp/copilot-api && \
rm -rf /tmp/copilot-api && \
npm cache clean --force

FROM runtimes AS cloud-tools

RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
Expand Down Expand Up @@ -119,6 +124,24 @@ RUN --mount=type=cache,target=/tmp/delta-cache,sharing=locked \
mv delta-0.18.2-${DELTA_ARCH}-unknown-linux-gnu/delta /usr/local/bin/ && \
rm -rf delta-0.18.2-${DELTA_ARCH}-unknown-linux-gnu*

# Install tmux from source (protocol version must match host for socket bridge)
# Same major.minor usually works; exact match is pragmatic, not required.
ARG TMUX_VERSION=3.6a
ARG TMUX_SHA256=b6d8d9c76585db8ef5fa00d4931902fa4b8cbe8166f528f44fc403961a3f3759
RUN --mount=type=cache,target=/tmp/tmux-cache,sharing=locked \
set -eu && \
cd /tmp/tmux-cache && \
TARBALL="tmux-${TMUX_VERSION}.tar.gz" && \
wget -q "https://github.com/tmux/tmux/releases/download/${TMUX_VERSION}/${TARBALL}" && \
echo "${TMUX_SHA256} ${TARBALL}" | sha256sum -c - && \
tar -xzf "${TARBALL}" && \
cd "tmux-${TMUX_VERSION}" && \
./configure --prefix=/usr/local && \
make -j"$(nproc)" && \
make install && \
rm -rf /tmp/tmux-cache/tmux-* && \
hash -r && \
tmux -V

ENV NPM_CONFIG_FETCH_RETRIES=5 \
NPM_CONFIG_FETCH_RETRY_FACTOR=2 \
Expand Down Expand Up @@ -146,31 +169,12 @@ RUN mkdir -p "$DEVA_HOME/.npm-global" && \

# Set npm configuration for deva user and install CLI tooling
USER $DEVA_USER
ARG CLAUDE_CODE_VERSION
ARG CODEX_VERSION

# Record key tool versions as labels for quick inspection
LABEL org.opencontainers.image.claude_code_version=${CLAUDE_CODE_VERSION}
LABEL org.opencontainers.image.codex_version=${CODEX_VERSION}

# Speed up npm installs and avoid noisy audits/funds prompts
ENV NPM_CONFIG_AUDIT=false \
NPM_CONFIG_FUND=false

# Use BuildKit cache for npm to speed up repeated builds
RUN --mount=type=cache,target=/home/deva/.npm,uid=${DEVA_UID},gid=${DEVA_GID},sharing=locked \
npm config set prefix "$DEVA_HOME/.npm-global" && \
npm install -g --no-audit --no-fund \
@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} \
@mariozechner/claude-trace \
@openai/codex@${CODEX_VERSION} && \
npm cache clean --force && \
npm list -g --depth=0 @anthropic-ai/claude-code @openai/codex || true

# Install Go tools for Atlassian integration (Confluence/Jira/Bitbucket)
RUN go install github.com/lroolle/atlas-cli/cmd/atl@5f6a20c4d164bf6fe6f5c60f9ac12dfccf210758 && \
sudo mv $HOME/go/bin/atl /usr/local/bin/

# Install stable components BEFORE ARG declarations to maximize cache reuse
RUN git clone --depth=1 https://github.com/ohmyzsh/ohmyzsh "$DEVA_HOME/.oh-my-zsh" && \
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions "$DEVA_HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" && \
git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting.git "$DEVA_HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting"
Expand All @@ -186,12 +190,47 @@ RUN echo 'export ZSH="$HOME/.oh-my-zsh"' > "$DEVA_HOME/.zshrc" && \
RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \
$DEVA_HOME/.local/bin/uv python install 3.14t

# Declare ARGs immediately before usage to minimize cache invalidation
ARG CLAUDE_CODE_VERSION
ARG CODEX_VERSION
ARG GEMINI_CLI_VERSION=latest

# Record key tool versions as labels for quick inspection
LABEL org.opencontainers.image.claude_code_version=${CLAUDE_CODE_VERSION}
LABEL org.opencontainers.image.codex_version=${CODEX_VERSION}
LABEL org.opencontainers.image.gemini_cli_version=${GEMINI_CLI_VERSION}

# Install CLI tools via npm
RUN --mount=type=cache,target=/home/deva/.npm,uid=${DEVA_UID},gid=${DEVA_GID},sharing=locked \
npm config set prefix "$DEVA_HOME/.npm-global" && \
npm install -g --no-audit --no-fund \
@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} \
@mariozechner/claude-trace \
@openai/codex@${CODEX_VERSION} \
@google/gemini-cli@${GEMINI_CLI_VERSION} && \
npm cache clean --force && \
npm list -g --depth=0 @anthropic-ai/claude-code @openai/codex @google/gemini-cli || true

# Volatile packages: Install at the end to avoid cascading rebuilds
ARG ATLAS_CLI_VERSION=main

LABEL org.opencontainers.image.atlas_cli_version=${ATLAS_CLI_VERSION}

# Install atlas-cli binary + skill via upstream install.sh
# - Uses prebuilt release tarball (faster than go install)
# - Falls back to go install if no prebuilt for platform
# - Installs skill with proper structure (SKILL.md + references/)
RUN curl -fsSL "https://raw.githubusercontent.com/lroolle/atlas-cli/${ATLAS_CLI_VERSION}/install.sh" \
| bash -s -- --skill-dir $DEVA_HOME/.skills

USER root

COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
COPY scripts/deva-bridge-tmux /usr/local/bin/deva-bridge-tmux

RUN chmod +x /usr/local/bin/docker-entrypoint.sh && \
chmod -R +x /usr/local/bin/scripts || true
RUN chmod 755 /usr/local/bin/docker-entrypoint.sh && \
chmod 755 /usr/local/bin/deva-bridge-tmux && \
chmod -R 755 /usr/local/bin/scripts || true

WORKDIR /root

Expand Down
7 changes: 6 additions & 1 deletion Dockerfile.rust
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
libpng-dev libjpeg-dev \
libudev-dev \
libproc2-dev \
libzmq3-dev libzmq5 libczmq-dev
libzmq3-dev libzmq5 libczmq-dev && \
curl -fsSL 'https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key' | gpg --dearmor -o /usr/share/keyrings/clickhouse-keyring.gpg && \
ARCH=$(dpkg --print-architecture) && \
echo "deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg arch=${ARCH}] https://packages.clickhouse.com/deb stable main" > /etc/apt/sources.list.d/clickhouse.list && \
apt-get update && \
apt-get install -y --no-install-recommends clickhouse-client

RUN --mount=type=cache,target=/tmp/rust-cache,sharing=locked \
set -euxo pipefail && \
Expand Down
Loading