feat(arbo): add irv-ml1 co-located engine stack (ADR-0001)
New stack mirroring the canonical convention for the Arbo (catalog) engine, co-located beside comfyui on irv-ml1 per ADR-0001 D1/D3: - engine<->ComfyUI over traefik-net container DNS (http://comfyui:8188), SSH dependency eliminated; file ops bind the shared basedir input/output - named local-disk volumes for the gallery SQLite (arbo_db, restic-backed) and hero images (arbo_heroes); catalog as a ro git-checkout mount (D2) - ENGINE_TOKEN + GRANITE_KEY via on-host .env; GRANITE via the LiteLLM gateway - Q5 catalog-pull: manual day-1, ytvc-style webhook follow-on Image build, /healthz, catalog in-container path, and non-root UID are comfy-dev's to confirm (CONFIRM items in README).
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# Arbo engine — real .env lives on irv-ml1 (gitignored), this is the template.
|
||||
# Copy to .env on the host and fill the secrets. See README.md for provisioning.
|
||||
|
||||
# Image comfy-dev builds + pushes (pin a tag; bump on code/schema change only):
|
||||
ARBO_IMAGE=gitea.phasefinal.com/vh/arbo:0.11.0
|
||||
|
||||
# Published port on irv-ml1 (reachable over WireGuard at 10.100.79.3:<port>):
|
||||
ARBO_PORT=8200
|
||||
ARBO_BIND=0.0.0.0
|
||||
|
||||
# Run-as ownership — must match /worktank owner so basedir writes land clean:
|
||||
ARBO_UID=1000
|
||||
ARBO_GID=1000
|
||||
|
||||
# Host path of the comfy-dev catalog checkout (the git pull target, mounted ro):
|
||||
ARBO_CATALOG_DIR=/worktank/arbo/repo
|
||||
|
||||
# ── Secrets (DO NOT COMMIT REAL VALUES) ──────────────────────────────
|
||||
# ENGINE_TOKEN: a real bearer token — auth is OPEN until this is set.
|
||||
# Mint a fresh one: openssl rand -hex 32
|
||||
ENGINE_TOKEN=
|
||||
|
||||
# GRANITE_KEY: the LiteLLM virtual key scoped to arbo (granite-only).
|
||||
# comfy-dev already holds 'arbo-prompt-enhance' (issued 2026-06-09) —
|
||||
# reuse or rotate it; do NOT use the master sk-corvid key.
|
||||
GRANITE_KEY=
|
||||
@@ -0,0 +1,72 @@
|
||||
# arbo — catalog-driven ComfyUI engine
|
||||
|
||||
Arbo's `comfy_engine` (FastAPI) reads a versioned workflow catalog and drives
|
||||
**ComfyUI** to generate images/video, serving an SPA + `/workflows` + `/run`.
|
||||
Owned by **comfy-dev** (engine code + catalog + image build); this stack is the
|
||||
**infra side** — compose, deploy, secrets, volumes, pull automation. See
|
||||
`vh/arbo` `docs/adr/0001-engine-production-deployment.md` for the architecture.
|
||||
|
||||
## Placement (ADR-0001)
|
||||
|
||||
- **Host:** irv-ml1, **co-located beside the `comfyui` stack** (D1/D3).
|
||||
- **engine ↔ ComfyUI:** `http://comfyui:8188` over the shared `traefik-net`
|
||||
(the "localhost-equivalent" — no scp, no WireGuard hop). File ops use the
|
||||
**same host dirs** comfyui mounts (`/worktank/comfyui/basedir/{input,output}`),
|
||||
bound into the engine — so `ARBO_COMFY_SSH_HOST` stays **unset** and the
|
||||
engine's local-cp path (comfy-dev's D3 follow-on) handles file ops.
|
||||
|
||||
## Deploy
|
||||
|
||||
```bash
|
||||
# 1. comfy-dev builds + pushes the image; pin the tag in .env (ARBO_IMAGE).
|
||||
# 2. On irv-ml1, clone the catalog checkout (the git-pull target, D2):
|
||||
# git clone git@gitea.phasefinal.com:vh/arbo.git /worktank/arbo/repo
|
||||
# 3. Create stacks/arbo/.env from .env.example on the host, fill secrets.
|
||||
# 4. From this workspace:
|
||||
scripts/deploy-stack.sh irv-ml1 arbo # diffs vs live, prompts y/N
|
||||
```
|
||||
|
||||
## Q3 — Secrets
|
||||
|
||||
Both injected via the on-host **`.env`** (gitignored; never the althing bus),
|
||||
the standard fleet pattern:
|
||||
|
||||
- **`ENGINE_TOKEN`** — auth is **OPEN today**. Mint a real bearer
|
||||
(`openssl rand -hex 32`), put it in `.env`. Closing this is the one security
|
||||
fix the move buys us.
|
||||
- **`GRANITE_KEY`** — the LiteLLM **virtual key scoped to arbo** (granite-only).
|
||||
comfy-dev already holds `arbo-prompt-enhance` (issued 2026-06-09); reuse or
|
||||
rotate it. Never the master `sk-corvid`.
|
||||
|
||||
## Q4 — Volumes + backup
|
||||
|
||||
| Volume | Holds | Durability |
|
||||
|---|---|---|
|
||||
| `arbo_db` (named, local disk) | gallery/history SQLite (`ARBO_DB`) | **backed up** — restic/Backrest daily |
|
||||
| `arbo_heroes` (named, local disk) | runtime hero images | persisted; regenerable, lower backup priority |
|
||||
| `/worktank/arbo/repo` (bind, ro) | catalog checkout | reproducible from git — not backed up |
|
||||
|
||||
`ARBO_DB` is a **named volume on local disk, NOT NFS** (the DB-off-NFS rule;
|
||||
SQLite-on-NFS locking is a foot-gun). The gallery DB is the durable asset → it
|
||||
joins the restic file backup. (Adds irv-ml1's `arbo_db` path to a Backrest
|
||||
repo — small; flag if irv-ml1 isn't yet a restic source.)
|
||||
|
||||
## Q5 — Catalog-pull automation
|
||||
|
||||
- **Day 1:** manual `git pull` in `/worktank/arbo/repo` + `deploy-stack.sh
|
||||
irv-ml1 arbo` restart (or `docker compose restart arbo`).
|
||||
- **Follow-on (recommended):** mirror the **yt-voice-clipper webhook** already
|
||||
live on irv-ml1 — gitea webhook → HMAC listener → `git pull` + `compose
|
||||
restart`, so a comfy-dev catalog push reaches prod in one action (D2's "cheap
|
||||
to reach prod"). Layered after the stack is up.
|
||||
|
||||
## Items needing comfy-dev's image (jointly owned)
|
||||
|
||||
1. **`ARBO_IMAGE` tag** — the built `gitea.phasefinal.com/vh/arbo:<tag>`.
|
||||
2. **`/healthz`** — the healthcheck assumes it; comfy-dev is adding it.
|
||||
3. **Catalog in-container path** — compose mounts the checkout's `catalog/` +
|
||||
`graphs/` to `/app/catalog` + `/app/graphs` (ro). Confirm against the image's
|
||||
expected layout (or expose a catalog-dir env), so the engine reloads from the
|
||||
mount on restart.
|
||||
4. **Run-as UID** — container runs `1000:1000` to write comfyui's basedir +
|
||||
volumes cleanly; confirm the image tolerates a non-root user.
|
||||
@@ -0,0 +1,86 @@
|
||||
# Arbo (catalog) engine — FastAPI sidecar that drives ComfyUI from a
|
||||
# versioned workflow catalog. Serves the SPA + /workflows + /run.
|
||||
#
|
||||
# Co-located on irv-ml1 BESIDE the comfyui stack (ADR-0001 / D3). This
|
||||
# collapses the engine<->ComfyUI link to the shared traefik-net + the
|
||||
# local filesystem and ELIMINATES the old SSH/WireGuard dependency:
|
||||
# - generation : COMFYUI_URL=http://comfyui:8188 (container DNS on
|
||||
# traefik-net — the "localhost-equivalent"; no scp, no WG hop)
|
||||
# - file ops : ARBO_COMFY_INPUT_DIR / ARBO_COMFY_OUTPUT_DIR bind the
|
||||
# SAME host dirs comfyui mounts (/worktank/comfyui/basedir/{input,
|
||||
# output}), so to-input / upload-input / artifact-wipe are local fs
|
||||
# ops. ARBO_COMFY_SSH_HOST is left UNSET on purpose → the engine's
|
||||
# local-cp/shutil path (comfy-dev's D3 follow-on) takes over.
|
||||
#
|
||||
# State that MUST survive restarts (named volumes, local disk — NOT NFS,
|
||||
# per the DB-off-NFS rule; SQLite-on-NFS locking is a known foot-gun):
|
||||
# - arbo_db -> the gallery/history SQLite (ARBO_DB)
|
||||
# - arbo_heroes -> runtime hero images (ARBO_HEROES_DIR)
|
||||
# The catalog is a comfy-dev git checkout bind-mounted read-only (D2):
|
||||
# a workflow update = `git pull` on the host + restart, no image rebuild.
|
||||
#
|
||||
# IMAGE + CATALOG TARGET are owned by comfy-dev (they build the image +
|
||||
# add /healthz). Two items marked CONFIRM below need their image layout.
|
||||
# All tunables live in .env — edit that, not this file.
|
||||
|
||||
services:
|
||||
engine:
|
||||
image: ${ARBO_IMAGE} # CONFIRM: gitea.phasefinal.com/vh/arbo:<tag>, comfy-dev builds
|
||||
container_name: arbo
|
||||
restart: unless-stopped
|
||||
# Run as the host owner of /worktank (1000:1000 = lkraven) so writes
|
||||
# into comfyui's basedir/input + the named volumes land with the
|
||||
# right ownership (matches the comfyui stack's WANTED_UID posture).
|
||||
user: "${ARBO_UID:-1000}:${ARBO_GID:-1000}"
|
||||
ports:
|
||||
- "${ARBO_BIND:-0.0.0.0}:${ARBO_PORT:-8200}:8200"
|
||||
env_file: .env
|
||||
environment:
|
||||
- ARBO_HOST=0.0.0.0
|
||||
- ARBO_PORT=8200
|
||||
- ARBO_DB=/data/gallery.sqlite
|
||||
- ARBO_HEROES_DIR=/heroes
|
||||
# engine<->ComfyUI over the shared network (no SSH):
|
||||
- COMFYUI_URL=http://comfyui:8188
|
||||
- ARBO_COMFY_INPUT_DIR=/comfy/input
|
||||
- ARBO_COMFY_OUTPUT_DIR=/comfy/output
|
||||
# ARBO_COMFY_SSH_HOST intentionally UNSET -> local-cp path (D3 follow-on)
|
||||
# LiteLLM gateway for the granite hero/metadata calls:
|
||||
- GRANITE_ENDPOINT=http://10.250.50.70:4000/v1
|
||||
- GRANITE_KEY=${GRANITE_KEY} # arbo-prompt-enhance scoped vkey (.env)
|
||||
- ENGINE_TOKEN=${ENGINE_TOKEN} # real bearer — closes today's open auth (.env)
|
||||
volumes:
|
||||
- arbo_db:/data
|
||||
- arbo_heroes:/heroes
|
||||
# comfyui's input/output, shared on the host (rw: upload-input writes
|
||||
# input, artifact-wipe deletes output):
|
||||
- /worktank/comfyui/basedir/input:/comfy/input
|
||||
- /worktank/comfyui/basedir/output:/comfy/output
|
||||
# catalog checkout (comfy-dev clone on the host), read-only:
|
||||
- ${ARBO_CATALOG_DIR:-/worktank/arbo/repo}/catalog:/app/catalog:ro # CONFIRM in-container path vs image layout
|
||||
- ${ARBO_CATALOG_DIR:-/worktank/arbo/repo}/graphs:/app/graphs:ro # CONFIRM in-container path vs image layout
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://localhost:8200/healthz >/dev/null || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
networks:
|
||||
- tnet
|
||||
labels:
|
||||
- homepage.group=AI Systems
|
||||
- homepage.name=Arbo
|
||||
- homepage.icon=mdi-image-multiple-outline
|
||||
- homepage.description=Catalog-driven ComfyUI engine (irv-ml1)
|
||||
- homepage.href=http://10.100.79.3:${ARBO_PORT:-8200}
|
||||
|
||||
volumes:
|
||||
arbo_db:
|
||||
name: arbo_db
|
||||
arbo_heroes:
|
||||
name: arbo_heroes
|
||||
|
||||
networks:
|
||||
tnet:
|
||||
name: traefik-net
|
||||
external: true
|
||||
Reference in New Issue
Block a user