docker-compose.demo.yml
Rama main · réplica de lectura del repositorio original.
# soc-ai PUBLIC DEMO stack — one container, read-only replay. NOT for real use.
#
# This is the local twin of the hosted demo (see render.yaml): the same image
# as docker-compose.yml, but started through docker/demo-entrypoint.sh, which
# runs the bundled mock Elasticsearch/LLM endpoint on 127.0.0.1:9200 (inside
# the container, never published) and then the app with SOC_AI_DEMO=true.
# The app seeds the sanitized soc_ai/demo/fixtures.json at startup (fail-soft:
# absent fixtures boot an empty demo), blocks every mutating request except
# the two replay triggers, and refuses all non-loopback egress.
#
# TLS: NONE in this container, deliberately. The hosted demo sits behind the
# platform edge (which terminates TLS and forwards plain HTTP), so the demo
# entrypoint starts uvicorn WITHOUT the --ssl-* flags the production compose
# file relies on. Locally that means plain http://127.0.0.1:8080 — bound to
# loopback by default, and it must stay that way (auth is pinned off below).
#
# Usage:
# docker compose -f docker-compose.demo.yml up --build
# → http://127.0.0.1:8080/app/
#
# NO .env, NO secrets: every value below is a loopback URL or an inert
# placeholder that the Settings schema requires to boot. Nothing sensitive
# exists in the demo environment — by spec.
services:
soc-ai-demo:
image: ghcr.io/nuk3s/soc-ai:${SOC_AI_IMAGE_TAG:-latest}
build:
context: .
dockerfile: Dockerfile
container_name: soc-ai-demo
# The demo entrypoint replaces the image's TLS-serving default CMD.
entrypoint: ["/opt/soc-ai/docker/demo-entrypoint.sh"]
# Loopback publish by default — API_AUTH_REQUIRED=false makes this stack
# unauthenticated, so never bind it wider on a box you share. The hosted
# demo gets its public exposure through the platform edge, not this port.
ports:
- "${SOC_AI_DEMO_BIND:-127.0.0.1}:${SOC_AI_DEMO_PORT:-8080}:8080"
# environment (NOT env_file) on purpose: the demo must boot with no .env.
environment:
# Master switch — env-only by design (soc_ai/config.py), so it can never
# be flipped from the UI.
SOC_AI_DEMO: "true"
# CARRIED DECISION: the demo read-only middleware blocks POST /login,
# and SOC_AI_DEMO does NOT force auth off — pin it off here so visitors
# land straight in the (read-only) UI instead of an unusable login form.
API_AUTH_REQUIRED: "false"
# Public-facing flood backpressure (default is 1200 for real deploys).
# The SPA fires ~10-15 API calls per page view, so this must clear a real
# visitor's navigation while still catching bots/scrapers — 30 throttled a
# human to ~2 clicks/min (dogfood finding). Override for trusted/local use:
# SOC_AI_DEMO_RATE_LIMIT=600 podman-compose -f docker-compose.demo.yml up.
API_RATE_LIMIT_PER_MIN: "${SOC_AI_DEMO_RATE_LIMIT:-120}"
# Everything below either points at the in-container mock (the demo
# egress guard refuses any non-loopback client) or is an inert
# placeholder Settings requires. No real endpoint, credential, or key.
ES_HOSTS: "http://127.0.0.1:9200"
SO_HOST: "http://127.0.0.1:9200"
SO_USERNAME: "demo"
SO_PASSWORD: "demo-placeholder-unused"
LITELLM_BASE_URL: "http://127.0.0.1:9200"
# Plain-HTTP healthcheck — overrides the image's HTTPS one, which matches
# the production CMD (TLS on 8443), not the demo entrypoint (HTTP on 8080).
# Two-target: probe the bundled mock ES (:9200) as well as the app, because
# /healthz is pure app liveness and would stay green with a dead mock behind
# it (empty grids). The entrypoint also process-supervises the mock; this
# additionally catches an alive-but-wedged mock.
healthcheck:
test:
- CMD-SHELL
- "curl -fs http://127.0.0.1:9200/ && curl -fs http://127.0.0.1:8080/healthz"
interval: 30s
timeout: 10s
start_period: 60s
retries: 3
restart: unless-stopped
# No volumes: the demo is stateless on purpose. The SQLite store lives in
# the container filesystem and is re-seeded from the packaged fixtures on
# every fresh container — exactly like the hosted free tier (ephemeral FS).
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# Same hardening as the production compose file.
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
# Sized to the hosting target (Render free web service = 512 MB).
deploy:
resources:
limits:
memory: 512M