Ctrl K
ComunidadGuías…

docker-compose.yml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Todd — self-hosted multi-agent operator.
#   docker compose up -d --build        → dashboard at http://localhost:3000
#   docker compose --profile local up   → also runs Ollama for fully local models
# Requires Docker Compose v2.24+.
#
# Isolation:
#   core         postgres, api, web          (sandbox and browser can't reach the DB or the dashboard)
#   sandbox_net  api ↔ sandbox               (agent-run code can reach the API port, but every API call needs a
#   browser_net  api ↔ browser                token that only api and web have: see the api-token volume)
#   signedin_net api ↔ signedin              commands that use your sign-ins (git push, gh, deploy CLIs, EAS, CLI
#                sign-ins) run here, in their own container with the same workspace: nothing running in the sandbox
#                (a dev server, an npm install script) can read their tokens
#   media_net    api ↔ media                 embeddings, slides and MP4s for the video toolset: the shared workspace,
#                a random token only the API has (media-token volume), no vault, fetches only MEDIA_FETCH_HOSTS
#   previews     localhost:3000 (and other dev ports) in the browser → api:17000+ → the same port in the sandbox, so
#                agents can look at what they're building without deploying it (PREVIEW_PORTS / PREVIEW_RELAY_BASE)
# Published ports bind to 127.0.0.1 only. The dashboard answers only to localhost and only to its own pages (see
# services/web/lib/guard.ts; TODD_ALLOWED_HOSTS adds names), and the live view has a password (random unless
# VNC_PASSWORD is set). Todd can spend money: don't expose it publicly without adding real sign-in.
name: todd

services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: todd
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-todd}
      POSTGRES_DB: todd
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks: [core]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U todd -d todd"]
      interval: 5s
      timeout: 3s
      retries: 20

  api:
    build: ./services/api
    restart: unless-stopped
    env_file:
      - path: .env
        required: false
    environment:
      DATABASE_URL: postgresql+psycopg://todd:${POSTGRES_PASSWORD:-todd}@postgres:5432/todd
      TODD_DATA_DIR: /data
      TODD_API_TOKEN_FILE: /shared/api_token
      SANDBOX_URL: http://sandbox:7000
      SANDBOX_TOKEN: ${SANDBOX_TOKEN:-change-me-sandbox-token}
      SIGNEDIN_URL: http://signedin:7000
      SIGNEDIN_TOKEN_FILE: /signedin/token
      MEDIA_URL: http://media:7000
      MEDIA_TOKEN_FILE: /media/token
      BROWSER_CDP_HOST: browser
      BROWSER_CDP_PORT: "9223"
      BROWSER_LIVE_URL: ${BROWSER_LIVE_URL:-http://localhost:6080/vnc_lite.html?scale=true}
      VNC_PASSWORD: ${VNC_PASSWORD:-}
      VNC_PASSWORD_FILE: /live/vnc_password
      CORS_ORIGINS: http://localhost:3000
    volumes:
      - apidata:/data
      - api-token:/shared
      - signedin-token:/signedin
      - media-token:/media
      - live-password:/live:ro
      - ./plugins:/app/custom/plugins
      - ./prompts:/app/custom/prompts
    networks: [core, sandbox_net, browser_net, signedin_net, media_net]
    depends_on:
      postgres:
        condition: service_healthy
      sandbox:
        condition: service_started
      signedin:
        condition: service_started
      media:
        condition: service_started
      browser:
        condition: service_started

  sandbox:
    build: ./services/sandbox
    restart: unless-stopped
    init: true  # reaps the background processes agents and CLI sign-ins leave behind
    environment:
      SANDBOX_TOKEN: ${SANDBOX_TOKEN:-change-me-sandbox-token}
    volumes:
      - workspace:/workspace
    networks: [sandbox_net]
    security_opt: ["no-new-privileges:true"]

  # Same image, same workspace; runs only the commands that carry your sign-ins. Only the API reaches it, with a
  # random token the API makes (signedin-token volume).
  signedin:
    build: ./services/sandbox
    restart: unless-stopped
    init: true
    environment:
      SANDBOX_TOKEN_FILE: /secret/token
      SANDBOX_PREVIEWS: "0"
    volumes:
      - workspace:/workspace
      - signedin-token:/secret:ro
    networks: [signedin_net]
    security_opt: ["no-new-privileges:true"]

  # Image embeddings (CLIP), captioned slides and MP4s for the video toolset. Only the API reaches it, with a random
  # token the API makes (media-token volume); it mounts the workspace and never sees the vault.
  media:
    build: ./services/media
    restart: unless-stopped
    init: true
    environment:
      MEDIA_TOKEN_FILE: /secret/token
      WORKSPACE_ROOT: /workspace
      MEDIA_FETCH_HOSTS: ${MEDIA_FETCH_HOSTS:-images.pexels.com,videos.pexels.com,pixabay.com,cdn.pixabay.com}
    volumes:
      - workspace:/workspace
      - media-token:/secret:ro
    networks: [media_net]
    security_opt: ["no-new-privileges:true"]

  browser:
    build: ./services/browser
    restart: unless-stopped
    shm_size: 2gb
    environment:
      VNC_PASSWORD: ${VNC_PASSWORD:-}
      SCREEN_WIDTH: "1440"
      SCREEN_HEIGHT: "900"
    volumes:
      - browser-profile:/data
      - live-password:/secret
    networks: [browser_net]
    ports:
      - "127.0.0.1:6080:6080"   # live view (noVNC)

  web:
    build: ./services/web
    restart: unless-stopped
    environment:
      API_URL: http://api:8000
      TODD_API_TOKEN_FILE: /shared/api_token
      TODD_ALLOWED_HOSTS: ${TODD_ALLOWED_HOSTS:-}  # names besides localhost the dashboard answers to (comma-separated)
    volumes:
      - api-token:/shared:ro
    networks: [core]
    ports:
      - "127.0.0.1:3000:3000"
    depends_on:
      api:
        condition: service_healthy

  ollama:
    image: ollama/ollama:latest
    profiles: ["local"]
    restart: unless-stopped
    volumes:
      - ollama:/root/.ollama
    networks: [core]

networks:
  core:
  sandbox_net:
  browser_net:
  signedin_net:
  media_net:

volumes:
  pgdata:
  apidata:
  api-token:
  signedin-token:
  media-token:
  live-password:
  workspace:
  browser-profile:
  ollama: