# GrowQR OpenCode Runtime Image This folder defines the custom per-user OpenCode image used by the Rivet user actor lifecycle. ## Why this exists The upstream image (`ghcr.io/anomalyco/opencode:latest`) is only the base runtime. GrowQR needs an owned image that bakes in: - GrowQR sub-agent markdown modules from `agents/` - GrowQR system prompts from `prompts/` - global OpenCode config under `/root/.config/opencode` - a Git-backed workspace template under `/opt/growqr/workspace-template` - runtime metadata for image/prompt/migration rollout checks ## Local build From `growqr-backend/`: ```bash docker build -f docker/opencode/Dockerfile -t growqr/opencode:dev . ``` Then run the backend with: ```bash OPENCODE_IMAGE=growqr/opencode:dev OPENCODE_IMAGE_VERSION=dev PROMPT_VERSION=dev docker compose up -d --build backend ``` For the current compose setup, the backend talks to the host Docker socket, so local images built on the host are available to per-user containers. ## Release build ```bash VERSION=2026.06.01-1 docker build -f docker/opencode/Dockerfile \ --build-arg GROWQR_IMAGE_VERSION=$VERSION \ --build-arg GROWQR_PROMPT_VERSION=$VERSION \ -t ghcr.io//growqr-opencode:$VERSION \ -t ghcr.io//growqr-opencode:latest . docker push ghcr.io//growqr-opencode:$VERSION docker push ghcr.io//growqr-opencode:latest ``` Then update backend env: ```bash OPENCODE_IMAGE=ghcr.io//growqr-opencode:$VERSION OPENCODE_IMAGE_VERSION=$VERSION PROMPT_VERSION=$VERSION ``` ## Runtime behavior The backend/Rivet actor still owns lifecycle: 1. provision central Gitea repo for the user 2. start one OpenCode container from this image 3. mount host workspace at `/workspace` 4. wait for OpenCode readiness 5. clone/pull the user's Gitea repo into `/workspace` 6. sync important runtime outputs back to Git The image only provides the runtime and templates; user data remains in the user's central Gitea repo.