Files
OpenShell/deploy/docker/sandbox/Dockerfile.base
T
Drew Newberry 6dc97171ed feat(sandbox): add image build/push and fix cluster deploy (!34)
Closes #44

## Summary

- Add `nav sandbox image push --dockerfile <path>` command to build and push custom container images into the cluster
- Fix sandbox Docker build failure caused by OpenClaw installer TTY access
- Fix sandbox proxy crash when network namespace is unavailable (fall back to loopback)
- Add e2e test for the full custom image build and sandbox creation flow
- Rename and rewrite BYOC architecture doc to reflect current state

## Changes

### `navigator-bootstrap`
- New `build` module (`src/build.rs`) with `build_and_push_image()` public API
- Builds via bollard `Docker::build_image()`, streams output to caller
- `.dockerignore` support with glob matching, 8 unit tests

### `navigator-cli`
- Added `SandboxImageCommands::Push` subcommand with `--dockerfile`, `--tag`, `--context`, `--build-arg` flags
- Added `test:e2e:custom-image` mise task

### `navigator-sandbox`
- Proxy now falls back to `127.0.0.1:3128` when no network namespace or explicit bind address is available, instead of erroring out

### Docker / Infra
- Sandbox Dockerfile: replaced OpenClaw native installer (`curl | bash`) with `npm install -g openclaw` to avoid `/dev/tty` failures in Docker build

### Documentation
- Renamed `architecture/sandbox-byoc.md` to `sandbox-custom-containers.md`
- Rewrote doc to describe current behavior (CLI usage, push pipeline, supervisor behavior, design decisions) instead of listing implementation changes

### Tests
- Added `e2e/bash/test_sandbox_custom_image.sh` covering image push + sandbox create with custom image
2026-02-22 20:32:18 -08:00

121 lines
4.5 KiB
Docker

# syntax=docker/dockerfile:1.4
# Supervisor container for navigator
# 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
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
cmake g++ make protobuf-compiler && rm -rf /var/lib/apt/lists/*
# Install cross-compilation toolchain + Rust target (no-ops for native builds)
COPY deploy/docker/cross-build.sh /usr/local/bin/
RUN . cross-build.sh && install_cross_toolchain && add_rust_target
WORKDIR /build
COPY Cargo.toml Cargo.lock ./
COPY crates/ crates/
COPY proto/ proto/
COPY dev-sandbox-policy.rego ./
# 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},sharing=locked,target=/build/target \
. 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)
FROM python:3.12-slim 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 -s /usr/sbin/nologin sandbox
# Stage 3: Python dependencies builder
FROM base AS builder
COPY --from=ghcr.io/astral-sh/uv:latest /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
RUN uv pip install --python /app/.venv/bin/python --no-cache-dir cloudpickle grpcio protobuf
# Stage 4: Coding agents layer
FROM base AS coding-agents
# Install Node.js 22 via NodeSource (required by OpenClaw and other agents)
# build-essential & python3 are needed for npm packages with native addons (node-gyp)
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get install -y --no-install-recommends git nodejs build-essential python3 && \
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
# Install OpenClaw CLI via npm (the native installer uses npm under the hood;
# installing directly avoids TTY and onboarding issues in Docker builds)
RUN npm install -g openclaw
# Install OpenCode CLI and Codex CLI (OpenAI)
RUN npm install -g opencode-ai @openai/codex
# 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 venv to PATH
ENV PATH="/app/.venv/bin:$PATH"
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
# OpenClaw startup helper for environments without systemd (e.g. sandboxes).
# Usage: nav sandbox create --forward 18789 -- openclaw-start
COPY deploy/docker/openclaw-start.sh /usr/local/bin/openclaw-start
RUN chmod +x /usr/local/bin/openclaw-start
ENTRYPOINT ["navigator-sandbox"]