pub const DOCKERFILE: &str = "# =============================================================================\n# opencode-cloud Container Image\n# =============================================================================\n# A comprehensive development environment for AI-assisted coding with opencode.\n#\n# Features:\n# - Ubuntu 24.04 LTS (noble) base\n# - Non-root user with passwordless sudo\n# - Multiple languages via mise (Node.js, Python, Rust, Go)\n# - Modern CLI tools (ripgrep, eza, fzf, lazygit, etc.)\n# - GSD opencode plugin for task management\n#\n# Usage:\n# docker build -t opencode-cloud .\n# docker run -it opencode-cloud\n#\n# =============================================================================\n\n# -----------------------------------------------------------------------------\n# Version Pinning Policy\n# -----------------------------------------------------------------------------\n# - APT packages: Use major.minor.* wildcards for patch updates\n# - GitHub tools: Pin to release tags (vX.Y.Z)\n# - Cargo/Go: Pin to exact versions (@X.Y.Z)\n# - Security exceptions marked with: # UNPINNED: package - reason\n# - Self-managing installers (mise, rustup, etc.) trusted to handle versions\n#\n# To check for updates: just check-updates\n# Last version audit: 2026-01-22\n# -----------------------------------------------------------------------------\n\n# -----------------------------------------------------------------------------\n# Stage 1: Runtime\n# -----------------------------------------------------------------------------\nFROM ubuntu:24.04 AS runtime\n\n# OCI Labels for image metadata\nLABEL org.opencontainers.image.title=\"opencode-cloud\"\nLABEL org.opencontainers.image.description=\"AI-assisted development environment with opencode\"\nLABEL org.opencontainers.image.url=\"https://github.com/pRizz/opencode-cloud\"\nLABEL org.opencontainers.image.source=\"https://github.com/pRizz/opencode-cloud\"\nLABEL org.opencontainers.image.vendor=\"pRizz\"\nLABEL org.opencontainers.image.licenses=\"MIT\"\nLABEL org.opencontainers.image.base.name=\"ubuntu:24.04\"\n\n# Environment configuration\nENV DEBIAN_FRONTEND=noninteractive\nENV TZ=UTC\nENV LANG=C.UTF-8\nENV LC_ALL=C.UTF-8\n\n# -----------------------------------------------------------------------------\n# System Dependencies\n# -----------------------------------------------------------------------------\n# Install core system packages in logical groups for better caching\n\n# Group 1: Core utilities and build tools (2026-01-22)\n# Use BuildKit cache mount for APT package lists and cache\nRUN --mount=type=cache,target=/var/lib/apt/lists \\\n --mount=type=cache,target=/var/cache/apt \\\n apt-get update && apt-get install -y --no-install-recommends \\\n # Init systems\n tini=0.19.* \\\n dumb-init=1.2.* \\\n # systemd for Cockpit support\n systemd=255.* \\\n systemd-sysv=255.* \\\n dbus=1.14.* \\\n # Shell and terminal\n zsh=5.9-* \\\n tmux=3.4-* \\\n # Editors\n vim=2:9.1.* \\\n neovim=0.9.* \\\n nano=7.2-* \\\n # Build essentials\n build-essential=12.* \\\n pkg-config=1.8.* \\\n cmake=3.28.* \\\n # Version control\n git=1:2.43.* \\\n git-lfs=3.4.* \\\n # Core utilities\n curl=8.5.* \\\n wget=1.21.* \\\n # UNPINNED: ca-certificates - security-critical root certs, needs auto-updates\n ca-certificates \\\n # UNPINNED: gnupg - key management security, needs auto-updates\n gnupg \\\n lsb-release=12.* \\\n software-properties-common=0.99.* \\\n sudo=1.9.* \\\n # UNPINNED: openssh-client - security-critical, needs auto-updates\n openssh-client \\\n # Process/system tools\n htop=3.3.* \\\n procps=2:4.0.* \\\n less=590-* \\\n file=1:5.45-* \\\n tree=2.1.* \\\n # JSON/YAML processing\n jq=1.7.* \\\n # Network tools\n netcat-openbsd=1.226-* \\\n iputils-ping=3:20240117-* \\\n dnsutils=1:9.18.* \\\n # Reverse proxy for opencode UI + API\n nginx=1.24.* \\\n # Compression\n zip=3.0-* \\\n unzip=6.0-* \\\n xz-utils=5.6.* \\\n p7zip-full=16.02* \\\n && rm -rf /var/lib/apt/lists/*\n\n# Mask unnecessary systemd services for container environment\nRUN systemctl mask \\\n dev-hugepages.mount \\\n sys-fs-fuse-connections.mount \\\n systemd-update-utmp.service \\\n systemd-tmpfiles-setup.service \\\n systemd-remount-fs.service\n\n# Group 2: Database clients (2026-01-22)\n# Use BuildKit cache mount for APT package lists and cache\nRUN --mount=type=cache,target=/var/lib/apt/lists \\\n --mount=type=cache,target=/var/cache/apt \\\n apt-get update && apt-get install -y --no-install-recommends \\\n sqlite3=3.45.* \\\n postgresql-client=16+* \\\n default-mysql-client=1.1.* \\\n && rm -rf /var/lib/apt/lists/*\n\n# Group 3: Development libraries for compiling tools (2026-01-22)\n# Use BuildKit cache mount for APT package lists and cache\nRUN --mount=type=cache,target=/var/lib/apt/lists \\\n --mount=type=cache,target=/var/cache/apt \\\n apt-get update && apt-get install -y --no-install-recommends \\\n # libssl-dev depends on libssl3t64 with exact version match\n libssl3t64=3.0.* \\\n libssl-dev=3.0.* \\\n libffi-dev=3.4.* \\\n zlib1g-dev=1:1.3.* \\\n libbz2-dev=1.0.* \\\n libreadline-dev=8.2-* \\\n libsqlite3-dev=3.45.* \\\n libncurses-dev=6.4+* \\\n libpam0g-dev=1.5.* \\\n liblzma-dev=5.6.* \\\n && rm -rf /var/lib/apt/lists/*\n\n# -----------------------------------------------------------------------------\n# Create Non-Root User\n# -----------------------------------------------------------------------------\n# Create \'opencode\' user with passwordless sudo\nRUN useradd -m -s /bin/zsh -G sudo opencode \\\n && echo \"opencode ALL=(ALL) NOPASSWD:ALL\" > /etc/sudoers.d/opencode \\\n && chmod 0440 /etc/sudoers.d/opencode\n\n# Switch to opencode user for remaining setup\nUSER opencode\nWORKDIR /home/opencode\n\n# Set up directories\nRUN mkdir -p \\\n /home/opencode/.config \\\n /home/opencode/.local/bin \\\n /home/opencode/.local/share \\\n /home/opencode/.cache \\\n /home/opencode/workspace\n\n# Add local bin to PATH\nENV PATH=\"/home/opencode/.local/bin:${PATH}\"\n\n# -----------------------------------------------------------------------------\n# Shell Setup: Zsh + Oh My Zsh + Starship\n# -----------------------------------------------------------------------------\n# Oh My Zsh - self-managing installer, trusted to handle versions\n# Disabled temporarily to reduce Docker build time.\n# RUN sh -c \"$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\" \"\" --unattended\n\n# Starship prompt - self-managing installer, trusted to handle versions\n# Disabled temporarily to reduce Docker build time.\n# RUN curl -sS https://starship.rs/install.sh | sh -s -- --yes --bin-dir /home/opencode/.local/bin\n\n# Configure zsh with starship\n# Disabled temporarily to reduce Docker build time.\n# RUN echo \'eval \"$(starship init zsh)\"\' >> /home/opencode/.zshrc \\\n# && echo \'export PATH=\"/home/opencode/.local/bin:$PATH\"\' >> /home/opencode/.zshrc\n\n# -----------------------------------------------------------------------------\n# mise: Universal Version Manager\n# -----------------------------------------------------------------------------\n# mise - self-managing installer, trusted to handle versions\nRUN curl https://mise.run | sh \\\n && echo \'eval \"$(/home/opencode/.local/bin/mise activate zsh)\"\' >> /home/opencode/.zshrc\n\n# Install language runtimes via mise (2026-01-22)\n# - node@lts: mise handles LTS resolution (currently 22.x)\n# - python@3.12: pinned to minor version\n# - go@1.24: pinned to minor version (was @latest)\nRUN /home/opencode/.local/bin/mise install node@lts \\\n && /home/opencode/.local/bin/mise install python@3.12 \\\n && /home/opencode/.local/bin/mise install go@1.24 \\\n && /home/opencode/.local/bin/mise use --global node@lts \\\n && /home/opencode/.local/bin/mise use --global python@3.12 \\\n && /home/opencode/.local/bin/mise use --global go@1.24\n\n# Set up mise shims in PATH for non-interactive shells\nENV PATH=\"/home/opencode/.local/share/mise/shims:${PATH}\"\n\n# -----------------------------------------------------------------------------\n# Rust Installation\n# -----------------------------------------------------------------------------\n# rustup - self-managing installer, trusted to handle versions\n# Uses stable toolchain (rustup manages toolchain versioning)\nRUN curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable \\\n && . /home/opencode/.cargo/env \\\n && rustup default stable \\\n && rustup component add rust-analyzer rustfmt clippy\n\nENV PATH=\"/home/opencode/.cargo/bin:${PATH}\"\n\n# -----------------------------------------------------------------------------\n# Package Managers\n# -----------------------------------------------------------------------------\n# Switch to bash for mise activation (mise outputs bash-specific syntax)\nSHELL [\"/bin/bash\", \"-c\"]\n\n# Install pnpm 10.x via corepack (2026-01-22)\nRUN eval \"$(/home/opencode/.local/bin/mise activate bash)\" \\\n && corepack enable \\\n && corepack prepare pnpm@10.28.1 --activate\n\n# Set up pnpm global bin directory\nENV PNPM_HOME=\"/home/opencode/.local/share/pnpm\"\nENV PATH=\"${PNPM_HOME}:${PATH}\"\nRUN mkdir -p \"${PNPM_HOME}\"\n\n\n# uv - self-managing installer, trusted to handle versions (fast Python package manager)\nRUN curl -LsSf https://astral.sh/uv/install.sh | sh\n\n# Install pipx for isolated Python application installs\n# Use BuildKit cache mount for pip cache\nRUN --mount=type=cache,target=/home/opencode/.cache/pip,uid=1000,gid=1000,mode=0755 \\\n mkdir -p /home/opencode/.cache/pip \\\n && eval \"$(/home/opencode/.local/bin/mise activate bash)\" \\\n && pip install --user pipx \\\n && pipx ensurepath\n\n# Install global TypeScript compiler\n# NOTE: Avoid cache mount here to prevent pnpm store permission issues\nRUN eval \"$(/home/opencode/.local/bin/mise activate bash)\" \\\n && pnpm add -g typescript\n\n# -----------------------------------------------------------------------------\n# Modern CLI Tools (Rust-based) - pinned versions (2026-01-22)\n# -----------------------------------------------------------------------------\n# ripgrep 15.1.0 - fast regex search\n# eza 0.23.4 - modern ls replacement\n# NOTE: Avoid cache mounts here due to permission issues with non-root Cargo cache\nRUN mkdir -p /home/opencode/.cargo/registry /home/opencode/.cargo/git \\\n && . /home/opencode/.cargo/env \\\n && cargo install --locked ripgrep@15.1.0 eza@0.23.4 \\\n && rm -rf /home/opencode/.cargo/registry /home/opencode/.cargo/git\n\n# lazygit v0.58.1 (2026-01-12) - terminal UI for git\n# Disabled temporarily to reduce Docker build time.\n# RUN eval \"$(/home/opencode/.local/bin/mise activate bash)\" \\\n# && go install github.com/jesseduffield/lazygit@v0.58.1\n\n# -----------------------------------------------------------------------------\n# Additional Development Tools\n# -----------------------------------------------------------------------------\n# fzf v0.67.0 (2025-11-16) - fuzzy finder\n# Disabled temporarily to reduce Docker build time.\n# RUN git clone --branch v0.67.0 --depth 1 https://github.com/junegunn/fzf.git /home/opencode/.fzf \\\n# && /home/opencode/.fzf/install --all --no-bash --no-fish\n\n# yq v4.50.1 (2025-12-14) - YAML processor\n# Disabled temporarily to reduce Docker build time.\n# RUN curl -sL https://github.com/mikefarah/yq/releases/download/v4.50.1/yq_linux_$(dpkg --print-architecture) -o /home/opencode/.local/bin/yq \\\n# && chmod +x /home/opencode/.local/bin/yq\n\n# Install direnv (2026-01-22)\n# Disabled temporarily to reduce Docker build time.\n# USER root\n# RUN apt-get update && apt-get install -y --no-install-recommends direnv=2.32.* \\\n# && rm -rf /var/lib/apt/lists/*\n# USER opencode\n# RUN echo \'eval \"$(direnv hook zsh)\"\' >> /home/opencode/.zshrc\n\n# Install HTTPie\n# Disabled temporarily to reduce Docker build time.\n# RUN eval \"$(/home/opencode/.local/bin/mise activate bash)\" \\\n# && pipx install httpie\n\n# Install shellcheck (2026-01-22)\n# Disabled temporarily to reduce Docker build time.\n# USER root\n# RUN apt-get update && apt-get install -y --no-install-recommends shellcheck=0.9.* \\\n# && rm -rf /var/lib/apt/lists/*\n# USER opencode\n# shfmt v3.12.0 (2025-07-06) - shell formatter\n# Disabled temporarily to reduce Docker build time.\n# RUN eval \"$(/home/opencode/.local/bin/mise activate bash)\" \\\n# && go install mvdan.cc/sh/v3/cmd/shfmt@v3.12.0\n\n# Install btop system monitor (2026-01-22)\n# Use BuildKit cache mount for APT package lists and cache\nUSER root\nRUN --mount=type=cache,target=/var/lib/apt/lists \\\n --mount=type=cache,target=/var/cache/apt \\\n apt-get update && apt-get install -y --no-install-recommends btop=1.3.* \\\n && rm -rf /var/lib/apt/lists/*\nUSER opencode\n\n# -----------------------------------------------------------------------------\n# GitHub CLI\n# -----------------------------------------------------------------------------\nUSER root\n# Use BuildKit cache mount for APT package lists and cache\nRUN --mount=type=cache,target=/var/lib/apt/lists \\\n --mount=type=cache,target=/var/cache/apt \\\n curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \\\n && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \\\n && 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 \\\n && apt-get update && apt-get install -y --no-install-recommends gh \\\n && rm -rf /var/lib/apt/lists/*\nUSER opencode\n\n# -----------------------------------------------------------------------------\n# Cockpit Web Console (2026-01-22) - temporarily disabled\n# -----------------------------------------------------------------------------\n# Cockpit provides web-based administration for the container\n# Ubuntu noble has cockpit 316 in main repos\n# Use BuildKit cache mount for APT package lists and cache\n# USER root\n# RUN --mount=type=cache,target=/var/lib/apt/lists \\\n# --mount=type=cache,target=/var/cache/apt \\\n# apt-get update && \\\n# apt-get install -y --no-install-recommends \\\n# cockpit-ws \\\n# cockpit-system \\\n# cockpit-bridge \\\n# && rm -rf /var/lib/apt/lists/*\n#\n# Enable Cockpit socket activation (manual symlink since systemctl doesn\'t work during build)\n# RUN mkdir -p /etc/systemd/system/sockets.target.wants \\\n# && ln -sf /lib/systemd/system/cockpit.socket /etc/systemd/system/sockets.target.wants/cockpit.socket\n#\n# Configure Cockpit for HTTP (TLS terminated externally) and proxy headers\n# RUN mkdir -p /etc/cockpit && \\\n# printf \'%s\\n\' \\\n# \'[WebService]\' \\\n# \'# Allow HTTP connections (TLS terminated externally like opencode)\' \\\n# \'AllowUnencrypted = true\' \\\n# \'\' \\\n# \'# Trust proxy headers for X-Forwarded-For, X-Forwarded-Proto\' \\\n# \'ProtocolHeader = X-Forwarded-Proto\' \\\n# \'ForwardedForHeader = X-Forwarded-For\' \\\n# \'\' \\\n# \'# Limit concurrent login attempts\' \\\n# \'MaxStartups = 10\' \\\n# > /etc/cockpit/cockpit.conf\n#\n# USER opencode\n\n# -----------------------------------------------------------------------------\n# CI/CD + tooling (disabled)\n# -----------------------------------------------------------------------------\n# NOTE: Commented out because this section adds significant time in GitHub Actions\n# builds. We will reconsider re-adding these tools later, potentially in a more\n# templated/configured Docker image optimized for build tooling.\n# -----------------------------------------------------------------------------\n# # CI/CD Tools\n# # -----------------------------------------------------------------------------\n# # act v0.2.84 (2026-01-01) - run GitHub Actions locally\n# RUN curl -sL https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash -s -- -b /home/opencode/.local/bin v0.2.84\n#\n# # -----------------------------------------------------------------------------\n# # Rust Tooling - pinned versions (2026-01-22)\n# # -----------------------------------------------------------------------------\n# # cargo-nextest 0.9.122 - fast test runner\n# # cargo-audit 0.22.0 - security audit\n# # cargo-deny 0.19.0 - dependency linter\n# RUN . /home/opencode/.cargo/env \\\n# && cargo install --locked cargo-nextest@0.9.122 cargo-audit@0.22.0 cargo-deny@0.19.0\n#\n# # Install mold fast linker (2026-01-22)\n# USER root\n# RUN apt-get update && apt-get install -y --no-install-recommends mold=2.30.* \\\n# && rm -rf /var/lib/apt/lists/*\n# USER opencode\n#\n# # -----------------------------------------------------------------------------\n# # Code Quality Tools\n# # -----------------------------------------------------------------------------\n# # JavaScript/TypeScript tools\n# RUN eval \"$(/home/opencode/.local/bin/mise activate bash)\" \\\n# && pnpm add -g \\\n# prettier \\\n# eslint \\\n# @biomejs/biome\n#\n# # Python tools\n# RUN eval \"$(/home/opencode/.local/bin/mise activate bash)\" \\\n# && pipx install black \\\n# && pipx install ruff\n#\n# # Test runners (commonly needed)\n# RUN eval \"$(/home/opencode/.local/bin/mise activate bash)\" \\\n# && pnpm add -g jest vitest\n#\n# # Python pytest via pipx\n# RUN eval \"$(/home/opencode/.local/bin/mise activate bash)\" \\\n# && pipx install pytest\n#\n# # -----------------------------------------------------------------------------\n# # Protocol Buffers / gRPC (2026-01-22)\n# # -----------------------------------------------------------------------------\n# USER root\n# RUN apt-get update && apt-get install -y --no-install-recommends protobuf-compiler=3.21.* \\\n# && rm -rf /var/lib/apt/lists/*\n# USER opencode\n#\n# # grpcurl v1.9.3 (2025-03-11) - gRPC debugging tool\n# RUN eval \"$(/home/opencode/.local/bin/mise activate bash)\" \\\n# && go install github.com/fullstorydev/grpcurl/cmd/grpcurl@v1.9.3\n\n# -----------------------------------------------------------------------------\n# Sensible Defaults Configuration\n# -----------------------------------------------------------------------------\n# Git configuration\nRUN git config --global init.defaultBranch main \\\n && git config --global core.editor \"nvim\" \\\n && git config --global pull.rebase false \\\n && git config --global merge.conflictstyle diff3 \\\n && git config --global diff.colorMoved default\n\n# Starship configuration (minimal, fast prompt)\nRUN mkdir -p /home/opencode/.config \\\n && printf \'%s\\n\' \\\n \'# Minimal starship config for fast prompt\' \\\n \'format = \"\"\"\' \\\n \'$directory\\\' \\\n \'$git_branch\\\' \\\n \'$git_status\\\' \\\n \'$character\"\"\"\' \\\n \'\' \\\n \'[directory]\' \\\n \'truncation_length = 3\' \\\n \'truncate_to_repo = true\' \\\n \'\' \\\n \'[git_branch]\' \\\n \'format = \"[$branch]($style) \"\' \\\n \'style = \"bold purple\"\' \\\n \'\' \\\n \'[git_status]\' \\\n \'format = \'\"\'\"\'([$all_status$ahead_behind]($style) )\'\"\'\"\'\' \\\n \'\' \\\n \'[character]\' \\\n \'success_symbol = \"[>](bold green)\"\' \\\n \'error_symbol = \"[>](bold red)\"\' \\\n > /home/opencode/.config/starship.toml\n\n# Shell aliases\nRUN printf \'%s\\n\' \\\n \'\' \\\n \'# Modern CLI aliases\' \\\n \'alias ls=\"eza --icons\"\' \\\n \'alias ll=\"eza -l --icons\"\' \\\n \'alias la=\"eza -la --icons\"\' \\\n \'alias lt=\"eza --tree --icons\"\' \\\n \'alias grep=\"rg\"\' \\\n \'alias top=\"btop\"\' \\\n \'\' \\\n \'# Git aliases\' \\\n \'alias g=\"git\"\' \\\n \'alias gs=\"git status\"\' \\\n \'alias gd=\"git diff\"\' \\\n \'alias gc=\"git commit\"\' \\\n \'alias gp=\"git push\"\' \\\n \'alias gl=\"git pull\"\' \\\n \'alias gco=\"git checkout\"\' \\\n \'alias gb=\"git branch\"\' \\\n \'alias lg=\"lazygit\"\' \\\n \'\' \\\n \'# Docker aliases (for Docker-in-Docker)\' \\\n \'alias d=\"docker\"\' \\\n \'alias dc=\"docker compose\"\' \\\n \'\' \\\n >> /home/opencode/.zshrc\n\n# Set up pipx path\nRUN echo \'export PATH=\"/home/opencode/.local/bin:$PATH\"\' >> /home/opencode/.zshrc\n\n# -----------------------------------------------------------------------------\n# opencode Setup (Fork + Broker + Proxy)\n# -----------------------------------------------------------------------------\n# Keep this block near the end to preserve cache for earlier layers. We expect\n# opencode fork changes to happen more often than base tooling changes.\n#\n# This block includes:\n# - opencode build (backend binary) + app build (frontend dist)\n# - opencode-broker build\n# - nginx config for single-endpoint UI + API proxy\n# - PAM configuration + systemd services\n# - opencode config file\n#\n# NOTE: This section switches between opencode and root users as needed.\n\n# -----------------------------------------------------------------------------\n# opencode Installation (Fork from pRizz/opencode)\n# -----------------------------------------------------------------------------\n# Clone the fork and build opencode from source (as non-root user)\n# Pin to specific commit for reproducibility\n# Build opencode from source (BuildKit cache mounts disabled for now)\nRUN rm -rf /tmp/opencode-repo \\\n && git clone --depth 1 https://github.com/pRizz/opencode.git /tmp/opencode-repo \\\n && cd /tmp/opencode-repo \\\n && git checkout 798ccdba1265b7e5499ba49db2f99ca1dd4a15d7 \\\n && curl -fsSL https://bun.sh/install | bash -s \"bun-v1.3.5\" \\\n && export PATH=\"/home/opencode/.bun/bin:${PATH}\" \\\n && bun install --frozen-lockfile \\\n && bun run packages/opencode/script/build.ts --single \\\n && cd packages/app \\\n && bun run build \\\n && rm -rf /home/opencode/.bun/install/cache /home/opencode/.bun/cache /home/opencode/.cache/bun \\\n && cd /tmp/opencode-repo \\\n && mkdir -p /home/opencode/.local/share/opencode/bin \\\n && cp /tmp/opencode-repo/packages/opencode/dist/opencode-*/bin/opencode /home/opencode/.local/share/opencode/bin/opencode \\\n && chown -R opencode:opencode /home/opencode/.local/share/opencode \\\n && chmod +x /home/opencode/.local/share/opencode/bin/opencode \\\n && /home/opencode/.local/share/opencode/bin/opencode --version\n\n# Add opencode to PATH\nENV PATH=\"/home/opencode/.local/share/opencode/bin:${PATH}\"\n\n# Copy UI assets to standard web root (requires root)\nUSER root\nRUN mkdir -p /var/www/opencode \\\n && cp -R /tmp/opencode-repo/packages/app/dist/. /var/www/opencode/ \\\n && chown -R root:root /var/www/opencode \\\n && chmod 755 /var/www /var/www/opencode \\\n && chmod -R a+rX /var/www/opencode\n\n# -----------------------------------------------------------------------------\n# opencode-broker Installation\n# -----------------------------------------------------------------------------\n# Build opencode-broker from source (Rust service for PAM authentication)\n# The broker handles PAM authentication and user process spawning\n# NOTE: Requires root privileges for setuid bit (chmod 4755) to allow broker\n# to run with elevated privileges for PAM authentication\nUSER root\nRUN cd /tmp/opencode-repo/packages/opencode-broker \\\n && runuser -u opencode -- bash -c \'. /home/opencode/.cargo/env && cargo build --release\' \\\n && mkdir -p /usr/local/bin \\\n && cp target/release/opencode-broker /usr/local/bin/opencode-broker \\\n && chmod 4755 /usr/local/bin/opencode-broker \\\n && rm -rf /tmp/opencode-repo\n\n# Verify broker binary exists and is executable\nRUN ls -la /usr/local/bin/opencode-broker \\\n && test -x /usr/local/bin/opencode-broker \\\n && echo \"Broker installed\"\n\n# -----------------------------------------------------------------------------\n# Nginx Reverse Proxy for UI + API\n# -----------------------------------------------------------------------------\n# Serve the built UI from /var/www/opencode and proxy API/WebSocket\n# TODO: Explore a sustainable routing strategy (e.g., backend-driven base URL or\n# TODO: content-type aware proxying without hardcoded path lists).\nRUN rm -f /etc/nginx/sites-enabled/default /etc/nginx/conf.d/default.conf 2>/dev/null || true \\\n && printf \'%s\\n\' \\\n \'server {\' \\\n \' listen 3000;\' \\\n \' server_name _;\' \\\n \'\' \\\n \' root /var/www/opencode;\' \\\n \' index index.html;\' \\\n \'\' \\\n \' location = /health {\' \\\n \' proxy_pass http://127.0.0.1:3001;\' \\\n \' }\' \\\n \'\' \\\n \' location /assets/ {\' \\\n \' try_files $uri =404;\' \\\n \' }\' \\\n \'\' \\\n \' location ~* \\.(?:js|css|png|jpg|jpeg|gif|svg|ico|webp|woff2?|ttf|map)$ {\' \\\n \' try_files $uri =404;\' \\\n \' }\' \\\n \'\' \\\n \' location ~ ^/(agent|auth|command|config|event|events|global|lsp|mcp|path|permission|project|provider|pty|question|rpc|session|sessions|status|v1|vcs|ws|api) {\' \\\n \' proxy_pass http://127.0.0.1:3001;\' \\\n \' proxy_http_version 1.1;\' \\\n \' proxy_set_header Upgrade $http_upgrade;\' \\\n \' proxy_set_header Connection \"upgrade\";\' \\\n \' proxy_set_header Host $host;\' \\\n \' proxy_set_header X-Real-IP $remote_addr;\' \\\n \' proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\' \\\n \' proxy_set_header X-Forwarded-Proto $scheme;\' \\\n \' }\' \\\n \'\' \\\n \' location / {\' \\\n \' try_files $uri $uri/ /index.html;\' \\\n \' }\' \\\n \'}\' \\\n > /etc/nginx/conf.d/opencode.conf\n\n# -----------------------------------------------------------------------------\n# PAM Configuration\n# -----------------------------------------------------------------------------\n# Install PAM configuration for opencode authentication\n# This allows opencode to authenticate users via PAM (same users as Cockpit)\n# NOTE: Requires root privileges to write to /etc/pam.d/\nRUN printf \'%s\\n\' \\\n \'# PAM configuration for OpenCode authentication\' \\\n \'# Install to /etc/pam.d/opencode\' \\\n \'\' \\\n \'# Standard UNIX authentication\' \\\n \'auth required pam_unix.so\' \\\n \'account required pam_unix.so\' \\\n \'\' \\\n \'# Optional: Enable TOTP 2FA (uncomment when pam_google_authenticator is installed)\' \\\n \'# auth required pam_google_authenticator.so\' \\\n > /etc/pam.d/opencode \\\n && chmod 644 /etc/pam.d/opencode\n\n# Verify PAM config file exists\nRUN ls -la /etc/pam.d/opencode && cat /etc/pam.d/opencode\n\n# -----------------------------------------------------------------------------\n# opencode-broker systemd Service\n# -----------------------------------------------------------------------------\n# Create opencode-broker service for PAM authentication\n# NOTE: Requires root privileges to write to /etc/systemd/system/\nRUN printf \'%s\\n\' \\\n \'[Unit]\' \\\n \'Description=OpenCode Authentication Broker\' \\\n \'Documentation=https://github.com/pRizz/opencode\' \\\n \'After=network.target\' \\\n \'\' \\\n \'[Service]\' \\\n \'Type=notify\' \\\n \'ExecStart=/usr/local/bin/opencode-broker\' \\\n \'ExecReload=/bin/kill -HUP $MAINPID\' \\\n \'Restart=always\' \\\n \'RestartSec=5\' \\\n \'\' \\\n \'# Security hardening\' \\\n \'NoNewPrivileges=false\' \\\n \'ProtectSystem=strict\' \\\n \'ProtectHome=read-only\' \\\n \'PrivateTmp=true\' \\\n \'ReadWritePaths=/run/opencode\' \\\n \'\' \\\n \'# Socket directory\' \\\n \'RuntimeDirectory=opencode\' \\\n \'RuntimeDirectoryMode=0755\' \\\n \'\' \\\n \'# Logging\' \\\n \'StandardOutput=journal\' \\\n \'StandardError=journal\' \\\n \'SyslogIdentifier=opencode-broker\' \\\n \'\' \\\n \'[Install]\' \\\n \'WantedBy=multi-user.target\' \\\n > /etc/systemd/system/opencode-broker.service\n\n# Enable opencode-broker service\nRUN mkdir -p /etc/systemd/system/multi-user.target.wants \\\n && ln -sf /etc/systemd/system/opencode-broker.service /etc/systemd/system/multi-user.target.wants/opencode-broker.service\n\nUSER opencode\n\n# -----------------------------------------------------------------------------\n# GSD Plugin Installation\n# -----------------------------------------------------------------------------\n# Install the GSD (Get Shit Done) plugin for opencode\n# Note: If this fails in container builds due to \"~\" path resolution, retry with\n# OPENCODE_CONFIG_DIR=/home/opencode/.config/opencode set explicitly.\nRUN mkdir -p /home/opencode/.npm \\\n && npx --yes get-shit-done-cc --opencode --global \\\n && rm -rf /home/opencode/.npm/_cacache /home/opencode/.npm/_npx\n\n# -----------------------------------------------------------------------------\n# opencode systemd Service (2026-01-22)\n# -----------------------------------------------------------------------------\n# Create opencode as a systemd service for Cockpit integration (backend only)\n# NOTE: Requires root privileges to write to /etc/systemd/system/\nUSER root\nRUN printf \'%s\\n\' \\\n \'[Unit]\' \\\n \'Description=opencode Web Interface\' \\\n \'After=network.target opencode-broker.service\' \\\n \'\' \\\n \'[Service]\' \\\n \'Type=simple\' \\\n \'User=opencode\' \\\n \'WorkingDirectory=/home/opencode/workspace\' \\\n \'ExecStart=/home/opencode/.local/share/opencode/bin/opencode --port 3001 --hostname 0.0.0.0\' \\\n \'Restart=always\' \\\n \'RestartSec=5\' \\\n \'Environment=PATH=/home/opencode/.local/share/opencode/bin:/home/opencode/.local/bin:/home/opencode/.cargo/bin:/home/opencode/.local/share/mise/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\' \\\n \'\' \\\n \'[Install]\' \\\n \'WantedBy=multi-user.target\' \\\n > /etc/systemd/system/opencode.service\n\n# Enable opencode service to start at boot (manual symlink since systemctl doesn\'t work during build)\nRUN mkdir -p /etc/systemd/system/multi-user.target.wants \\\n && ln -sf /etc/systemd/system/opencode.service /etc/systemd/system/multi-user.target.wants/opencode.service\n\n# Nginx service for serving UI + proxying API\nRUN printf \'%s\\n\' \\\n \'[Unit]\' \\\n \'Description=Nginx reverse proxy for opencode UI\' \\\n \'After=network.target opencode.service\' \\\n \'\' \\\n \'[Service]\' \\\n \'Type=simple\' \\\n \'ExecStart=/usr/sbin/nginx -g \"daemon off;\"\' \\\n \'ExecReload=/usr/sbin/nginx -s reload\' \\\n \'Restart=always\' \\\n \'RestartSec=5\' \\\n \'\' \\\n \'[Install]\' \\\n \'WantedBy=multi-user.target\' \\\n > /etc/systemd/system/opencode-nginx.service\n\n# Enable nginx service\nRUN mkdir -p /etc/systemd/system/multi-user.target.wants \\\n && ln -sf /etc/systemd/system/opencode-nginx.service /etc/systemd/system/multi-user.target.wants/opencode-nginx.service\n\n# Prevent the distro nginx service from also starting (port 3000 conflict)\nRUN rm -f /etc/systemd/system/multi-user.target.wants/nginx.service \\\n && ln -sf /dev/null /etc/systemd/system/nginx.service\n\n# -----------------------------------------------------------------------------\n# opencode Configuration\n# -----------------------------------------------------------------------------\n# Create opencode.json config file with PAM authentication enabled\nRUN mkdir -p /home/opencode/.config/opencode \\\n && printf \'%s\\n\' \\\n \'{\' \\\n \' \"auth\": {\' \\\n \' \"enabled\": true\' \\\n \' }\' \\\n \'}\' \\\n > /home/opencode/.config/opencode/opencode.json \\\n && chown -R opencode:opencode /home/opencode/.config/opencode \\\n && chmod 644 /home/opencode/.config/opencode/opencode.json\n\n# Verify config file exists\nRUN ls -la /home/opencode/.config/opencode/opencode.json && cat /home/opencode/.config/opencode/opencode.json\n\nUSER opencode\n\n# -----------------------------------------------------------------------------\n# Entrypoint Script (Hybrid Init Support)\n# -----------------------------------------------------------------------------\n# Supports both tini (default, works everywhere) and systemd (for Cockpit on Linux)\n# Set USE_SYSTEMD=1 environment variable to use systemd init\n# Note: Entrypoint runs as root to support both modes; tini mode drops to opencode user\nUSER root\nRUN printf \'%s\\n\' \\\n \'#!/bin/bash\' \\\n \'if [ \"${USE_SYSTEMD}\" = \"1\" ]; then\' \\\n \' exec /sbin/init\' \\\n \'else\' \\\n \' # Use runuser to switch to opencode user without password prompt\' \\\n \' runuser -u opencode -- /home/opencode/.local/share/opencode/bin/opencode --port 3001 --hostname 0.0.0.0 &\' \\\n \' exec /usr/sbin/nginx -g \"daemon off;\"\' \\\n \'fi\' \\\n > /usr/local/bin/entrypoint.sh && chmod +x /usr/local/bin/entrypoint.sh\n\n# Note: Don\'t set USER here - entrypoint needs root to use runuser\n# The tini mode drops privileges to opencode user via runuser\n\n# -----------------------------------------------------------------------------\n# Health Check\n# -----------------------------------------------------------------------------\n# Check that opencode health endpoint responds\n# Works for both tini and systemd modes\nHEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \\\n CMD curl -f http://localhost:3000/health || exit 1\n\n# -----------------------------------------------------------------------------\n# Version File\n# -----------------------------------------------------------------------------\n# Store version in file for runtime access (debugging, scripts)\n# Keep this near the end: changing the version invalidates the cache\n# and significantly slows down docker builds if placed earlier.\nUSER root\nARG OPENCODE_CLOUD_VERSION=dev\nLABEL org.opencode-cloud.version=\"${OPENCODE_CLOUD_VERSION}\"\nRUN echo \"${OPENCODE_CLOUD_VERSION}\" > /etc/opencode-cloud-version\n# Note: Stay as root - entrypoint.sh needs root to run either:\n# - /sbin/init (systemd mode)\n# - runuser (tini mode, which then drops privileges to opencode user)\n\n# -----------------------------------------------------------------------------\n# Final Configuration\n# -----------------------------------------------------------------------------\nWORKDIR /home/opencode/workspace\n\n# Expose opencode web port (3000) and Cockpit port (9090)\nEXPOSE 3000 9090\n\n# Hybrid init: entrypoint script chooses tini or systemd based on USE_SYSTEMD env\nENTRYPOINT [\"/usr/local/bin/entrypoint.sh\"]\n";Expand description
The Dockerfile for building the opencode-cloud-sandbox container image