mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-26 11:34:39 +02:00
Some checks are pending
CD Mac Mini / Detect Changes (push) Waiting to run
CD Mac Mini / Deploy (push) Blocked by required conditions
CI / Detect Changes (push) Waiting to run
CI / Validate (push) Waiting to run
CI / Build mana-search (push) Blocked by required conditions
CI / Build mana-sync (push) Blocked by required conditions
CI / Build mana-api-gateway (push) Blocked by required conditions
CI / Build mana-crawler (push) Blocked by required conditions
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
docker-compose.macmini.yml von 37 auf 6 reale App-Services reduziert (landings, searxng, mana-web, manavoxel-web, mana-llm, mana-api). postgres/redis/minio/auth/credits/share/mcp/notify/sync/... + Dubletten raus; depends_on auf Kern-Services entfernt (Kern via Netz erreichbar). mana-api + mana-web credits-URL auf kanonisch 3061. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
323 lines
14 KiB
YAML
323 lines
14 KiB
YAML
# Mana Mac Mini Configuration
|
||
# Domain: mana.how (via Cloudflare Tunnel)
|
||
#
|
||
# Port Schema:
|
||
# 3000-3099: Core Services & Backends
|
||
# 5000-5099: Web Frontends
|
||
# 5100-5199: Games
|
||
# 8000-8099: Monitoring Dashboards
|
||
# 9000-9199: Infrastructure & Exporters
|
||
#
|
||
# Naming Convention: mana-{category}-{service}
|
||
# Categories: infra, core, app, mon, auto
|
||
#
|
||
# Memory Limits:
|
||
# All containers have explicit mem_limit to prevent unbounded growth.
|
||
# Total budget: ~9.8 GiB (fits in 12 GiB Colima VM with ~2 GiB for builds)
|
||
# Run ./scripts/mac-mini/memory-baseline.sh to verify actual usage.
|
||
# Limits are ceilings — actual usage is typically 50-70% of limits.
|
||
|
||
services:
|
||
# ── Verbleibende managarten-Apps (Kern lebt in mana-core, 2026-05-25) ──
|
||
landings:
|
||
image: nginx:alpine
|
||
container_name: mana-infra-landings
|
||
restart: always
|
||
mem_limit: 48m
|
||
volumes:
|
||
- ./docker/nginx:/etc/nginx/host-config:ro
|
||
- /Volumes/ManaData/landings:/srv/landings:ro
|
||
command: >
|
||
sh -c "mkdir -p /etc/nginx/snippets &&
|
||
cp /etc/nginx/host-config/landings.conf /etc/nginx/conf.d/default.conf &&
|
||
cp /etc/nginx/host-config/snippets/* /etc/nginx/snippets/ &&
|
||
nginx -g 'daemon off;'"
|
||
ports:
|
||
- "4400:80"
|
||
healthcheck:
|
||
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1/health"]
|
||
interval: 30s
|
||
timeout: 5s
|
||
retries: 3
|
||
|
||
searxng:
|
||
image: searxng/searxng:latest
|
||
container_name: mana-core-searxng
|
||
restart: always
|
||
mem_limit: 256m
|
||
volumes:
|
||
- ./services/mana-search/searxng:/mnt/searxng-config:ro
|
||
entrypoint: ["sh", "-c", "cp /mnt/searxng-config/settings.yml /etc/searxng/settings.yml && cp /mnt/searxng-config/limiter.toml /etc/searxng/limiter.toml 2>/dev/null; exec /usr/local/searxng/entrypoint.sh"]
|
||
environment:
|
||
SEARXNG_BASE_URL: http://searxng:8080
|
||
SEARXNG_SECRET: ${SEARXNG_SECRET:-change-me-searxng-secret}
|
||
# Internal only - no external port mapping
|
||
healthcheck:
|
||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/healthz"]
|
||
interval: 120s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 15s
|
||
|
||
mana-web:
|
||
build:
|
||
context: .
|
||
dockerfile: apps/mana/apps/web/Dockerfile
|
||
args:
|
||
PUBLIC_SYNC_SERVER_URL: wss://sync.mana.how
|
||
image: mana-web:local
|
||
container_name: mana-app-web
|
||
restart: always
|
||
mem_limit: 256m
|
||
# Kern (mana-auth) lebt in Projekt mana-core, erreichbar übers Netz.
|
||
depends_on:
|
||
mana-api:
|
||
condition: service_healthy
|
||
environment:
|
||
NODE_ENV: production
|
||
PORT: 5000
|
||
PUBLIC_MANA_AUTH_URL: http://mana-auth:3001
|
||
PUBLIC_MANA_AUTH_URL_CLIENT: https://auth.mana.how
|
||
# Auth-Portal-UI (mana-auth-web). In Prod gleiche Origin wie
|
||
# mana-auth API — nginx splittet `/api/*` (mana-auth) von den
|
||
# UI-Routen `/login`, `/register`, `/auth/callback` (mana-auth-web).
|
||
# Client-Code liest die `_CLIENT`-Variante via hooks.server.ts
|
||
# injection (`window.__PUBLIC_AUTH_WEB_URL__`).
|
||
PUBLIC_AUTH_WEB_URL: https://auth.mana.how
|
||
PUBLIC_AUTH_WEB_URL_CLIENT: https://auth.mana.how
|
||
PUBLIC_SYNC_SERVER_URL: http://mana-sync:3010
|
||
PUBLIC_SYNC_SERVER_URL_CLIENT: https://sync.mana.how
|
||
# Unified Hono/Bun API server (apps/api) — hosts all 16 product
|
||
# compute modules (calendar, todo, picture, …) on port 3060.
|
||
# Browser calls go through https://mana-api.mana.how (cloudflared
|
||
# tunnel route to mana-api:3060). SSR calls inside the docker
|
||
# network use the internal hostname.
|
||
#
|
||
# NOTE: api.mana.how is already in use for the Go mana-api-gateway
|
||
# on port 3016 (different service, predates apps/api). The
|
||
# mana-api.* subdomain is the unambiguous new home.
|
||
PUBLIC_MANA_API_URL: http://mana-api:3060
|
||
PUBLIC_MANA_API_URL_CLIENT: https://mana-api.mana.how
|
||
# Server-side same-origin proxy upstream for routes/api/v1/who/[...path].
|
||
# Inside docker the SvelteKit handler reaches mana-api over the internal
|
||
# network; the dev fallback in code is localhost:3060, so this env var
|
||
# is what makes prod hit the right hostname.
|
||
MANA_API_INTERNAL_URL: http://mana-api:3060
|
||
PUBLIC_MANA_CREDITS_URL: http://mana-credits:3061
|
||
PUBLIC_MANA_CREDITS_URL_CLIENT: https://credits.mana.how
|
||
# Per-app HTTP backend URLs (todo-api, calendar-api, contacts-api,
|
||
# chat-api, storage-api, cards-api, music-api,
|
||
# picture-api, presi-api, quotes-api, clock-api, context-api) and
|
||
# standalone server URLs were removed in the pre-launch
|
||
# ghost-API cleanup — every product module talks to mana-sync
|
||
# directly via mana-sync.
|
||
# See docs/PRE_LAUNCH_CLEANUP.md for the full rationale.
|
||
PUBLIC_MANA_MEDIA_URL: http://mana-media:3011
|
||
PUBLIC_MANA_MEDIA_URL_CLIENT: https://media.mana.how
|
||
PUBLIC_MANA_LLM_URL: http://mana-llm:3025
|
||
PUBLIC_MANA_LLM_URL_CLIENT: https://llm.mana.how
|
||
PUBLIC_MANA_EVENTS_URL: http://mana-events:3065
|
||
PUBLIC_MANA_EVENTS_URL_CLIENT: https://events.mana.how
|
||
# mana-research — async web-research provider orchestration.
|
||
# Browser hits /research/* endpoints directly; SSR uses the
|
||
# internal docker-network URL. Without this pair, the SSR-
|
||
# injected window.__PUBLIC_MANA_RESEARCH_URL__ is empty string
|
||
# and research fetches fall back to the current origin (404).
|
||
PUBLIC_MANA_RESEARCH_URL: https://research.mana.how
|
||
PUBLIC_MANA_RESEARCH_URL_CLIENT: https://research.mana.how
|
||
# mana-analytics — public-feedback hub. Browser hits the
|
||
# /api/v1/(public/)feedback/* endpoints directly; SSR uses the
|
||
# internal docker-network URL.
|
||
PUBLIC_MANA_ANALYTICS_URL: http://mana-analytics:3064
|
||
PUBLIC_MANA_ANALYTICS_URL_CLIENT: https://feedback.mana.how
|
||
# mana-ai background Mission Runner. Browser calls the audit
|
||
# endpoint (/api/v1/me/ai-audit) to render the Workbench
|
||
# "Datenzugriff" tab. SSR doesn't hit this service directly.
|
||
PUBLIC_MANA_AI_URL: http://mana-ai:3067
|
||
PUBLIC_MANA_AI_URL_CLIENT: https://mana-ai.mana.how
|
||
# Feature flag for the Mission Key-Grant consent UI. false → the
|
||
# dialog + Workbench audit tab stay hidden even on missions with
|
||
# encrypted inputs. Flip to "true" per deployment once the keypair
|
||
# is provisioned (see docs/plans/ai-mission-key-grant.md).
|
||
PUBLIC_AI_MISSION_GRANTS: ${PUBLIC_AI_MISSION_GRANTS:-false}
|
||
# Analytics & Error Tracking
|
||
PUBLIC_UMAMI_WEBSITE_ID: 32777167-e026-4618-933a-3429120b479b
|
||
PUBLIC_GLITCHTIP_DSN: ${GLITCHTIP_DSN_MANA_WEB:-}
|
||
# Speech-to-Text proxy: SvelteKit /api/v1/voice/transcribe forwards
|
||
# to mana-stt via Cloudflare Tunnel. The browser never sees the API
|
||
# key — it stays server-side.
|
||
MANA_STT_URL: https://gpu-stt.mana.how
|
||
MANA_STT_API_KEY: ${MANA_STT_API_KEY:-}
|
||
# LLM proxy: /api/v1/voice/parse-task and /api/v1/voice/parse-habit
|
||
# call mana-llm for structured extraction. Set WITHOUT the PUBLIC_
|
||
# prefix because $env/dynamic/private explicitly excludes vars
|
||
# that start with the public prefix — so the parse endpoints
|
||
# would never see PUBLIC_MANA_LLM_URL even though it's right
|
||
# there in the compose env. Both vars exist; the public one
|
||
# is read by the browser-side playground and status page.
|
||
MANA_LLM_URL: http://mana-llm:3025
|
||
MANA_LLM_API_KEY: ${MANA_LLM_API_KEY:-}
|
||
ports:
|
||
- "5000:5000"
|
||
healthcheck:
|
||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5000/health"]
|
||
interval: 180s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 20s
|
||
|
||
# REMOVED standalone web containers — now served by unified mana-web container (mana.how):
|
||
# chat-web, todo-web, quotes-web, calendar-web, clock-web, contacts-web,
|
||
# storage-web, presi-web, cards-web, skilltree-web, photos-web,
|
||
# music-web, picture-web, inventory-web, calc-web, times-web,
|
||
# uload-web
|
||
|
||
# picture-backend: REMOVED — replaced by Hono server (apps/picture/apps/server)
|
||
# arcade-web: REMOVED — extracted to standalone repo at ~/Documents/Code/arcade
|
||
|
||
manavoxel-web:
|
||
build:
|
||
context: .
|
||
dockerfile: apps/manavoxel/apps/web/Dockerfile
|
||
image: manavoxel-web:local
|
||
container_name: mana-app-manavoxel-web
|
||
restart: always
|
||
mem_limit: 128m
|
||
# Kern (mana-auth) lebt in Projekt mana-core, erreichbar übers Netz.
|
||
environment:
|
||
NODE_ENV: production
|
||
PORT: 5028
|
||
PUBLIC_MANA_AUTH_URL: http://mana-auth:3001
|
||
PUBLIC_MANA_AUTH_URL_CLIENT: https://auth.mana.how
|
||
PUBLIC_SYNC_SERVER_URL: http://mana-sync:3010
|
||
PUBLIC_SYNC_SERVER_URL_CLIENT: https://sync.mana.how
|
||
ports:
|
||
- "5028:5028"
|
||
healthcheck:
|
||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5028/health"]
|
||
interval: 180s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 45s
|
||
|
||
mana-llm:
|
||
build:
|
||
context: ../mana/services/mana-llm
|
||
dockerfile: Dockerfile
|
||
container_name: mana-service-llm
|
||
restart: unless-stopped
|
||
# Tier-3 right-size 2026-04-28: live RSS ~46 MiB (18%). The service
|
||
# is a thin OpenAI-compatible router around the GPU-box Ollama —
|
||
# all heavy LLM work happens upstream, this container just proxies.
|
||
# 128m is 2.5× headroom for streaming response buffers.
|
||
mem_limit: 128m
|
||
# Kern (redis) lebt in Projekt mana-core, erreichbar übers Netz.
|
||
# Ollama lives on the Windows GPU box at 192.168.178.11:11434, but
|
||
# Colima containers can't reach the LAN range — the entire
|
||
# 192.168.178.0/24 subnet gets synthesized RST from inside any
|
||
# container, even though the macOS host routes there fine. The
|
||
# gpu-proxy LaunchAgent on the Mac Mini host (com.mana.gpu-proxy,
|
||
# see /Users/mana/gpu-proxy.py) bridges 127.0.0.1:13434 → GPU
|
||
# box's 11434, so we go through host.docker.internal:13434 to
|
||
# reach Ollama. Without this hop the local mana-llm starts
|
||
# cleanly but reports an empty model list and every chat
|
||
# completion fails with "All connection attempts failed", which
|
||
# cascades into voice quick-add silently degrading to its no-LLM
|
||
# fallback for everyone hitting the local stack.
|
||
extra_hosts:
|
||
- "host.docker.internal:host-gateway"
|
||
environment:
|
||
PORT: 3025
|
||
LOG_LEVEL: info
|
||
OLLAMA_URL: ${OLLAMA_URL:-http://host.docker.internal:13434}
|
||
OLLAMA_DEFAULT_MODEL: ${OLLAMA_MODEL:-gemma3:12b}
|
||
OLLAMA_TIMEOUT: 120
|
||
REDIS_URL: redis://redis:6379
|
||
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
|
||
GROQ_API_KEY: ${GROQ_API_KEY:-}
|
||
TOGETHER_API_KEY: ${TOGETHER_API_KEY:-}
|
||
GOOGLE_API_KEY: ${GOOGLE_API_KEY:-}
|
||
GOOGLE_DEFAULT_MODEL: gemini-2.0-flash
|
||
# Direct providers (added 2026-05-14, option D — sowohl direkt als auch
|
||
# OpenRouter). Leer-Werte werden vom Router still übersprungen.
|
||
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
||
OPENAI_DEFAULT_MODEL: ${OPENAI_DEFAULT_MODEL:-gpt-4o-mini}
|
||
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
|
||
ANTHROPIC_DEFAULT_MODEL: ${ANTHROPIC_DEFAULT_MODEL:-claude-sonnet-4-5}
|
||
AUTO_FALLBACK_ENABLED: "true"
|
||
OLLAMA_MAX_CONCURRENT: 5
|
||
CORS_ORIGINS: https://playground.mana.how,https://mana.how,https://chat.mana.how,https://manawald.mana.how,http://localhost:3090
|
||
ports:
|
||
- "3025:3025"
|
||
healthcheck:
|
||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:3025/health')"]
|
||
interval: 120s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 30s
|
||
|
||
mana-api:
|
||
build:
|
||
context: .
|
||
dockerfile: apps/api/Dockerfile
|
||
args:
|
||
# Verdaccio-Token zum Pullen von @mana/media-client (und künftiger
|
||
# Plattform-Pakete). Auf dem Mac-Mini kommt aus `.env.macmini`,
|
||
# in CI aus `secrets.NPM_AUTH_TOKEN`. Setzt die `.npmrc`-
|
||
# Variable `${NPM_TOKEN}` zur Build-Zeit.
|
||
NPM_TOKEN: ${NPM_AUTH_TOKEN:-${NPM_TOKEN:-}}
|
||
image: mana-api:local
|
||
container_name: mana-api
|
||
restart: always
|
||
mem_limit: 384m
|
||
# Kern (postgres, mana-auth) lebt in Projekt mana-core, erreichbar übers Netz.
|
||
environment:
|
||
TZ: Europe/Berlin
|
||
NODE_ENV: production
|
||
PORT: 3060
|
||
# Auth (JWT validation via JWKS)
|
||
MANA_AUTH_URL: http://mana-auth:3001
|
||
JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY:-}
|
||
# Compute services apps/api orchestrates
|
||
MANA_LLM_URL: http://mana-llm:3025
|
||
MANA_SEARCH_URL: http://mana-search:3012
|
||
MANA_CREDITS_URL: http://mana-credits:3061
|
||
MANA_MEDIA_URL: http://mana-media:3011
|
||
MANA_CRAWLER_URL: http://mana-crawler:3014
|
||
# mana-news-pool — Plattform-Service (Lift-B 2026-05-16). Wird
|
||
# vom news-research-Modul + von der Pageta-Standalone konsumiert.
|
||
MANA_NEWS_POOL_URL: http://mana-news-pool:3079
|
||
MANA_LLM_DEFAULT_MODEL: ${MANA_LLM_DEFAULT_MODEL:-gemma3:4b}
|
||
MANA_SERVICE_KEY: ${MANA_SERVICE_KEY}
|
||
# OpenAI — picture module gpt-image-2 path. Optional: without it,
|
||
# /api/v1/picture/generate falls through to Replicate/local Flux.
|
||
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
||
# Replicate — fallback for Flux-schnell image generation
|
||
REPLICATE_API_TOKEN: ${REPLICATE_API_TOKEN:-}
|
||
APP_ID: mana-api
|
||
# Database (used by modules that have server-side state — research,
|
||
# presi share-links, traces guides). Same Postgres + schema split
|
||
# as the rest of the platform.
|
||
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform
|
||
# CORS — only the unified mana.how origin needs access today.
|
||
# The manavoxel game frontend doesn't call apps/api.
|
||
CORS_ORIGINS: https://mana.how
|
||
# Structured-logger format
|
||
LOGGER_FORMAT: json
|
||
ports:
|
||
- "3060:3060"
|
||
healthcheck:
|
||
test: ["CMD", "bun", "-e", "fetch('http://127.0.0.1:3060/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
||
interval: 60s
|
||
timeout: 5s
|
||
retries: 3
|
||
start_period: 30s
|
||
|
||
# ============================================
|
||
# News-Pool — wurde 2026-05-17 zur Plattform geliftet.
|
||
# ============================================
|
||
# Der ehemalige services/news-ingester:3066-Container ist abgeschaltet
|
||
# zugunsten von `mana-news-pool` (Plattform-Service Port 3079,
|
||
# ~/projects/mana/services/mana-news-pool/, eigene DB `mana_news_pool`).
|
||
# mana-api/news/routes.ts proxied auf MANA_NEWS_POOL_URL.
|
||
|