# syntax=docker/dockerfile:1.4 # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # Supervisor container for OpenShell # Multi-stage build with cross-compilation support for multi-arch # Stage 1: Rust builder (runs on build platform, cross-compiles for target) FROM --platform=$BUILDPLATFORM rust:1.88-slim AS rust-builder ARG RUST_BUILD_PROFILE=debug ARG TARGETARCH ARG BUILDARCH ARG CARGO_TARGET_CACHE_SCOPE=default # Install build dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ cmake g++ make protobuf-compiler curl && rm -rf /var/lib/apt/lists/* # Install cross-compilation toolchain, sccache, + Rust target (no-ops for native builds) COPY deploy/docker/cross-build.sh /usr/local/bin/ RUN . cross-build.sh && install_cross_toolchain && install_sccache && add_rust_target ARG SCCACHE_MEMCACHED_ENDPOINT WORKDIR /build # Copy dependency manifests first for better caching COPY Cargo.toml Cargo.lock ./ COPY crates/navigator-cli/Cargo.toml crates/navigator-cli/Cargo.toml COPY crates/navigator-core/Cargo.toml crates/navigator-core/Cargo.toml COPY crates/navigator-policy/Cargo.toml crates/navigator-policy/Cargo.toml COPY crates/navigator-providers/Cargo.toml crates/navigator-providers/Cargo.toml COPY crates/navigator-router/Cargo.toml crates/navigator-router/Cargo.toml COPY crates/navigator-sandbox/Cargo.toml crates/navigator-sandbox/Cargo.toml COPY crates/navigator-server/Cargo.toml crates/navigator-server/Cargo.toml COPY crates/navigator-bootstrap/Cargo.toml crates/navigator-bootstrap/Cargo.toml # Create dummy source files to build dependencies RUN mkdir -p crates/navigator-cli/src crates/navigator-core/src crates/navigator-policy/src crates/navigator-providers/src crates/navigator-router/src crates/navigator-sandbox/src crates/navigator-server/src crates/navigator-bootstrap/src && \ echo "fn main() {}" > crates/navigator-cli/src/main.rs && \ echo "fn main() {}" > crates/navigator-sandbox/src/main.rs && \ echo "fn main() {}" > crates/navigator-server/src/main.rs && \ touch crates/navigator-core/src/lib.rs && \ touch crates/navigator-policy/src/lib.rs && \ touch crates/navigator-providers/src/lib.rs && \ touch crates/navigator-router/src/lib.rs && \ touch crates/navigator-bootstrap/src/lib.rs # Copy proto files needed for build COPY proto/ proto/ COPY crates/navigator-sandbox/data/sandbox-policy.rego ./crates/navigator-sandbox/data/ COPY deploy/docker/sandbox/dev-sandbox-policy.yaml ./deploy/docker/sandbox/ # Build dependencies only (cached unless Cargo.toml/lock changes). # sccache uses memcached in CI or the local disk cache mount for local dev. # The cargo-target mount gives cargo a persistent target/ dir for incremental rebuilds. RUN --mount=type=cache,id=cargo-registry-sandbox-${TARGETARCH},sharing=locked,target=/usr/local/cargo/registry \ --mount=type=cache,id=cargo-target-sandbox-${TARGETARCH}-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ --mount=type=cache,id=sccache-sandbox-${TARGETARCH},sharing=locked,target=/tmp/sccache \ . cross-build.sh && cargo_cross_build -p navigator-sandbox 2>/dev/null || true # Copy actual source code COPY crates/ crates/ # Touch source files to ensure they're rebuilt (not the cached dummy). # Touch build.rs and proto files to force proto code regeneration when the # cargo target cache mount retains stale OUT_DIR artifacts from prior builds. RUN touch crates/navigator-sandbox/src/main.rs \ crates/navigator-core/build.rs \ proto/*.proto # Build with cross-compilation support. # Cache mounts are keyed by TARGETARCH so parallel multi-platform builds # don't corrupt each other's cargo registry or build artifacts. RUN --mount=type=cache,id=cargo-registry-sandbox-${TARGETARCH},sharing=locked,target=/usr/local/cargo/registry \ --mount=type=cache,id=cargo-target-sandbox-${TARGETARCH}-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ --mount=type=cache,id=sccache-sandbox-${TARGETARCH},sharing=locked,target=/tmp/sccache \ . cross-build.sh && \ if [ "$RUST_BUILD_PROFILE" = "release" ]; then \ cargo_cross_build --release -p navigator-sandbox; \ else \ cargo_cross_build -p navigator-sandbox; \ fi && \ mkdir -p /build/out && \ cp "$(cross_output_dir "$RUST_BUILD_PROFILE")/navigator-sandbox" /build/out/ # Stage 2: Python base (uses target platform) # Pin to specific Python patch + Debian release for reproducible builds. # CVE-2025-13836 (stdlib urllib Content-Length OOM) affects Python <= 3.12.13; # update to python:3.12.14-slim-bookworm (or later) when released. FROM python:3.12.13-slim-bookworm AS base ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 WORKDIR /sandbox # Install system dependencies # iproute2 is needed for network namespace management (ip netns, veth pairs) RUN apt-get update && apt-get install -y --no-install-recommends \ curl dnsutils iproute2 iputils-ping \ net-tools netcat-openbsd traceroute \ && rm -rf /var/lib/apt/lists/* # Create supervisor and sandbox users/groups RUN groupadd -r supervisor && useradd -r -g supervisor -s /usr/sbin/nologin supervisor && \ groupadd -r sandbox && useradd -r -g sandbox -d /sandbox -s /bin/bash sandbox # Stage 3: Python dependencies builder FROM base AS builder COPY --from=ghcr.io/astral-sh/uv:0.10.8 /uv /usr/local/bin/uv # Copy project files for dependency resolution COPY pyproject.toml uv.lock ./ # Install dependencies into a virtual environment RUN uv venv /app/.venv && \ uv sync --frozen --no-dev --no-install-project 2>/dev/null || \ uv sync --no-dev --no-install-project 2>/dev/null || true # Install navigator SDK dependencies and pip (uv venvs don't include pip by # default, and sandbox users need it for `pip install` inside the sandbox). RUN uv pip install --python /app/.venv/bin/python --no-cache-dir cloudpickle grpcio protobuf openai pip # Stage 4: Coding agents layer FROM base AS coding-agents # Install Node.js 22 via NodeSource (required by coding agents). # build-essential & python3 are needed for npm packages with native addons (node-gyp). # Include a minimal native toolchain so npm can compile optional native # dependencies on platforms where prebuilt artifacts are unavailable. # Pin to a specific patch version for reproducible builds. # CVE-2026-21637, CVE-2025-59466, CVE-2025-59465, CVE-2025-55131 affect # Node.js <= 22.22.1. Update to 22.23.0+ when a patched release ships. RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \ apt-get install -y --no-install-recommends build-essential git nodejs=22.22.1-1nodesource1 python3 nano && \ rm -rf /var/lib/apt/lists/* # Install GitHub CLI (gh) from the official apt repository RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \ -o /usr/share/keyrings/githubcli-archive-keyring.gpg && \ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \ > /etc/apt/sources.list.d/github-cli.list && \ apt-get update && apt-get install -y --no-install-recommends gh && \ rm -rf /var/lib/apt/lists/* # Install Claude CLI via native installer (npm method is deprecated) RUN curl -fsSL https://claude.ai/install.sh | bash \ && cp /root/.local/bin/claude /usr/local/bin/claude \ && chmod 755 /usr/local/bin/claude # Upgrade npm to v11 which ships tar@^7.5.9 (fixes tar@6.2.1 vulns bundled in # npm 10: GHSA-r6q2-hw4h-h46w, GHSA-8qq5-rm4j-mr97, GHSA-83g3-92jg-28cx, # GHSA-34x7-hfp2-rc4v, GHSA-qffp-2rhf-9h96). RUN npm install -g npm@11.11.0 # Install OpenCode CLI and Codex CLI (OpenAI) with pinned versions for # reproducible builds. Force-upgrade tar and @hono/node-server afterward to # resolve transitive dependency vulnerabilities (GHSA-wc8c-qw6v-h7f6, # GHSA-r6q2-hw4h-h46w, GHSA-qffp-2rhf-9h96, GHSA-8qq5-rm4j-mr97, # GHSA-83g3-92jg-28cx, GHSA-34x7-hfp2-rc4v). RUN npm install -g opencode-ai@1.2.18 @openai/codex@0.111.0 && \ npm install -g tar@7.5.11 @hono/node-server@1.19.11 # Install ai-pim-utils (NVIDIA PIM CLI tools: outlook, calendar, transcripts, etc.) # The install script auto-detects Debian and installs via .deb package to /usr/bin. # Skills are installed to /usr/share/ai-pim-utils/skills and symlinked to ~/.claude/skills. # RUN curl -fsSL https://outlook-cli-80d21a.gitlab-master-pages.nvidia.com/install.sh | bash # Stage 5: Final image FROM coding-agents AS final ARG RUST_BUILD_PROFILE=debug COPY --from=builder /app/.venv /app/.venv COPY --from=builder /usr/local/bin/uv /usr/local/bin/uv COPY --from=rust-builder /build/out/navigator-sandbox /usr/local/bin/ # Copy navigator Python SDK into the virtual environment COPY python/navigator/ /app/.venv/lib/python3.12/site-packages/navigator/ # Add venvs to PATH -- /sandbox/.venv (writable, user-installed packages) # takes priority over /app/.venv (read-only, build-time packages). # ssh.rs inherits PATH at runtime so it stays in sync with this layout. # VIRTUAL_ENV and UV_PYTHON_INSTALL_DIR are also exported in .bashrc # so that login shell sessions (interactive and exec) see them. ENV PATH="/sandbox/.venv/bin:/app/.venv/bin:/usr/local/bin:/usr/bin:/bin" \ VIRTUAL_ENV="/sandbox/.venv" \ UV_PYTHON_INSTALL_DIR="/sandbox/.uv/python" # Copy custom navigator skills into the image # To add a skill, create a subdirectory under deploy/docker/sandbox/skills/ # with a SKILL.md file (and any supporting files). It will be installed into # both ~/.claude/skills/ and ~/.agents/skills/ for agent discovery. COPY deploy/docker/sandbox/skills/ /sandbox/.agents/skills/ # Bake the default sandbox policy into the image so that sandboxes without an # explicit policy from the gateway can discover it on disk. COPY deploy/docker/sandbox/dev-sandbox-policy.yaml /etc/navigator/policy.yaml RUN mkdir -p /var/navigator /sandbox /var/log && \ touch /var/log/navigator.log && \ chown supervisor:supervisor /var/log/navigator.log && \ chmod 0664 /var/log/navigator.log && \ chown sandbox:sandbox /sandbox && \ # Create a writable venv that inherits all packages from the read-only # /app/.venv. Sandbox users can `pip install` or `uv pip install` into # this venv without touching the base image layer. uv venv --python /app/.venv/bin/python --seed --system-site-packages /sandbox/.venv && \ chown -R sandbox:sandbox /sandbox/.venv && \ # Minimal shell init files so interactive and non-interactive shells # get a sane PATH and prompt. Without these, bash sources nothing # under /sandbox and tools like VS Code Remote-SSH may mis-detect # the platform. printf 'export PATH="/sandbox/.venv/bin:/app/.venv/bin:/usr/local/bin:/usr/bin:/bin"\nexport VIRTUAL_ENV="/sandbox/.venv"\nexport UV_PYTHON_INSTALL_DIR="/sandbox/.uv/python"\nexport PS1="\\u@\\h:\\w\\$ "\n' \ > /sandbox/.bashrc && \ printf '[ -f ~/.bashrc ] && . ~/.bashrc\n' > /sandbox/.profile && \ chown sandbox:sandbox /sandbox/.bashrc /sandbox/.profile && \ # Install agent skills into the sandbox user's home directory # so Claude and other agents can discover them at runtime. # Sources: ai-pim-utils (.deb) and custom navigator skills. mkdir -p /sandbox/.claude/skills /sandbox/.agents/skills && \ for src in /usr/share/ai-pim-utils/skills /usr/share/navigator/skills; do \ [ -d "$src" ] || continue; \ find "$src" -mindepth 1 -maxdepth 1 -type d | while read -r skill; do \ ln -sf "$skill" "/sandbox/.claude/skills/$(basename "$skill")"; \ ln -sf "$skill" "/sandbox/.agents/skills/$(basename "$skill")"; \ done; \ done && \ chown -R sandbox:sandbox /sandbox/.claude /sandbox/.agents ENTRYPOINT ["navigator-sandbox"]