diff --git a/CLAUDE.md b/CLAUDE.md index b06eceb2e..defd2449e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -58,6 +58,15 @@ For comprehensive guidelines on code patterns and conventions, see the `.claude/ | **calc** | Calculator & converter | Web | | **playground** | LLM playground | Web | +### Games (`games/`) + +| Game | Description | Tech | +|------|-------------|------| +| **mana-games** | AI browser games platform (22+ games) | SvelteKit, NestJS, Gemini/Claude/GPT | +| **voxelava** | Voxel game | SvelteKit | +| **whopixels** | Phaser.js pixel game | Phaser, JavaScript | +| **worldream** | World exploration game | SvelteKit | + ### Archived Projects (`apps-archived/`) Currently empty. To archive a project, move it from `apps/` to `apps-archived/` (excluded from workspace). @@ -144,7 +153,10 @@ manacore-monorepo/ │ ├── uload/ │ └── wisekeep/ ├── games/ # Game projects -│ └── {game-name}/ # Individual games +│ ├── mana-games/ # AI browser games platform (SvelteKit + NestJS) +│ ├── voxelava/ # Voxel game +│ ├── whopixels/ # Phaser.js pixel game +│ └── worldream/ # World exploration game ├── services/ # Standalone microservices │ ├── mana-auth/ # Central auth (Hono + Bun + Better Auth) │ ├── mana-auth/ # Central auth rewrite (Hono + Bun + Better Auth) diff --git a/docker-compose.macmini.yml b/docker-compose.macmini.yml index c35da718b..509f0b2b2 100644 --- a/docker-compose.macmini.yml +++ b/docker-compose.macmini.yml @@ -11,6 +11,12 @@ # # Naming Convention: mana-{category}-{service} # Categories: infra, core, app, matrix, 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: # ============================================ @@ -21,6 +27,7 @@ services: image: postgres:16-alpine container_name: mana-infra-postgres restart: always + mem_limit: 1024m environment: POSTGRES_DB: mana POSTGRES_USER: postgres @@ -53,6 +60,7 @@ services: image: postgres:16-alpine container_name: mana-infra-postgres-backup restart: unless-stopped + mem_limit: 128m depends_on: postgres: condition: service_healthy @@ -94,6 +102,7 @@ services: 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 @@ -113,6 +122,7 @@ services: image: redis:7-alpine container_name: mana-infra-redis restart: always + mem_limit: 192m command: redis-server --requirepass ${REDIS_PASSWORD:-redis123} volumes: - redis_data:/data @@ -129,6 +139,7 @@ services: image: minio/minio:latest container_name: mana-infra-minio restart: always + mem_limit: 256m command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin} @@ -150,6 +161,7 @@ services: minio-init: image: minio/mc:latest container_name: mana-infra-minio-init + mem_limit: 64m depends_on: minio: condition: service_healthy @@ -189,6 +201,7 @@ services: image: codeberg.org/forgejo/forgejo:11 container_name: mana-core-forgejo restart: always + mem_limit: 512m depends_on: postgres: condition: service_healthy @@ -231,6 +244,7 @@ services: container_name: mana-core-forgejo-runner command: forgejo-runner daemon restart: always + mem_limit: 256m user: "0:0" depends_on: forgejo: @@ -252,6 +266,7 @@ services: image: mana-auth:local container_name: mana-auth restart: always + mem_limit: 192m depends_on: postgres: condition: service_healthy @@ -293,6 +308,7 @@ services: image: mana-credits:local container_name: mana-credits restart: always + mem_limit: 128m depends_on: postgres: condition: service_healthy @@ -327,6 +343,7 @@ services: image: mana-user:local container_name: mana-user restart: always + mem_limit: 128m depends_on: postgres: { condition: service_healthy } environment: @@ -352,6 +369,7 @@ services: image: mana-subscriptions:local container_name: mana-subscriptions restart: always + mem_limit: 128m depends_on: postgres: { condition: service_healthy } environment: @@ -380,6 +398,7 @@ services: image: mana-analytics:local container_name: mana-analytics restart: always + mem_limit: 128m depends_on: postgres: { condition: service_healthy } environment: @@ -387,7 +406,7 @@ services: PORT: 3064 DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_analytics MANA_CORE_AUTH_URL: http://mana-auth:3001 - MANA_LLM_URL: http://mana-llm:3020 + MANA_LLM_URL: http://mana-llm:3025 CORS_ORIGINS: https://mana.how ports: - "3064:3064" @@ -409,6 +428,7 @@ services: image: mana-api-gateway:local container_name: mana-api-gateway restart: always + mem_limit: 64m depends_on: postgres: condition: service_healthy @@ -440,6 +460,7 @@ services: image: searxng/searxng:latest container_name: mana-core-searxng restart: always + mem_limit: 256m volumes: - ./services/mana-search/searxng:/etc/searxng:ro environment: @@ -460,6 +481,7 @@ services: image: mana-search:local container_name: mana-core-search restart: always + mem_limit: 64m depends_on: searxng: condition: service_healthy @@ -491,6 +513,7 @@ services: image: mana-sync:local container_name: mana-core-sync restart: always + mem_limit: 64m depends_on: postgres: condition: service_healthy @@ -515,6 +538,7 @@ services: image: mana-notify:local container_name: mana-core-notify restart: always + mem_limit: 64m depends_on: postgres: condition: service_healthy @@ -547,6 +571,7 @@ services: image: mana-crawler:local container_name: mana-crawler restart: always + mem_limit: 128m depends_on: postgres: condition: service_healthy @@ -576,6 +601,7 @@ services: image: mana-media:local container_name: mana-core-media restart: always + mem_limit: 128m depends_on: postgres: condition: service_healthy @@ -616,6 +642,7 @@ services: image: mana-landing-builder:local container_name: mana-core-landing-builder restart: always + mem_limit: 192m depends_on: mana-auth: condition: service_healthy @@ -661,6 +688,7 @@ services: image: matrixdotorg/synapse:latest container_name: mana-matrix-synapse restart: always + mem_limit: 512m depends_on: postgres: condition: service_healthy @@ -690,6 +718,7 @@ services: image: vectorim/element-web:latest container_name: mana-matrix-element restart: always + mem_limit: 48m depends_on: synapse: condition: service_healthy @@ -711,6 +740,7 @@ services: image: matrix-web:latest container_name: mana-matrix-web restart: always + mem_limit: 96m depends_on: synapse: condition: service_healthy @@ -739,6 +769,7 @@ services: image: mana-matrix-bot:local container_name: mana-matrix-bot restart: always + mem_limit: 128m depends_on: synapse: condition: service_healthy @@ -827,6 +858,7 @@ services: image: manacore-web:local container_name: mana-app-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -855,6 +887,7 @@ services: image: ghcr.io/memo-2023/chat-web:latest container_name: mana-app-chat-web restart: always + mem_limit: 128m environment: NODE_ENV: production PORT: 5010 @@ -878,6 +911,7 @@ services: image: todo-web:local container_name: mana-app-todo-web restart: always + mem_limit: 128m environment: NODE_ENV: production PORT: 5011 @@ -901,6 +935,7 @@ services: image: zitare-web:local container_name: mana-app-zitare-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -926,6 +961,7 @@ services: image: calendar-web:local container_name: mana-app-calendar-web restart: always + mem_limit: 128m environment: NODE_ENV: production PORT: 5012 @@ -948,6 +984,7 @@ services: image: ghcr.io/memo-2023/clock-web:latest container_name: mana-app-clock-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -976,6 +1013,7 @@ services: image: contacts-web:local container_name: mana-app-contacts-web restart: always + mem_limit: 128m environment: NODE_ENV: production PORT: 5014 @@ -1003,6 +1041,7 @@ services: image: storage-web:local container_name: mana-app-storage-web restart: always + mem_limit: 128m environment: NODE_ENV: production PORT: 5015 @@ -1023,6 +1062,7 @@ services: image: ghcr.io/memo-2023/presi-web:latest container_name: mana-app-presi-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -1048,6 +1088,7 @@ services: image: manadeck-web:local container_name: mana-app-manadeck-web restart: always + mem_limit: 128m environment: NODE_ENV: production PORT: 5023 @@ -1068,6 +1109,7 @@ services: image: ghcr.io/memo-2023/nutriphi-web:latest container_name: mana-app-nutriphi-web restart: always + mem_limit: 128m environment: NODE_ENV: production PORT: 5017 @@ -1093,6 +1135,7 @@ services: image: skilltree-web:local container_name: mana-app-skilltree-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -1121,6 +1164,7 @@ services: image: photos-web:local container_name: mana-app-photos-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -1151,6 +1195,7 @@ services: image: mukke-web:local container_name: mana-app-mukke-web restart: always + mem_limit: 128m environment: NODE_ENV: production PORT: 5024 @@ -1176,6 +1221,7 @@ services: image: citycorners-web:local container_name: mana-app-citycorners-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -1205,6 +1251,7 @@ services: image: picture-web:local container_name: mana-app-picture-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -1230,6 +1277,7 @@ services: image: inventar-web:local container_name: mana-app-inventar-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -1254,6 +1302,7 @@ services: image: calc-web:local container_name: mana-app-calc-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -1272,6 +1321,32 @@ services: retries: 3 start_period: 20s + mana-games-web: + build: + context: . + dockerfile: games/mana-games/apps/web/Dockerfile + image: mana-games-web:local + container_name: mana-app-mana-games-web + restart: always + mem_limit: 128m + depends_on: + mana-auth: + condition: service_healthy + environment: + NODE_ENV: production + PORT: 5210 + PUBLIC_MANA_CORE_AUTH_URL: http://mana-auth:3001 + PUBLIC_MANA_CORE_AUTH_URL_CLIENT: https://auth.mana.how + PUBLIC_SYNC_SERVER_URL: ws://mana-sync:3010 + ports: + - "5210:5210" + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5210/health"] + interval: 180s + timeout: 10s + retries: 3 + start_period: 20s + taktik-web: build: context: . @@ -1279,6 +1354,7 @@ services: image: taktik-web:local container_name: mana-app-taktik-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -1304,6 +1380,7 @@ services: image: manavoxel-web:local container_name: mana-app-manavoxel-web restart: always + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -1328,6 +1405,7 @@ services: dockerfile: Dockerfile container_name: mana-service-llm restart: unless-stopped + mem_limit: 256m depends_on: redis: condition: service_healthy @@ -1347,9 +1425,9 @@ services: OLLAMA_MAX_CONCURRENT: 5 CORS_ORIGINS: https://playground.mana.how,https://mana.how,https://chat.mana.how ports: - - "3020:3020" + - "3025:3025" healthcheck: - test: ["CMD", "python", "-c", "import httpx; httpx.get('http://localhost:3020/health').raise_for_status()"] + test: ["CMD", "python", "-c", "import httpx; httpx.get('http://localhost:3025/health').raise_for_status()"] interval: 120s timeout: 10s retries: 3 @@ -1361,6 +1439,7 @@ services: dockerfile: apps/playground/apps/web/Dockerfile container_name: mana-app-llm-playground restart: unless-stopped + mem_limit: 128m depends_on: mana-auth: condition: service_healthy @@ -1371,7 +1450,7 @@ services: PORT: 5050 PUBLIC_MANA_CORE_AUTH_URL: http://mana-auth:3001 PUBLIC_MANA_CORE_AUTH_URL_CLIENT: https://auth.mana.how - PUBLIC_MANA_LLM_URL: http://mana-llm:3020 + PUBLIC_MANA_LLM_URL: http://mana-llm:3025 PUBLIC_MANA_LLM_URL_CLIENT: https://llm.mana.how ports: - "5050:5050" @@ -1392,6 +1471,7 @@ services: image: grafana/grafana:10.4.1 container_name: mana-mon-grafana restart: always + mem_limit: 192m depends_on: victoriametrics: condition: service_healthy @@ -1422,6 +1502,7 @@ services: image: ghcr.io/umami-software/umami:postgresql-latest container_name: mana-mon-umami restart: always + mem_limit: 256m depends_on: postgres: condition: service_healthy @@ -1447,6 +1528,7 @@ services: image: victoriametrics/victoria-metrics:v1.99.0 container_name: mana-mon-victoria restart: always + mem_limit: 256m command: - '-storageDataPath=/storage' - '-retentionPeriod=2y' @@ -1471,6 +1553,7 @@ services: image: grafana/loki:3.0.0 container_name: mana-mon-loki restart: always + mem_limit: 192m command: -config.file=/etc/loki/local-config.yaml volumes: - ./docker/loki:/etc/loki:ro @@ -1488,6 +1571,7 @@ services: image: prom/pushgateway:v1.7.0 container_name: mana-mon-pushgateway restart: always + mem_limit: 48m ports: - "9091:9091" healthcheck: @@ -1501,6 +1585,7 @@ services: image: gcr.io/cadvisor/cadvisor:v0.49.1 container_name: mana-mon-cadvisor restart: always + mem_limit: 128m privileged: true volumes: - /:/rootfs:ro @@ -1521,6 +1606,7 @@ services: image: prometheuscommunity/postgres-exporter:v0.15.0 container_name: mana-mon-postgres-exporter restart: always + mem_limit: 48m depends_on: postgres: condition: service_healthy @@ -1533,6 +1619,7 @@ services: image: oliver006/redis_exporter:v1.58.0 container_name: mana-mon-redis-exporter restart: always + mem_limit: 32m depends_on: redis: condition: service_healthy @@ -1546,6 +1633,7 @@ services: image: prom/node-exporter:v1.7.0 container_name: mana-mon-node-exporter restart: always + mem_limit: 32m # macOS Docker runs in a Linux VM, so we can only monitor the VM's metrics # For full host metrics on macOS, install node_exporter natively command: @@ -1575,6 +1663,7 @@ services: image: victoriametrics/vmalert:v1.99.0 container_name: mana-mon-vmalert restart: always + mem_limit: 64m depends_on: victoriametrics: condition: service_healthy @@ -1603,6 +1692,7 @@ services: image: prom/alertmanager:v0.27.0 container_name: mana-mon-alertmanager restart: always + mem_limit: 64m depends_on: alert-notifier: condition: service_healthy @@ -1629,6 +1719,7 @@ services: image: alert-notifier:local container_name: mana-mon-alert-notifier restart: always + mem_limit: 32m environment: PORT: 8080 TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN:-} @@ -1651,6 +1742,7 @@ services: image: nickfedor/watchtower:latest container_name: mana-auto-watchtower restart: always + mem_limit: 64m volumes: - /var/run/docker.sock:/var/run/docker.sock environment: @@ -1673,6 +1765,7 @@ services: image: glitchtip/glitchtip:latest container_name: mana-mon-glitchtip restart: always + mem_limit: 256m environment: DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/glitchtip REDIS_URL: redis://:${REDIS_PASSWORD:-redis123}@redis:6379/1 @@ -1700,6 +1793,7 @@ services: image: glitchtip/glitchtip:latest container_name: mana-mon-glitchtip-worker restart: always + mem_limit: 192m command: ./bin/run-celery-with-beat.sh environment: DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/glitchtip @@ -1723,6 +1817,7 @@ services: dockerfile: games/whopixels/Dockerfile container_name: mana-game-whopixels restart: unless-stopped + mem_limit: 128m environment: PORT: 5100 AZURE_OPENAI_API_KEY: ${AZURE_OPENAI_API_KEY:-}