tts-voice-creator-clone-and.../docker-compose.yml
mARTin-B78 e81fa64448 fix: magazine state persistence, misfiled in book library, confidence badge layout, docker-socket-proxy crash loop
Magazine "forgets" the open document (real bug, confirmed live):
- A full page reload or the browser's own Back button tears down all
  in-memory JS state; magInit() always defaulted to the empty library
  view with no way back to what was open. Persists the last-opened
  magazine id in localStorage and auto-reopens it on load, falling back
  to the library only if that document no longer exists.
- The actual root cause of the reported duplicate: readerRenderLibrary()
  (Studio/Reader "Source") and libraryRenderBooks() (Library > Books)
  both listed EVERY document from the shared reader_library store with
  no kind filter, so a magazine (kind: "magazine") was already showing
  up there disguised as a plain book. The magazine never actually
  vanished -- it was misfiled in a different list, which is what led to
  the user re-uploading it and getting a real duplicate under a
  different kind. Both listings now exclude kind: "magazine".

Confidence badge layout (Studio Characters review):
- The badge (radiobox is already positioned after the speaker name in
  DOM order) rendered on its own line far below the name because both
  .ab-cv-spk and .ab-cv-txt were `display: block`, forcing a line break
  regardless of markup order. Wrapped the name + badge + check button in
  a shared flex row (.ab-cv-spk-row) so the badge now sits directly
  beside the name/after "NARRATOR" as requested, without needing to
  touch the two existing incremental-patch code paths (they insert
  relative to the same elements, now just nested one level deeper).

docker-socket-proxy crash loop (found while restarting for the magazine
Python changes to take effect -- unrelated to this session's earlier
work, landed in an earlier commit during the interruption):
- read_only: true left the entrypoint with nowhere to write its own
  haproxy.cfg ("Read-only file system"), crash-looping the container and
  leaving the app without Docker access. Removed read_only for this one
  service -- cap_drop: ALL, no-new-privileges, and the :ro socket mount
  already provide the real hardening.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 09:39:20 +02:00

96 lines
3.2 KiB
YAML

services:
# The web process talks to this narrowly configured proxy over the Compose
# network; the host Docker socket is never mounted into the web container.
docker-socket-proxy:
image: tecnativa/docker-socket-proxy:0.3.0
container_name: tts-voice-creator-docker-proxy
restart: unless-stopped
# NOT read_only: the entrypoint sed's its own haproxy.cfg.template into
# haproxy.cfg at startup, in the same directory the template ships in --
# read_only crash-looped it ("Read-only file system"), and a tmpfs mount
# over that directory only trades one failure for another by hiding the
# template it needs to read (confirmed live: "No such file or
# directory"). The real hardening here is cap_drop: ALL,
# no-new-privileges, and the Docker socket itself being mounted :ro.
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
environment:
CONTAINERS: "1"
POST: "1"
IMAGES: "0"
NETWORKS: "0"
VOLUMES: "0"
SERVICES: "0"
TASKS: "0"
SWARM: "0"
SYSTEM: "0"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- dgx_net
tts-voice-creator-clone-and-design-2:
build:
context: .
dockerfile: Dockerfile
image: ghcr.io/martin-b78/tts-voice-creator-clone-and-design-2:${APP_TAG:-latest}
container_name: tts-voice-creator-clone-and-design-2
restart: unless-stopped
# The application writes only to the explicitly mounted data paths below.
# Keep the image filesystem immutable even if an endpoint is compromised.
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:size=1g,mode=1777
# Keep a runaway OCR/import/generation request from consuming all host RAM.
# Override these per machine when large PDF jobs need more headroom:
# APP_MEM_LIMIT=12g APP_MEM_RESERVATION=2g docker compose up -d
mem_limit: "${APP_MEM_LIMIT:-8g}"
mem_reservation: "${APP_MEM_RESERVATION:-2g}"
ports:
- "7890:7890"
depends_on:
- docker-socket-proxy
volumes:
- ${VOICE_HOST_DIR:-/home/sparky/Media/_Sounds/TTS_Voices}:/voices:rw
- ./config:/home/app/.config/tts-voice-creator:rw # settings, presets, routes
- ./logs:/logs:rw # application logs
- /home/sparky/Docker/tts-dgx-spark-faster-qwen3-tts/config:/tts-config:rw
- ./static:/app/static:ro
- ./server.py:/app/server.py:ro
- ./core:/app/core:ro
- ./routes:/app/routes:ro
- ./workflows:/app/workflows:ro
- ./VERSION:/app/VERSION:ro
- ./CHANGELOG.md:/app/CHANGELOG.md:ro
environment:
- PYTHONUNBUFFERED=1
- VOICES_DIR=/voices
- OUTPUT_DIR=/voices/active_voices
# Containers that load WAV files and need a restart after voice changes
- TTS_CONTAINER_NAME=tts-faster-qwen3-tts-voiceclone
- TTS_CONTAINER_NAMES=tts-faster-qwen3-tts-voiceclone,tts-faster-qwen3-tts-streaming
- VOICE_DESIGN_MODEL=Qwen3-TTS-12Hz-1.7B-VoiceDesign
- DOCKER_HOST=tcp://docker-socket-proxy:2375
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
- dgx_net
networks:
dgx_net:
external: true