docker-compose.yml
Rama main · réplica de lectura del repositorio original.
version: "3.8"
services:
api:
build: ./services/api-rs
container_name: gctrl-api
ports:
- "4000:4000"
environment:
- NODE_ENV=development
- PHOENIX_OTLP_URL=${PHOENIX_OTLP_URL:-}
- PHOENIX_SERVICE_NAME=gctrl-api
- PORT=4000
- RUST_LOG=${RUST_LOG:-info,gctrl_api=debug}
- DATABASE_URL=postgresql://GCTRL:GCTRL@postgres:5432/GCTRL
- REDIS_URL=redis://redis:6379
- NEO4J_URI=bolt://neo4j:7687
- NEO4J_USER=neo4j
- NEO4J_PASSWORD=password
- JWT_SECRET=${JWT_SECRET:-GCTRL-dev-jwt-secret-change-in-production}
- JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:-GCTRL-dev-refresh-secret-change-in-production}
# AES-256-GCM key (base64 of 32 bytes) for at-rest secret encryption.
# MUST be set & rotated in prod; losing it makes encrypted secrets unrecoverable.
- GCTRL_SECRET_KEY=${GCTRL_SECRET_KEY:-rjFL483r7bwkbIpLSqYokmRqmZvq4Jt0XHUrEqczQ9g=}
- MAIL_HOST=maildev
- MAIL_PORT=1025
# Origin the user's browser actually hits. The dockerized web (nginx) is
# published on 3001 below, so the OAuth redirect_uri sent to Google and the
# post-login redirect must point there too. Override via .env for prod/dev.
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:3001}
- KEX_WORKER_URL=http://kex:4010
- FUSE_URL=http://fuse:4020
- INTERNAL_API_SECRET=${INTERNAL_API_SECRET:-gctrl-dev-internal-secret-change-me}
- QDRANT_URL=http://qdrant:6333
- QDRANT_API_KEY=${QDRANT_API_KEY:-}
- OLLAMA_BASE=http://ollama:11434
# Cloak Gateway cloud upstreams (default: the official hosts, pinned).
# - ANTHROPIC_BASE=https://api.anthropic.com
# - OPENAI_BASE=https://api.openai.com
# Skips the official-host pin on those bases. Dev only, never in prod.
# - GCTRL_CLOAK_UPSTREAM_UNPINNED=1
# OAuth Connectors (set in .env or override here)
- GCTRL_BASE_URL=${GCTRL_BASE_URL:-http://localhost:4000}
# Root under which server-side Obsidian folder vaults must live (read-only mount below).
- GCTRL_VAULTS_ROOT=/vaults
# Remote MCP gateway (/api/agent/mcp) — lets external multi-agent
# orchestrators drive the Pi harness over the network. OFF by default;
# every call is api-key authed, clearance-scoped and audited. On by default
# so the MCP gateway is reachable right after install; set to false to disable.
- GCTRL_AGENT_GATEWAY_ENABLED=${GCTRL_AGENT_GATEWAY_ENABLED:-true}
# ── Single source of truth for user-visible endpoint URLs ──────────────
# The UI fetches GET /api/config/public and derives EVERY displayed URL
# (MCP/n8n setup strings, Neo4j Browser links, agent health) from one
# canonical external origin = FRONTEND_URL (above). Change the published
# ports + FRONTEND_URL here / in .env and all displayed references update
# automatically — no hardcoded host:port is baked into the web bundle.
# The two vars below override the auto-derived Neo4j Browser / agent URLs.
# Leave empty to let the frontend derive http://<browser-host>:7474 / :7070.
- GCTRL_NEO4J_BROWSER_URL=${GCTRL_NEO4J_BROWSER_URL:-}
- GCTRL_AGENT_URL=${GCTRL_AGENT_URL:-}
# A4 memory-maintenance worker cadence (seconds). Default 600 (10 min).
- GCTRL_MEMORY_TICK_SECS=${GCTRL_MEMORY_TICK_SECS:-600}
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID:-}
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET:-}
- MICROSOFT_CLIENT_ID=${MICROSOFT_CLIENT_ID:-}
- MICROSOFT_CLIENT_SECRET=${MICROSOFT_CLIENT_SECRET:-}
- SLACK_CLIENT_ID=${SLACK_CLIENT_ID:-}
- SLACK_CLIENT_SECRET=${SLACK_CLIENT_SECRET:-}
- GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID:-}
- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET:-}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
volumes:
# Expose operator-chosen vault folders read-only. The API reads .md bytes
# from here for server-side Obsidian folder vaults (under GCTRL_VAULTS_ROOT).
- ${GCTRL_VAULTS_ROOT_HOST:-./vaults}:/vaults:ro
# The in-dashboard "Update now" button drives docker via this socket
# (pull + recreate). Without it the update executor can't reach docker
# (matches deploy/compose-template.yml, which already mounts it).
- /var/run/docker.sock:/var/run/docker.sock:ro
# Host hardware profile (true host RAM/GPU). Host RAM is unknowable from
# inside the container (/proc only sees the WSL2 VM), so it's supplied here.
# WITHOUT this mount the file is lost on every container recreate and the
# Settings "System (host)" view + Re-scan go blank. See deploy/hardware.json.
- ./deploy/hardware.json:/app/hardware.json:ro
networks:
- GCTRL
- ollama-net
restart: unless-stopped
web:
build: ./services/web
container_name: gctrl-web
ports:
- "3001:80"
depends_on:
- api
networks:
- GCTRL
restart: unless-stopped
# gctrl-agent: license enforcement — disabled for local dev.
# Re-enable for prod testing by pulling ghcr.io/gctrl-tech/agent:latest
# after `docker login ghcr.io`.
kex:
build: ./services/kex
container_name: gctrl-kex
ports:
- "127.0.0.1:4010:4010"
environment:
- OLLAMA_BASE=http://ollama:11434
- NEO4J_URI=bolt://neo4j:7687
- NEO4J_USER=neo4j
- NEO4J_PASSWORD=password
- REDIS_URL=redis://redis:6379
- QDRANT_URL=http://qdrant:6333
- QDRANT_API_KEY=${QDRANT_API_KEY:-}
- QDRANT_COLLECTION=GCTRL_chunks
- EMBEDDING_MODEL=nomic-embed-text
- PG_URL=postgresql://GCTRL:GCTRL@postgres:5432/GCTRL
- PORT=4010
- INTERNAL_API_SECRET=${INTERNAL_API_SECRET:-gctrl-dev-internal-secret-change-me}
# Worker credential fetch for keyed OpenAI-compatible runtimes (D7 in
# docs/superpowers/specs/2026-09-04-mlx-runtime-and-backpressure-design.md).
- GCTRL_INTERNAL_API_URL=http://api:4000
- KEX_ENTITY_VERIFY=${KEX_ENTITY_VERIFY:-false}
- PHOENIX_OTLP_URL=${PHOENIX_OTLP_URL:-}
- PHOENIX_SERVICE_NAME=gctrl-kex
- GLINER_MODEL=${GLINER_MODEL:-}
- GLINER_FALLBACK_MODEL=${GLINER_FALLBACK_MODEL:-}
- NER_THRESHOLD_BI=${NER_THRESHOLD_BI:-}
volumes:
- kex-models:/app/models
depends_on:
redis:
condition: service_started
postgres:
condition: service_healthy
networks:
- GCTRL
- ollama-net
restart: unless-stopped
fuse:
build: ./services/fuse
container_name: gctrl-fuse
ports:
- "127.0.0.1:4020:4020"
environment:
- NEO4J_URI=bolt://neo4j:7687
- NEO4J_USER=neo4j
- NEO4J_PASSWORD=password
- REDIS_URL=redis://redis:6379
- PORT=4020
- SIMILARITY_THRESHOLD=0.85
- RESOLVER_URL=http://resolver:8080
# WIKI distillation: Postgres (wiki_pages), Qdrant (grounding chunks),
# Ollama (page generation). Provider/model configurable; defaults are
# zero-config local Ollama llama3.2.
- PG_URL=postgresql://GCTRL:GCTRL@postgres:5432/GCTRL
- QDRANT_URL=http://qdrant:6333
- QDRANT_API_KEY=${QDRANT_API_KEY:-}
- QDRANT_COLLECTION=GCTRL_chunks
- OLLAMA_BASE=http://ollama:11434
- INTERNAL_API_SECRET=${INTERNAL_API_SECRET:-gctrl-dev-internal-secret-change-me}
- GCTRL_INTERNAL_API_URL=http://api:4000
- GCTRL_DISTILL_PROVIDER=${GCTRL_DISTILL_PROVIDER:-ollama}
- GCTRL_DISTILL_MODEL=${GCTRL_DISTILL_MODEL:-llama3.2}
- PHOENIX_OTLP_URL=${PHOENIX_OTLP_URL:-}
- PHOENIX_SERVICE_NAME=gctrl-fuse
depends_on:
redis:
condition: service_started
postgres:
condition: service_healthy
networks:
- GCTRL
- ollama-net
restart: unless-stopped
resolver:
build:
context: ../DataBorg Github/LIMES-master
dockerfile: Dockerfile
container_name: gctrl-resolver
ports:
- "8085:8080"
environment:
- JDK_JAVA_OPTIONS=-Xmx2G
command: ["-s"]
networks:
- GCTRL
restart: unless-stopped
postgres:
image: postgres:16-alpine
container_name: gctrl-postgres
ports:
- "5433:5432"
environment:
- POSTGRES_USER=GCTRL
- POSTGRES_PASSWORD=GCTRL
- POSTGRES_DB=GCTRL
volumes:
- GCTRL-pgdata:/var/lib/postgresql/data
- ./shared/db/migrations:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U GCTRL"]
interval: 5s
timeout: 5s
retries: 5
networks:
- GCTRL
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: gctrl-redis
ports:
- "6380:6379"
networks:
- GCTRL
restart: unless-stopped
maildev:
image: maildev/maildev
container_name: gctrl-maildev
ports:
- "1081:1080"
- "1026:1025"
networks:
- GCTRL
restart: unless-stopped
volumes:
GCTRL-pgdata:
kex-models:
networks:
GCTRL:
driver: bridge
ollama-net:
external: true
name: self-hosted-ai-starter-kit_demo