Public-flip audit: env-driven paths, scrub audit-ticket prefixes, terser README

the host bind paths + LAN host pins replaced with env defaults. Repository URLs
→ git.sulkta.com. Audit-changelog scaffolding stripped from inline comments
(technical reasoning preserved). README sheds marketing scaffolding. AI-speak
in load-bearing prompts/SOULs left alone — that IS the product.
This commit is contained in:
Sulkta 2026-05-27 11:42:58 -07:00
parent 566ca05ea8
commit 564233cbff
20 changed files with 233 additions and 258 deletions

View file

@ -40,19 +40,18 @@ generalise. Examples:
all three at once), preset choice ergonomics, character→tortoise-
voice seed assignments.
When deploying an engine to the host, the build dir at
`/srv/appdata/<engine>/build/` tracks the engine's branch:
To deploy a tuned engine, check out the engine's branch in the build
dir and `docker compose up -d --build`:
```bash
cd /srv/appdata/kokoro/build
git fetch && git checkout engine/kokoro
docker compose -p <name> up -d --build
docker compose up -d --build
```
## GPU coordination (2070 Super)
## GPU coordination
The 8GB card is the bottleneck. F5 + Kokoro can co-reside (~5GB +
~1GB). Tortoise pushes the budget over and needs the GPU largely
to itself — the `engine/tortoise` branch will carry the script
that stops kokoro + f5 before a tortoise run and restarts them
after. Replace with proper coordination once we have more VRAM.
On an 8GB card F5 + Kokoro can co-reside (~5GB + ~1GB). Tortoise
pushes the budget over and needs the GPU largely to itself — the
`engine/tortoise` branch carries a script to stop kokoro + f5
before a Tortoise run and restart them after. Replace with proper
coordination once more VRAM is available.

View file

@ -1,9 +1,6 @@
# Sulkta build of F5-TTS — upstream ghcr.io/swivid/f5-tts:main was
# shipped with torch 2.11/torchaudio 2.4 ABI mismatch on 2026-05-13,
# breaking import torchaudio at boot. We rebuild on a known-good
# pytorch base + pip install f5-tts.
#
# Image tag in localhost-registry: registry.example.local:5000/f5-tts:<ver>
# F5-TTS rebuild on a known-good pytorch base. Upstream
# ghcr.io/swivid/f5-tts:main shipped a torch/torchaudio ABI mismatch
# that broke `import torchaudio` at boot; this image bypasses that.
#
# License: Apache 2.0 (code) / CC-BY-NC (Emilia-trained weights).
# Personal use OK; redistribution gray-area — flagged.
@ -30,12 +27,11 @@ RUN pip install --no-cache-dir 'f5-tts>=1.0.0'
# Pre-warm the HF cache directory.
RUN mkdir -p /cache/hf /audio /voices
COPY f5_server.py /app/f5_server.py
COPY server.py /app/server.py
WORKDIR /app
EXPOSE 7860
# Skald talks to our purpose-built FastAPI server, not Gradio.
# Models load at startup (first request would otherwise pay the
# cold-start cost). uvicorn on :7860 to keep the port stable.
CMD ["uvicorn", "f5_server:app", "--host", "0.0.0.0", "--port", "7860"]
# Purpose-built FastAPI server, not Gradio. Models load at startup
# so the first request doesn't pay the cold-start cost.
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "7860"]

View file

@ -1,19 +1,21 @@
# F5-TTS standalone stack on the host.
# F5-TTS sidecar.
#
# License posture (acknowledged 2026-05-13): code is Apache 2.0, but
# the pretrained model weights are CC-BY-NC (Emilia training data).
# Personal listen is fine; public sharing is a flagged gray area.
# Sulkta's call: ship anyway.
# Code is Apache 2.0; pretrained F5TTS_v1_Base weights are CC-BY-NC
# (Emilia training data). Personal use is fine; redistribution is a
# flagged gray area.
#
# Runtime: 8GB GPU is plenty (F5 inference ~4-6GB peak).
# First run downloads ~2GB of model weights from HuggingFace into
# the hf-cache volume; subsequent runs are warm.
#
# First-run cost: ~2GB model download from HuggingFace into hf-cache,
# happens on first inference request. Subsequent runs are warm.
# Set in .env (or override):
# F5_HOST_PORT=7792
# F5_DATA=./data # ${F5_DATA}/hf-cache + voices + audio
name: f5-tts
services:
f5-tts:
image: registry.example.local:5000/f5-tts:0.3
build: .
image: f5-tts:0.3
container_name: f5-tts
restart: unless-stopped
deploy:
@ -24,20 +26,11 @@ services:
count: all
capabilities: [gpu]
ports:
- "127.0.0.1:7792:7860"
- "127.0.0.1:7792:7860"
- "${F5_HOST_PORT:-7792}:7860"
volumes:
# HF model weights cache — persists ~2GB after first download.
- /srv/appdata/f5-tts/hf-cache:/cache/hf
# Reference voice clips (lj_speech.wav, etc).
- /srv/appdata/f5-tts/voices:/voices:ro
# Rendered audio output — skald writes story narrations here.
- /srv/appdata/f5-tts/audio:/audio
- ${F5_DATA:-./data}/hf-cache:/cache/hf
- ${F5_DATA:-./data}/voices:/voices:ro
- ${F5_DATA:-./data}/audio:/audio
environment:
HF_HOME: /cache/hf
HF_HUB_DISABLE_TELEMETRY: "1"
labels:
org.sulkta.domain: "sulkta"
org.sulkta.owner: "Sulkta"
org.sulkta.managed-by: "compose"
org.sulkta.role: "f5-tts"

View file

@ -1,13 +1,8 @@
# Sulkta build of Kokoro-82M TTS.
# Kokoro-82M TTS. Apache 2.0 code AND weights — clean stack vs
# F5-TTS's CC-BY-NC asterisk.
#
# License: Apache 2.0 (code AND model weights). Clean stack — no
# CC-BY-NC asterisk like F5-TTS's Emilia weights. This is the
# narrator engine for sleep-quality audiobook reads; F5-TTS stays
# around for voice-cloning cases.
#
# Kokoro is small enough to run on CPU but we use the cuda base
# anyway to stay consistent with f5-tts and so it'll pick up the
# GPU when no other tenant has it.
# Kokoro runs fine on CPU but we use the cuda base to stay
# consistent with f5-tts and pick up the GPU when free.
FROM pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime
ENV DEBIAN_FRONTEND=noninteractive \
@ -27,9 +22,9 @@ RUN pip install --no-cache-dir 'kokoro>=0.9.0' 'fastapi>=0.115.0' 'uvicorn>=0.32
RUN mkdir -p /cache/hf /audio
COPY kokoro_server.py /app/kokoro_server.py
COPY server.py /app/server.py
WORKDIR /app
EXPOSE 7860
CMD ["uvicorn", "kokoro_server:app", "--host", "0.0.0.0", "--port", "7860"]
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "7860"]

View file

@ -1,16 +1,18 @@
# Kokoro-82M TTS stack on the host.
# Kokoro-82M TTS sidecar.
#
# Audiobook-quality narrator engine (Apache 2.0 code + weights —
# clean stack vs F5-TTS's CC-BY-NC asterisk). Sibling to f5-tts;
# both share /srv/appdata/f5-tts/audio so skald's audio
# route serves outputs from either engine through the same path.
# Apache 2.0 code AND model weights — clean stack for share/publish.
# Audiobook-quality narrator; F5-TTS stays around for voice-cloning.
#
# License: Apache 2.0 top to bottom. Right for share/publish.
# Set in .env (or override):
# KOKORO_HOST_PORT=7794
# KOKORO_DATA=./data # ${KOKORO_DATA}/hf-cache
# AUDIO_DIR=../f5-tts/data/audio # shared output dir across engines
name: kokoro
services:
kokoro:
image: registry.example.local:5000/kokoro:0.5
build: .
image: kokoro:0.5
container_name: kokoro
restart: unless-stopped
deploy:
@ -21,17 +23,10 @@ services:
count: all
capabilities: [gpu]
ports:
- "127.0.0.1:7794:7860"
- "127.0.0.1:7794:7860"
- "${KOKORO_HOST_PORT:-7794}:7860"
volumes:
- /srv/appdata/kokoro/hf-cache:/cache/hf
# Shared with f5-tts so skald's /audio route covers both.
- /srv/appdata/f5-tts/audio:/audio
- ${KOKORO_DATA:-./data}/hf-cache:/cache/hf
- ${AUDIO_DIR:-./data/audio}:/audio
environment:
HF_HOME: /cache/hf
HF_HUB_DISABLE_TELEMETRY: "1"
labels:
org.sulkta.domain: "sulkta"
org.sulkta.owner: "Sulkta"
org.sulkta.managed-by: "compose"
org.sulkta.role: "kokoro"

View file

@ -1,4 +1,4 @@
"""Kokoro-82M FastAPI server, sibling to f5_server.
"""Kokoro-82M FastAPI server, sibling to the f5-tts server.
Same /synthesize contract as F5 so skald can route between engines
just by which URL it points at. The semantic difference: Kokoro
@ -234,7 +234,7 @@ def _startup() -> None:
@app.get("/healthz")
def healthz() -> dict:
# Shape matches f5_server's so the same Rust HealthResponse
# Shape matches the f5-tts server's so the same Rust HealthResponse
# struct deserializes both: model/vocoder/loaded fields are
# required by skald-core::narrate::HealthResponse.
return {

View file

@ -1,17 +1,13 @@
# Sulkta build of Tortoise-TTS.
# Tortoise-TTS. Apache 2.0 code + weights.
#
# Voice roster (built-in, no cloning needed): angie, daniel, deniro,
# emma, freeman, geralt, halle, jlaw, lj, mol, myself, pat, pat2,
# rainbow, snakes, tim_reynolds, tom, train_atkins, train_dotrice,
# ~26 built-in voices (no cloning): angie, daniel, deniro, emma,
# freeman, geralt, halle, jlaw, lj, mol, myself, pat, pat2, rainbow,
# snakes, tim_reynolds, tom, train_atkins, train_dotrice,
# train_dreams, train_grace, train_kennard, train_lescault,
# train_mouse, weaver, william. ~26 voices baked in.
# train_mouse, weaver, william.
#
# License: Apache 2.0 (code) + Apache 2.0 (model weights). Clean
# stack for share/publish.
#
# Speed: slow. Trade for quality. Standard preset is ~10x slower
# than Kokoro; high_quality is ~30x slower. Worth it for the
# audiobook-quality bar.
# Slow: standard preset is ~10x slower than Kokoro; high_quality is
# ~30x. Trade for quality.
FROM pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime
@ -37,9 +33,9 @@ RUN pip install --no-cache-dir \
RUN mkdir -p /cache/hf /cache/tortoise-models /audio
COPY tortoise_server.py /app/tortoise_server.py
COPY server.py /app/server.py
WORKDIR /app
EXPOSE 7860
CMD ["uvicorn", "tortoise_server:app", "--host", "0.0.0.0", "--port", "7860"]
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "7860"]

View file

@ -1,19 +1,22 @@
# Tortoise-TTS stack on the host. Audiobook-quality engine with 25+
# named voices (no cloning). Apache 2.0 top to bottom.
# Tortoise-TTS sidecar. 25+ named voices, no cloning needed.
# Apache 2.0 top to bottom.
#
# Slow: ~10x kokoro wall clock at 'standard' preset. Worth it for
# the quality bar. Sulkta's call 2026-05-14: "use higgs (now tortoise)
# and we will only let it use the full gpu for runs" — translated:
# runs are batched, slow is acceptable.
# Slow: ~10x kokoro wall-clock at 'standard' preset. Worth it for the
# quality bar; runs are batched.
#
# Co-resides with kokoro on the 2070 Super since tortoise is ~5GB
# and kokoro is ~1GB (8GB total). If OOM hits during a render,
# we'll add a coordination layer to pause kokoro first.
# Co-resides with kokoro on an 8GB card (tortoise ~5GB + kokoro ~1GB).
# OOM during a render: add a coordinator that pauses kokoro first.
#
# Set in .env (or override):
# TORTOISE_HOST_PORT=7795
# TORTOISE_DATA=./data # ${TORTOISE_DATA}/{hf-cache,models}
# AUDIO_DIR=../f5-tts/data/audio # shared output dir across engines
name: tortoise
services:
tortoise:
image: registry.example.local:5000/tortoise:0.1
build: .
image: tortoise:0.1
container_name: tortoise
restart: unless-stopped
deploy:
@ -24,20 +27,12 @@ services:
count: all
capabilities: [gpu]
ports:
- "127.0.0.1:7795:7860"
- "127.0.0.1:7795:7860"
- "${TORTOISE_HOST_PORT:-7795}:7860"
volumes:
- /srv/appdata/tortoise/hf-cache:/cache/hf
- /srv/appdata/tortoise/models:/cache/tortoise-models
# Shared audio dir with f5/kokoro so skald serves all engines'
# outputs through the same /audio route.
- /srv/appdata/f5-tts/audio:/audio
- ${TORTOISE_DATA:-./data}/hf-cache:/cache/hf
- ${TORTOISE_DATA:-./data}/models:/cache/tortoise-models
- ${AUDIO_DIR:-./data/audio}:/audio
environment:
HF_HOME: /cache/hf
HF_HUB_DISABLE_TELEMETRY: "1"
TORTOISE_MODELS_DIR: /cache/tortoise-models
labels:
org.sulkta.domain: "sulkta"
org.sulkta.owner: "Sulkta"
org.sulkta.managed-by: "compose"
org.sulkta.role: "tortoise-tts"

View file

@ -1,4 +1,4 @@
"""Tortoise-TTS FastAPI server. Sibling to kokoro_server.
"""Tortoise-TTS FastAPI server. Sibling to the kokoro server.
Same /synthesize contract as the kokoro server so skald only has to
route by voice.source. Differences:
@ -71,7 +71,7 @@ def _get_voice(name: str) -> tuple:
return _voice_cache[name]
# ─── tag splitter (lifted from kokoro_server) ───────────────────
# ─── tag splitter (lifted from the kokoro server) ───────────────
class Node:
@ -209,7 +209,7 @@ def _startup() -> None:
@app.get("/healthz")
def healthz() -> dict:
# Shape matches f5_server/kokoro_server so skald's HealthResponse
# Shape matches the f5-tts + kokoro servers so skald's HealthResponse
# struct deserializes all three.
return {
"ok": True,