From ec780b2e73e6ad6198bff2c06d839610c8282620 Mon Sep 17 00:00:00 2001 From: Quince Date: Wed, 10 Jun 2026 10:42:46 +0200 Subject: [PATCH] Add prod deployment for the agent fleet (docker-compose.prod.yml) - docker-compose.prod.yml + .env.prod.example: one container per agent, Quince first; deployed under /opt/gu_agents and wired into the server's start/stop.sh. - Route API + git via host-gateway (containers can't hairpin the host's public IP). - Dockerfile: drop the base image's uid-1000 user before creating quince. - entrypoint: pip install --break-system-packages (Debian bookworm PEP 668). Verified on prod: image builds, API reachable (200), PyPI egress ok, gutask installs and runs, git clone from ramanujan works via host-gateway. Co-Authored-By: Claude Opus 4.8 --- deploy/.env.prod.example | 29 ++++++++++++++++++ deploy/Dockerfile | 5 +++- deploy/docker-compose.prod.yml | 55 ++++++++++++++++++++++++++++++++++ deploy/entrypoint.sh | 4 ++- 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 deploy/.env.prod.example create mode 100644 deploy/docker-compose.prod.yml diff --git a/deploy/.env.prod.example b/deploy/.env.prod.example new file mode 100644 index 0000000..59a1959 --- /dev/null +++ b/deploy/.env.prod.example @@ -0,0 +1,29 @@ +# Glitch University agents — production env (passed via --env-file). +# Real secrets live ONLY on the server at /opt/gu_agents/.env.prod (never committed). +# This is the committed template. + +# ── Claude Code auth (REQUIRED) ───────────────────────────────────────────── +# One key can power the whole agent fleet. Create at https://console.anthropic.com/ +ANTHROPIC_API_KEY= +# Optional model pin, e.g. claude-opus-4-8 +CLAUDE_MODEL= + +# ── Glitch Content API (gutask) ───────────────────────────────────────────── +API_URL=https://glitch.university +CONTENT_API_KEY= + +# ── Scheduling / runtime (shared) ─────────────────────────────────────────── +TZ=Etc/UTC +WAKE_TIME=09:00 +RUN_ON_START=0 +QUINCE_UID=1000 + +# ── Quince (agent #9) ─────────────────────────────────────────────────────── +QUINCE_AGENT_ID=9 +QUINCE_AGENT_NAME=Quince +QUINCE_AGENT_PASSWORD= + +# ── Add more agents below, e.g.: ──────────────────────────────────────────── +# GUNNAR_AGENT_ID=3 +# GUNNAR_AGENT_NAME=Gunnar +# GUNNAR_AGENT_PASSWORD= diff --git a/deploy/Dockerfile b/deploy/Dockerfile index f4275a4..1257d24 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -18,7 +18,10 @@ RUN npm install -g @anthropic-ai/claude-code # Non-root user. Claude Code refuses --dangerously-skip-permissions as root, # and we want the volume owned by a stable uid the agent can write to. ARG QUINCE_UID=1000 -RUN useradd --create-home --uid ${QUINCE_UID} --shell /bin/bash quince +# The node base image already has a user at uid 1000; drop it, then create quince. +RUN existing="$(getent passwd ${QUINCE_UID} | cut -d: -f1)"; \ + if [ -n "$existing" ] && [ "$existing" != "quince" ]; then userdel -r "$existing" 2>/dev/null || true; fi; \ + useradd --create-home --uid ${QUINCE_UID} --shell /bin/bash quince # Entrypoint (scheduler loop) + wake script (one awakening). COPY entrypoint.sh /usr/local/bin/entrypoint.sh diff --git a/deploy/docker-compose.prod.yml b/deploy/docker-compose.prod.yml new file mode 100644 index 0000000..265cf1b --- /dev/null +++ b/deploy/docker-compose.prod.yml @@ -0,0 +1,55 @@ +# Glitch University agents — Production +# +# One container per agent. Quince (#9) is the first; add more as sibling +# services (each with its own build context dir + QUINCE_*-style identity vars). +# +# Deployed under /opt/gu_agents/ and started from /root/start.sh: +# docker compose -f /opt/gu_agents/docker-compose.prod.yml \ +# --env-file /opt/gu_agents/.env.prod up -d +# +# Requires gu_common to be up (nginx → the API at glitch.university, and +# gnommo-gitea → ramanujan.glitch.university:2222) and the external `gnommo` +# network to exist (start.sh creates it). + +services: + quince: + build: + context: ./quince # build inputs only: Dockerfile, scripts, identity/ + dockerfile: Dockerfile + args: + QUINCE_UID: ${QUINCE_UID:-1000} + image: gnommo-agent-quince:latest + container_name: gnommo-agent-quince + restart: unless-stopped + environment: + # Claude Code auth (unattended) + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} + CLAUDE_MODEL: ${CLAUDE_MODEL:-} + # Glitch Content API (gutask) + API_URL: ${API_URL} + CONTENT_API_KEY: ${CONTENT_API_KEY} + # Quince's identity (gutask reads these as process env) + AGENT_ID: ${QUINCE_AGENT_ID} + AGENT_NAME: ${QUINCE_AGENT_NAME} + AGENT_PASSWORD: ${QUINCE_AGENT_PASSWORD} + # Scheduling + TZ: ${TZ:-Etc/UTC} + WAKE_TIME: ${WAKE_TIME:-09:00} + RUN_ON_START: ${RUN_ON_START:-0} + volumes: + # Runtime self (kept OUTSIDE the build context so the SSH key is never + # sent to the build daemon): .ssh, notes/, workspace/, logs/, .claude/. + - ./quince-home:/home/quince + extra_hosts: + # Reach the API and git via the host's published ports rather than + # hairpinning to the public IP. + - "glitch.university:host-gateway" + - "ramanujan.glitch.university:host-gateway" + networks: + - default + - gnommo + +networks: + default: {} + gnommo: + external: true diff --git a/deploy/entrypoint.sh b/deploy/entrypoint.sh index 3d2f81f..7ca164d 100755 --- a/deploy/entrypoint.sh +++ b/deploy/entrypoint.sh @@ -43,7 +43,9 @@ if [ ! -d "$GUTASK_DIR/.git" ]; then fi if [ -d "$GUTASK_DIR" ]; then log "Installing gutasktool (editable)…" - python3 -m pip install --user -e "$GUTASK_DIR" -q 2>/dev/null || \ + # --break-system-packages: the Debian base marks its Python externally-managed + # (PEP 668); this is an isolated container, so installing to --user is fine. + python3 -m pip install --user --break-system-packages -e "$GUTASK_DIR" -q || \ log "WARNING: gutasktool install failed." fi