# docker-compose.yml — Cerbero Bite # # Bite runs in its own Compose project but joins the same Docker # network used by Cerbero MCP V2 and Traefik (`traefik`) so it can # either resolve the in-cluster service name (`cerbero-mcp:9000`) # or reach the public gateway (`https://cerbero-mcp.tielogic.xyz`) # transparently. # # The reverse-proxy network (`traefik`) is declared as external # here. It is created by the Traefik stack at /opt/docker/traefik # and shared by every web-facing service on the host. # # Authentication: a single bearer token is passed through from the # host `.env` file via `CERBERO_BITE_MCP_TOKEN`. The Cerbero MCP V2 # server uses the token to decide whether the upstream environment # is testnet or mainnet; switching environment = switching token. # # Two services are defined: # * `cerbero-bite` — the trading engine / CLI worker # * `cerbero-bite-gui` — the Streamlit dashboard, exposed by # Traefik at https://cerbero-bite. networks: traefik: external: true volumes: bite-data: x-bite-env: &bite-env CERBERO_BITE_MCP_TOKEN: ${CERBERO_BITE_MCP_TOKEN:?missing CERBERO_BITE_MCP_TOKEN} CERBERO_BITE_MCP_BOT_TAG: ${CERBERO_BITE_MCP_BOT_TAG:-BOT__CERBERO_BITE} # Two independent runtime flags that decide what each cycle does. # Initial period ("data-only"): DATA_ANALYSIS=true, STRATEGY=false. CERBERO_BITE_ENABLE_DATA_ANALYSIS: ${CERBERO_BITE_ENABLE_DATA_ANALYSIS:-true} CERBERO_BITE_ENABLE_STRATEGY: ${CERBERO_BITE_ENABLE_STRATEGY:-false} # Service URLs — defaults below match the in-cluster Traefik network # DNS (V2 unified image listening on port 9000). Override any of # them via .env to point at the public gateway, a custom host, or # localhost for dev work. CERBERO_BITE_MCP_DERIBIT_URL: ${CERBERO_BITE_MCP_DERIBIT_URL:-http://cerbero-mcp:9000/mcp-deribit} CERBERO_BITE_MCP_HYPERLIQUID_URL: ${CERBERO_BITE_MCP_HYPERLIQUID_URL:-http://cerbero-mcp:9000/mcp-hyperliquid} CERBERO_BITE_MCP_MACRO_URL: ${CERBERO_BITE_MCP_MACRO_URL:-http://cerbero-mcp:9000/mcp-macro} CERBERO_BITE_MCP_SENTIMENT_URL: ${CERBERO_BITE_MCP_SENTIMENT_URL:-http://cerbero-mcp:9000/mcp-sentiment} services: cerbero-bite: build: context: . dockerfile: Dockerfile image: cerbero-bite:dev restart: unless-stopped networks: [traefik] cap_drop: [ALL] security_opt: - no-new-privileges:true environment: <<: *bite-env # Telegram and Portfolio are no longer shared MCP services. The # bot now calls the Telegram Bot API directly and aggregates # portfolio in-process from Deribit + Hyperliquid + Macro. # Set the two env vars below to enable Telegram notifications. # CERBERO_BITE_TELEGRAM_BOT_TOKEN: ... # CERBERO_BITE_TELEGRAM_CHAT_ID: ... volumes: - bite-data:/app/data healthcheck: test: ["CMD", "cerbero-bite", "healthcheck", "--db", "/app/data/state.sqlite"] interval: 60s timeout: 5s retries: 3 start_period: 120s # Engine main loop (scheduler + monitoring). Switch to `status`, # `ping`, `dry-run`, ... for one-shot diagnostics. The MCP token in # `.env` decides the upstream environment server-side; the `start` # flag below tells the local boot check what to expect (must match, # otherwise the engine arms the kill switch). command: ["start", "--environment", "mainnet"] # Streamlit dashboard published by Traefik on # https://cerbero-bite.${DOMAIN_NAME:-tielogic.xyz} # # The CLI sub-command `cerbero-bite gui` hard-codes the listen # address to 127.0.0.1, so we bypass the entrypoint and invoke # Streamlit directly. The two `CERBERO_BITE_GUI_*` env vars match # what the CLI normally injects (see src/cerbero_bite/cli.py). cerbero-bite-gui: image: cerbero-bite:dev restart: unless-stopped depends_on: - cerbero-bite networks: [traefik] cap_drop: [ALL] security_opt: - no-new-privileges:true environment: <<: *bite-env CERBERO_BITE_GUI_DB: /app/data/state.sqlite CERBERO_BITE_GUI_AUDIT: /app/data/log/audit.jsonl volumes: - bite-data:/app/data entrypoint: - python - -m - streamlit - run - /app/src/cerbero_bite/gui/main.py - --server.address=0.0.0.0 - --server.port=8765 - --server.headless=true - --browser.gatherUsageStats=false command: [] healthcheck: test: - "CMD" - "python" - "-c" - "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8765/_stcore/health', timeout=3).close()" interval: 30s timeout: 5s retries: 3 start_period: 30s labels: - traefik.enable=true - traefik.docker.network=traefik - "traefik.http.routers.cerbero-bite.rule=Host(`cerbero-bite.${DOMAIN_NAME:-tielogic.xyz}`)" - traefik.http.routers.cerbero-bite.tls=true - traefik.http.routers.cerbero-bite.entrypoints=websecure - traefik.http.routers.cerbero-bite.tls.certresolver=mytlschallenge - traefik.http.services.cerbero-bite.loadbalancer.server.port=8765 - com.centurylinklabs.watchtower.enable=true