Operator-directed. The wg0 lifeline retired at the 2026-09-06 headscale cutover is on no interface anywhere, so anything pointing at it gets no route at all. Homepage went from 9 dead cards to 0 of 112. The load-bearing part is that there is no single right target: it depends on who resolves it. The operator's browser and the Homepage and open-webui containers on esh-docker-vm all resolve nh3.internal, so those get the name and survive the next renumber. Containers on irv-ml1 and ana-docker cannot resolve it at all, so those get the IP. litellm on ana-docker looked like a counterexample and is not: it resolves the name only through its own extra_hosts entry, while asset-engine on the same host fails on it. Test from the container you are about to change, never from a neighbour. Before committing to the name I confirmed the Homepage container actually fetches ytvc's healthz through it in production rather than assuming resolution implies reach. On irv-ml1, 24 files swept and 14 comment-only hits left as port-allocation history. Seven running containers recreated so the labels took. Seven dormant ones carried stale labels because editing a compose file does not touch an existing container object - fixed with compose create --force-recreate, which rebuilds the container without starting it, the right tool for a deliberately dormant stack. The sweep's real find was off irv-ml1 entirely: four live values on two other hosts, silently dead for nine days and alerting nobody. Open WebUI's read-aloud TTS, asset-engine's inference host, and two skaldsong TTS URLs. Both running services were recreated and verified reaching their targets afterwards rather than merely carrying the new string. One self-inflicted outage worth recording: I recreated breeze-tts for a cosmetic label change and took ext-tts down for its ~90s CUDA-graph warm-up, returning 500. I caught it only because I had taken a baseline before touching it. A label-only edit still costs a full model reload on a GPU container.
65 lines
3.1 KiB
Bash
65 lines
3.1 KiB
Bash
# skaldsong stack tunables. Copy to `.env` on ana-docker before deploying.
|
|
#
|
|
# The deploy playbook seeds `.env` from this template on first run only —
|
|
# it won't clobber an existing `.env`.
|
|
|
|
# Image tag. CI's deploy.yaml pushes this SHA-pinned per commit:
|
|
# gitea.phasefinal.com/vh/skaldsong:<sha>
|
|
# Workflow passes the SHA via `scripts/elway ... --var ref=<sha>`; the
|
|
# playbook substitutes that into this var before `docker compose up -d`.
|
|
# For manual runs, set to a known good SHA (e.g. after a green CI run):
|
|
SKALDSONG_IMAGE=gitea.phasefinal.com/vh/skaldsong:latest
|
|
|
|
# Host port exposing the wizard UI + Bifrost endpoint. Container always
|
|
# listens on 8000 internally. 8300 is the canonical slot — adjacent to
|
|
# the 8xxx web-app block (asset-engine:8200, sillytavern:8100, beszel:8090).
|
|
SKALDSONG_PORT=8300
|
|
|
|
# Bind address for the host port. 0.0.0.0 = LAN-reachable.
|
|
SKALDSONG_BIND=0.0.0.0
|
|
|
|
# Host paths for persistent state. db/ holds skaldsong-ui.db (SQLite,
|
|
# wizard state + story rows); runs/ holds per-story generation pipeline
|
|
# artifacts (spec, state, output, logs, manuscripts). Created with uid
|
|
# 1000 (matches container's app user — no chown dance needed).
|
|
SKALDSONG_DB_DIR=/opt/docker/conf/skaldsong/db
|
|
SKALDSONG_RUNS_DIR_HOST=/opt/docker/conf/skaldsong/runs
|
|
|
|
# Worldtree integration ─────────────────────────────────────────────────
|
|
|
|
# Shared-secret Bearer for Worldtree's HS256 Bifrost JWT path. Same
|
|
# value as Worldtree's WORLDTREE_SKALDSONG_USER_KEY env. Get from
|
|
# worldtree-dev when standing up a fresh deploy; rotate via worldtree-dev's
|
|
# /heimdall:rotate flow if compromise suspected.
|
|
SKALDSONG_BIFROST_JWT_KEY=
|
|
|
|
# URL Worldtree uses to call BACK to skaldsong's Bifrost endpoint at
|
|
# tool-dispatch time. Must match Worldtree's BIFROST_CLIENT_ALLOWED_HOSTS
|
|
# entry. From ana-docker LAN: http://10.250.50.70:8300 (this port).
|
|
# Coordinated update with worldtree-dev required if host:port changes.
|
|
SKALDSONG_HOST_BIFROST_ENDPOINT_URL=http://10.250.50.70:8300
|
|
|
|
# Worldtree API client — outbound. Same Bearer as
|
|
# SKALDSONG_BIFROST_JWT_KEY (different code path consumes it).
|
|
WORLDTREE_TOKEN=
|
|
WORLDTREE_BASE_URL=http://10.250.50.152:8080
|
|
|
|
# Wizard agent ID on Worldtree. MUST be pinned to the existing slot
|
|
# (skaldsong:wizard-v2) — blank would POST /agents/define and burn a
|
|
# slot of the 50-agent-per-key quota. Heimdall scopes agent_id to
|
|
# user_id, so this matches the agent defined from skaldsong-dev's
|
|
# prior nh3-dev hand-launch.
|
|
SKALDSONG_HOST_WIZARD_AGENT_ID=skaldsong:wizard-v2
|
|
|
|
# CORS — JSON array of origins. Pydantic-settings parses complex-typed
|
|
# env vars via json.loads(), so the value MUST be a JSON array literal
|
|
# (a bare URL string fails first-boot with `SettingsError: error
|
|
# parsing value for field "cors_origins"`). Include the SPA's public
|
|
# hostname AND any dev origins still in rotation.
|
|
SKALDSONG_HOST_CORS_ORIGINS=["http://10.250.50.70:8300"]
|
|
|
|
# TTS — Kokoro on irv-ml1, reached over WireGuard via ana-wg. Same URL
|
|
# works fleet-wide; the WG tunnel handles the cross-site routing.
|
|
SKALDSONG_TTS_ENGINE=kokoro
|
|
SKALDSONG_TTS_BASE_URL=http://10.6.110.50:8193
|