services: toolmesh: image: ghcr.io/dunkelcloud/toolmesh:latest # To build locally instead of pulling the image, comment the line above # and uncomment the next line: # build: . restart: unless-stopped # Bound total memory so a runaway sandbox allocation (composite/code-mode # JS has no per-runtime heap cap) is contained to this auto-restarting # container instead of OOM-killing the host. GOMEMLIMIT below is a soft # limit (~75% of mem_limit) so the GC reclaims and throttles first. mem_limit: 1g ports: - "${TOOLMESH_PORT:-8123}:8080" # Prometheus metrics — runs on a separate listener so the scraper does # not need to traverse the public auth-protected MCP port. The endpoint # is unauthenticated, so the host port is bound to 127.0.0.1 by default: # reachable from the local host (and Prometheus on this Docker network via # the service name), but never exposed on a public interface. Override # TOOLMESH_METRICS_HOST=0.0.0.0 only if you deliberately scrape from # another host and have firewalled the port yourself. - "${TOOLMESH_METRICS_HOST:-127.0.0.1}:${TOOLMESH_METRICS_PORT:-9090}:9090" # Every value from .env ends up in the container configuration on disk # (docker inspect shows it). To keep credentials out of it, use the # command credential store: docker-compose.override.yml.example has the # mounts it needs, docs/configuration.md ("Command store") the rest. env_file: .env environment: # Soft heap limit (~75% of mem_limit) so the Go GC throttles allocation # before the container hard limit triggers an OOM kill. - GOMEMLIMIT=${GOMEMLIMIT:-768MiB} - AUDIT_STORE=${AUDIT_STORE:-log} - TOOLMESH_DATA_DIR=/app/data - TOOLMESH_METRICS_ENABLED=${TOOLMESH_METRICS_ENABLED:-true} - TOOLMESH_METRICS_LABEL_TOOL=${TOOLMESH_METRICS_LABEL_TOOL:-true} - TOOLMESH_DEBUG_TOOLS=${TOOLMESH_DEBUG_TOOLS:-false} depends_on: keydb: condition: service_healthy volumes: - ./config:/app/config:ro - ./config/policies:/app/policies:ro - ./tools:/app/tools:ro - ./dadl:/app/dadl:ro - ./data:/app/data # Demo MCP backend (optional — uncomment to enable) # Note: mcp/everything has a known crash loop with SSE transport. # Use only for testing, not in production. # everything: # image: mcp/everything # entrypoint: ["node", "dist/sse.js"] # ports: # - "3001:3001" # --- External OpenFGA server (TOOLMESH_AUTHZ_MODE=external) --- # Not needed by default: authorization runs inside ToolMesh # (TOOLMESH_AUTHZ_MODE=embedded, data in ./data/authz.db). Use these services # only to run your own OpenFGA server, e.g. one shared with other # applications. # To enable: # 1. Uncomment the OpenFGA services and the mysqldata volume below. # 2. docker compose up -d # 3. ./config/openfga/setup.sh # 4. Copy OPENFGA_STORE_ID from the output into .env and set # TOOLMESH_AUTHZ_MODE=external and OPENFGA_API_URL=http://openfga:8080. # 5. docker compose down && docker compose up -d # # openfga-migrate: # image: openfga/openfga:latest # command: migrate # environment: # - OPENFGA_DATASTORE_ENGINE=mysql # - OPENFGA_DATASTORE_URI=${OPENFGA_DB_USER:-openfga}:${OPENFGA_DB_PASSWORD:-openfga}@tcp(mysql:3306)/${OPENFGA_DB_NAME:-openfga}?parseTime=true # depends_on: # mysql: # condition: service_healthy # # openfga: # image: openfga/openfga:latest # ports: # - "8081:8080" # API — expose only if you need host access # command: run # environment: # - OPENFGA_DATASTORE_ENGINE=mysql # - OPENFGA_DATASTORE_URI=${OPENFGA_DB_USER:-openfga}:${OPENFGA_DB_PASSWORD:-openfga}@tcp(mysql:3306)/${OPENFGA_DB_NAME:-openfga}?parseTime=true # depends_on: # openfga-migrate: # condition: service_completed_successfully # healthcheck: # test: ["CMD", "/usr/local/bin/grpc_health_probe", "-addr=:8081"] # interval: 5s # timeout: 3s # retries: 5 # # mysql: # image: mysql:8.0 # environment: # MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword} # MYSQL_DATABASE: ${OPENFGA_DB_NAME:-openfga} # MYSQL_USER: ${OPENFGA_DB_USER:-openfga} # MYSQL_PASSWORD: ${OPENFGA_DB_PASSWORD:-openfga} # volumes: # - mysqldata:/var/lib/mysql # healthcheck: # test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] # interval: 10s # timeout: 5s # retries: 10 # start_period: 30s keydb: image: eqalpha/keydb:alpine restart: unless-stopped # No ports exposed — only accessible from within the Docker network. command: keydb-server --appendonly yes volumes: - keydbdata:/data healthcheck: test: ["CMD", "keydb-cli", "ping"] interval: 5s timeout: 3s retries: 5 # --- Reverse proxy (uncomment for HTTPS / Claude.ai access) --- # caddy: # image: caddy:2-alpine # ports: # - "443:443" # - "80:80" # volumes: # - ./Caddyfile:/etc/caddy/Caddyfile:ro # - caddy_data:/data # - caddy_config:/config # depends_on: # - toolmesh volumes: # mysqldata: # uncomment together with the OpenFGA services above keydbdata: # caddy_data: # caddy_config: