# Autonomous port agent for the Sylpheed Godot menu shell. # # DELIBERATELY SMALL. The reverse-engineering container next door is 4.36 GB # because it builds Xenia Canary and drives it under a software Vulkan stack. # This agent has no emulator, no oracle and no C++ build: it converts already- # decoded assets and drives Godot. Keeping it light is what lets both containers # run on one 12-core / 15 GB box without the memory pressure that has crashed it. # # What it needs, and nothing else: Rust (the exporter), Godot 4 (the runtime), # ffmpeg (the transcode), and a headless display to screenshot Godot for # comparison against the reference renderer. FROM ubuntu:24.04 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 \ TZ=Etc/UTC RUN apt-get update && apt-get install -y --no-install-recommends \ # toolchain for the exporter and for building sylpheed-cli from /reborn build-essential pkg-config git curl ca-certificates \ libssl-dev \ # Godot 4 needs these even headless; the windowed run needs the X libs libx11-6 libxcursor1 libxinerama1 libxrandr2 libxi6 libgl1 \ libasound2t64 libpulse0 libfontconfig1 \ # the transcode target (libtheora + libvorbis ship in Ubuntu's ffmpeg) ffmpeg \ # headless display + the screenshot path, for diffing Godot's output # against `sylpheed-cli screen render` xvfb x11-utils openbox imagemagick \ # everyday python3 jq ripgrep unzip file less nano tini sudo procps \ # expect drives Claude Code's one-time interactive gates expect \ && rm -rf /var/lib/apt/lists/* # ── Godot 4 ────────────────────────────────────────────────────────────────── # Pinned. An engine version bump changes rendering, and this project compares # screenshots against a reference renderer — so an upgrade must be a deliberate, # stated act rather than a silent drift. ARG GODOT_VERSION=4.7.2 RUN cd /tmp \ && curl -fsSLO "https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}-stable/Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip" \ && unzip -q "Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip" \ && mv "Godot_v${GODOT_VERSION}-stable_linux.x86_64" /usr/local/bin/godot \ && chmod +x /usr/local/bin/godot \ && printf '#!/bin/sh\nexec /usr/local/bin/godot --headless "$@"\n' > /usr/local/bin/godot-headless \ && chmod +x /usr/local/bin/godot-headless \ && rm -f "Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip" # ── Node + Claude Code ─────────────────────────────────────────────────────── RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ && apt-get install -y --no-install-recommends nodejs \ && npm install -g @anthropic-ai/claude-code \ && npm cache clean --force \ && rm -rf /var/lib/apt/lists/* # ── The agent user ─────────────────────────────────────────────────────────── # NOT root: Claude Code refuses --dangerously-skip-permissions with root # privileges. Ubuntu 24.04 ships its own `ubuntu` account at uid 1000, so the # common case — matching a host user who is also 1000 — collides with it. ARG AGENT_UID=1000 ARG AGENT_GID=1000 RUN if getent passwd "${AGENT_UID}" >/dev/null; then \ userdel -r "$(getent passwd "${AGENT_UID}" | cut -d: -f1)" 2>/dev/null || true; \ fi; \ if getent group "${AGENT_GID}" >/dev/null; then \ groupdel "$(getent group "${AGENT_GID}" | cut -d: -f1)" 2>/dev/null || true; \ fi; \ groupadd -g "${AGENT_GID}" agent \ && useradd -m -u "${AGENT_UID}" -g "${AGENT_GID}" -s /bin/bash -d /sylph-home/port agent \ && mkdir -p /sylph-home/port /work /reborn \ && chown -R "${AGENT_UID}:${AGENT_GID}" /sylph-home \ && echo 'agent ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/agent COPY bin/ /usr/local/bin/ COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/* /usr/local/bin/entrypoint.sh USER agent WORKDIR /work # CARGO_TARGET_DIR points OUTSIDE the bind-mounted repo so the host and the # container do not invalidate each other's incremental state on every switch. ENV RUSTUP_HOME=/sylph-home/port/.rustup \ CARGO_HOME=/sylph-home/port/.cargo \ CARGO_TARGET_DIR=/sylph-home/port/target-container \ PATH=/sylph-home/port/.cargo/bin:/usr/local/bin:/usr/bin:/bin RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ | sh -s -- -y --default-toolchain stable --profile minimal --component clippy --component rustfmt RUN mkdir -p /sylph-home/port/target-container /sylph-home/port/.claude ENV HOME=/sylph-home/port \ DISPLAY=:97 \ SCREEN_GEOMETRY=1280x720x24 \ PROJECT_DIR=/work ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"] CMD ["bash"]