docker-compose.yml
Rama master · réplica de lectura del repositorio original.
services:
api:
build:
context: .
dockerfile: api/Dockerfile
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
env_file:
- .env
environment:
- TZ=America/New_York
volumes:
# Mount the whole api/ source so uvicorn --reload picks up .py edits live —
# no docker cp, no image drift. Nested mounts below layer on top.
- ./api:/app
- ./api/data:/app/data
- ./api/templates:/app/templates
- ./web:/app/static
# App submodules (spire, noodle): api/app_paths.py puts apps/*/api on
# sys.path; their web/ joins the "/" static mount, their templates _tmpl.
- ./apps:/app/apps
- ./graphify-out:/app/graphify-out:ro
- ./mtg/data:/app/mtg/data
- ./nightfall-incident:/app/nightfall
# KEPT ON PURPOSE though the rmapi binary is gone (2026-09-17). This volume
# holds a real authenticated reMarkable device token (`rmapi.conf`, last
# written 2026-04-28) -- re-pairing a device is a manual, physical act, so
# the token is not something to throw away to tidy a compose file. Left
# MOUNTED rather than merely declared, because a declared-but-unmounted
# compose volume counts as unused and `docker volume prune` would eat it.
# Costs ~1.5KB. Restore the feature by putting the builder stage back.
- rmapi-auth:/root/.config/rmapi
- gcal-auth:/root/.config/gcal
# nightfall-src/ is the GAME'S SOURCE tree (354MB, 29k entries incl. node_modules).
# Nothing at runtime reads it -- only the BUILT output is served (index.html,
# static/, wai-*.js, audio/), and webpack runs on the HOST, never in here. But it
# rode along inside the ./nightfall-incident bind mount, and uvicorn --reload
# rglob()s the whole /app tree looking for *.py, so those 29k entries were 85% of
# a walk that ran 4x a second: 0.21s per pass on a 0.25s interval = 44% of a core,
# permanently, for 8 days straight. An empty tmpfs over that one path masks it out
# of the container (34,187 entries walked -> 5,177) and costs 0 bytes, since
# nothing ever writes there. Docker mounts shallowest-first, so this lands ON TOP
# of the ./nightfall-incident bind above it; the host copy is untouched.
tmpfs:
- /app/nightfall/nightfall-src
# The docker log had no size limit and lived as long as the container. It is
# capped (3 x 10MB, oldest dropped) -- the access lines in it carried noodle
# slugs and draft tokens until api/log_masking.py (security audit 2026-10-14).
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# Always-on GLaDOS voice (CPU Piper). Built separately from the hosaka repo:
# docker build -f ~/src/hosaka/Dockerfile.piper -t hosaka-piper:latest ~/src/hosaka
# No torch, glados baked into the image. Keeps /hosaka's glados live even when
# the home GPU box is off. The api container reaches it by service name over
# the compose network (TTS_PIPER_UPSTREAM defaults to hosaka-piper:8123); no
# ports are published -- only api talks to it.
hosaka-piper:
image: hosaka-piper:latest
restart: unless-stopped
volumes:
# Pronunciation lexicon synced from the home box (scripts/sync_lexicon.sh,
# rsync --inplace). Single-file bind mount + hosaka's Lexicon mtime
# hot-reload => live pronunciation updates with no restart. The host file
# must exist before the container starts (else docker creates a dir);
# seed it with the first sync. Read-only: the container only reads it.
- /srv/hosaka-piper/lexicon.json:/opt/hosaka/.local/share/hosaka/lexicon.json:ro
volumes:
rmapi-auth:
gcal-auth: