diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md deleted file mode 100644 index 2eef10e..0000000 --- a/DEPLOYMENT.md +++ /dev/null @@ -1,448 +0,0 @@ -# Deployment Cerbero_mcp - -Guida operativa per il deploy della suite MCP su un VPS pubblico. -L'architettura è: Gitea ospita codice + container registry; le immagini -vengono buildate e pushate dalla **macchina di sviluppo** (laptop) verso -il registry; il VPS produzione non builda nulla, fa solo pull dei -container già pronti e usa Watchtower per il rollover automatico. - -``` -┌──────────────────────────┐ ┌─────────────────────────┐ ┌──────────────────────────────────┐ -│ Laptop dev │ │ Gitea git.tielogic.xyz │ │ VPS produzione │ -│ │ │ │ │ cerbero-mcp.tielogic.xyz │ -│ build-push.sh ──push──▶ │───▶│ ┌────────────────────┐ │ │ │ -│ (8 image) │ │ │ Container registry │ │ │ ┌────────────────────────────┐ │ -│ git push ─────────────▶ │───▶│ └────────────────────┘ │◀──┼──┤ docker compose │ │ -│ │ │ ┌────────────────────┐ │ pull │ (docker-compose.prod.yml) │ │ -│ │ │ │ Cerbero-mcp repo │ │ │ │ gateway, mcp-* │ │ -│ │ │ └────────────────────┘ │ │ │ watchtower (poll 5min) │ │ -│ │ │ │ │ └────────────────────────────┘ │ -└──────────────────────────┘ └─────────────────────────┘ └──────────────────────────────────┘ -``` - -Niente CI/CD su Gitea — qualità e build sono responsabilità del laptop -prima del push (lint/test in locale, poi `scripts/build-push.sh`). - -## 1. Build & push image (dal laptop) - -Lo script `scripts/build-push.sh` builda e pusha le 8 image al registry -Gitea, replicando il vecchio job CI ma in locale. Pre-requisiti: - -- `docker` + `buildx` sul laptop. -- Personal Access Token Gitea con scope `write:package` (User Settings - → Applications → Generate Token). - -```bash -export GITEA_PAT='' -export GITEA_USER=adriano - -# Tutte le 8 image (base + gateway + 6 mcp-*) -./scripts/build-push.sh - -# Solo specifiche (es. dopo modifica a un singolo servizio) -./scripts/build-push.sh base mcp-bybit -``` - -Lo script: -- Fa `docker login git.tielogic.xyz`. -- Builda con `docker buildx build --push` (cache buildx locale del - laptop, niente cache registry: build successivi rapidi senza pesare - sul registry). -- Tagga `:latest` + `:sha-`. -- Per le mcp-* passa `BASE_IMAGE`/`BASE_TAG` come build-arg in modo da - ereditare dall'image `base` appena pushata. - -Ordine consigliato: builda `base` prima delle `mcp-*` (lo script lo fa -di default se chiamato senza argomenti). - -## 1b. Quality gate locale (consigliato prima del push) - -Prima di `build-push.sh` esegui in locale i check che prima girava il CI: - -```bash -uv run ruff check services/ -uv run mypy services/common/src/mcp_common -uv run pytest services/ --tb=short -docker compose -f docker-compose.prod.yml config -q -``` - -Tutti devono essere verdi prima di pushare image al registry. - -## 2a. Topologia: standalone vs behind-Traefik - -Cerbero_mcp supporta due topologie di deploy: - -### Standalone (Caddy gestisce TLS direttamente) - -``` -Internet ──[443]──► Caddy gateway ──► mcp-* services - (ACME Let's Encrypt) -``` - -Setto: `docker-compose.prod.yml` da solo. Caddy bind sulle porte -80/443 host, fa cert auto via ACME. Adatto a un VPS dedicato senza -altri servizi sulle 80/443. - -### Behind-Traefik (Traefik termina TLS) - -``` -Internet ──[443]──► Traefik ──[traefik network]──► Caddy gateway ──► mcp-* services - (TLS+ACME) (rate-limit, IP allowlist) -``` - -Setto: `docker-compose.prod.yml` + `docker-compose.traefik.yml` overlay. -Caddy non bind su host, ascolta plain HTTP `:80` interno alla -`traefik` network. Traefik fa routing per `Host(cerbero-mcp.tielogic.xyz)`, -TLS, ACME. Adatto a VPS condiviso con altri servizi (Gitea, ecc.). - -## 2. Deploy automatizzato (script no-clone) - -Il modo più rapido è `scripts/deploy-noclone.sh`, idempotente. Sul VPS -**non** viene clonato il repo: lo script scarica via raw HTTP solo i -file strettamente necessari al runtime (compose, Caddyfile, public -assets). Esegui sul VPS: - -```bash -# Prerequisiti -export GITEA_PAT="" -export GITEA_USER=adriano - -# Crea la dir di deploy e mettici i secrets via scp dal posto sicuro -sudo mkdir -p /docker/cerbero_mcp/secrets -sudo chown -R "$USER" /docker/cerbero_mcp -# scp deribit.json bybit.json hyperliquid.json alpaca.json \ -# macro.json sentiment.json core.token observer.token \ -# vps:/docker/cerbero_mcp/secrets/ - -# Behind Traefik (opzionale, solo se VPS condiviso con Gitea o altri) -# export BEHIND_TRAEFIK=true -# export TRAEFIK_NETWORK=gitea_traefik-public - -curl -sL -o /tmp/deploy-noclone.sh \ - https://git.tielogic.xyz/Adriano/Cerbero-mcp/raw/branch/main/scripts/deploy-noclone.sh -chmod +x /tmp/deploy-noclone.sh -/tmp/deploy-noclone.sh -``` - -Lo script esegue: docker login registry → scarica `docker-compose.prod.yml`, -`docker-compose.traefik.yml`, `gateway/Caddyfile`, `gateway/public/*` in -`/docker/cerbero_mcp/` → chmod 600 sui secrets → genera `.env` iniziale -(testnet) → crea `/var/log/cerbero-mcp` con permessi `1000:1000` → pull -image dal registry → `docker compose up -d` → smoke test pubblico. - -Per aggiornare in seguito: ri-esegui lo stesso script (preserva `.env` -e secrets, ricarica config dal branch `main` aggiornato). - -**Override paths**: `DEPLOY_DIR` (default `/docker/cerbero_mcp`), -`SECRETS_SRC` (default `$DEPLOY_DIR/secrets`), `AUDIT_LOG_DIR` (default -`/var/log/cerbero-mcp`). - -**Override compose locale (`docker-compose.local.yml`)**: lo script -include automaticamente come ultimo `-f` un eventuale -`$DEPLOY_DIR/docker-compose.local.yml`. Utile per fix specifici della -macchina (es. forzare `DOCKER_API_VERSION` su watchtower se il daemon -del VPS è più vecchio dell'API attesa). File gitignored per design — -non viene scaricato dal repo, lo crei a mano sul VPS. Esempio: - -```yaml -# /docker/cerbero_mcp/docker-compose.local.yml -services: - watchtower: - environment: - DOCKER_API_VERSION: "1.44" -``` - -### Modalità behind-Traefik - -Se sul VPS gira già un Traefik (es. lo stesso VPS di Gitea), prima di -lanciare lo script aggiungi al tuo `.env`: - -```bash -BEHIND_TRAEFIK=true -TRAEFIK_NETWORK=gitea_traefik-public # nome network esterna di Traefik -TRAEFIK_CERTRESOLVER=letsencrypt # nome resolver in Traefik -TRAEFIK_ENTRYPOINT=websecure # entrypoint HTTPS Traefik - -# Porte gateway non più necessarie (Traefik bind 80/443): -# GATEWAY_HTTP_PORT, GATEWAY_HTTPS_PORT non vengono usate. -``` - -Lo script rileva `BEHIND_TRAEFIK=true` e usa -`docker compose -f docker-compose.prod.yml -f docker-compose.traefik.yml`. -Il gateway Caddy NON bind su 80/443 host; viene esposto via Traefik con -labels per `Host(cerbero-mcp.tielogic.xyz)`. - -Verifica della network Traefik: - -```bash -docker network ls | grep -i traefik -# Tipicamente vedrai: gitea_traefik-public, traefik_default, ecc. -# Usa il nome ESATTO come TRAEFIK_NETWORK in .env. -``` - -## 3. Safety: switch testnet → mainnet - -`mcp_common.environment.consistency_check` (richiamato dal boot -`run_exchange_main`) PREVIENE switch accidentali: - -- Se l'ambiente risolto è **mainnet** ma il secret JSON corrispondente - non contiene `"environment": "mainnet"` esplicito → boot abort con - `EnvironmentMismatchError`. -- Se il secret dichiara un environment diverso da quello risolto (es. - `creds["environment"]="mainnet"` ma env var setta testnet) → boot abort. - -**Per passare a mainnet su un exchange specifico** (es. bybit): - -1. Edita `secrets/bybit.json`: aggiungi `"environment": "mainnet"`. -2. Modifica `.env`: `BYBIT_TESTNET=false`. -3. `docker compose -f docker-compose.prod.yml --env-file .env restart mcp-bybit`. - -Senza il flag esplicito nel secret, il container mcp-bybit fallirà al -boot e Watchtower NON aggiornerà su versioni con cred mainnet rotti. - -Override `STRICT_MAINNET=false` in `.env` permette mainnet senza la -conferma esplicita (downgrade safety, sconsigliato in produzione). - -## 4. Audit log persistente - -Tutti i write endpoint (`place_order`, `place_combo_order`, `cancel_*`, -`set_*`, `close_*`, `transfer_*`, `amend_*`, `switch_*`) emettono un -record JSON strutturato sul logger `mcp.audit`. - -**Sink**: -- stdout/stderr container (sempre, visibile via `docker logs`). -- File JSONL persistente su volume host: - `${AUDIT_LOG_DIR:-/var/log/cerbero-mcp}/.audit.jsonl`. - Rotation a mezzanotte UTC con retention `AUDIT_LOG_BACKUP_DAYS` - (default 30 giorni). - -**Esempio record**: - -```json -{ - "audit_event": "write_op", - "action": "place_order", - "exchange": "bybit", - "principal": "core", - "target": "BTCUSDT", - "payload": {"side": "Buy", "qty": 0.01, "price": 60000, "leverage": 3}, - "result": {"order_id": "abc123", "status": "submitted"} -} -``` - -**Query operative**: - -```bash -# Tutto l'audit log oggi -tail -f /var/log/cerbero-mcp/*.audit.jsonl - -# Solo place_order su bybit -jq -c 'select(.action=="place_order" and .exchange=="bybit")' \ - /var/log/cerbero-mcp/bybit.audit.jsonl - -# Errori -jq -c 'select(.error)' /var/log/cerbero-mcp/*.audit.jsonl - -# Operazioni di un principal -jq -c 'select(.principal=="core")' /var/log/cerbero-mcp/*.audit.jsonl -``` - -I secret (api_key, password) sono filtrati automaticamente da -`SecretsFilter` prima di arrivare al sink. - -## 5. Setup iniziale del VPS (manuale, alternativa allo script) - -**Pre-requisiti**: Docker Engine ≥ 24, `docker compose` plugin, accesso SSH -sudo, dominio DNS A record `cerbero-mcp.tielogic.xyz` → IP del VPS, porte 80 -e 443 aperte sul firewall (per ACME challenge + traffico HTTPS). - -### a) Login al registry Gitea - -Crea un Personal Access Token su Gitea (`Settings → Applications → -Generate new token`) con scope `read:package`. Quindi sul VPS: - -```bash -echo "$GITEA_PAT" | docker login git.tielogic.xyz -u --password-stdin -``` - -Le credenziali vengono salvate in `~/.docker/config.json`. Watchtower lo -bind-monta in sola lettura per fare i pull autenticati. - -### b) Crea dir di deploy e scarica i file di config - -Sul VPS NON serve clonare il repo. Bastano i file di compose, il -`Caddyfile` e i public assets del gateway: - -```bash -sudo mkdir -p /docker/cerbero_mcp/{secrets,gateway/public} -sudo chown -R "$USER" /docker/cerbero_mcp -cd /docker/cerbero_mcp - -BASE=https://git.tielogic.xyz/Adriano/Cerbero-mcp/raw/branch/main -curl -fsSL -o docker-compose.prod.yml $BASE/docker-compose.prod.yml -curl -fsSL -o docker-compose.traefik.yml $BASE/docker-compose.traefik.yml -curl -fsSL -o gateway/Caddyfile $BASE/gateway/Caddyfile -curl -fsSL -o gateway/public/index.html $BASE/gateway/public/index.html -curl -fsSL -o gateway/public/status.js $BASE/gateway/public/status.js -curl -fsSL -o gateway/public/style.css $BASE/gateway/public/style.css -``` - -Il VPS NON ha bisogno di buildare; usa `docker-compose.prod.yml` che fa solo -pull dal registry. - -### c) Prepara secrets - -```bash -mkdir -p secrets -# Copia (via scp) i file JSON con cred reali: -# secrets/deribit.json, bybit.json, alpaca.json, hyperliquid.json, -# secrets/macro.json, sentiment.json -# secrets/core.token, observer.token -chmod 600 secrets/* -``` - -### d) `.env` con configurazione runtime - -Crea `/docker/cerbero_mcp/.env`: - -```bash -# Gateway -ACME_EMAIL=adrianodalpastro@tielogic.com -GATEWAY_HTTP_PORT=80 -GATEWAY_HTTPS_PORT=443 -WRITE_ALLOWLIST="127.0.0.1/32 ::1/128 172.16.0.0/12" - -# Image tag — `latest` per auto-update Watchtower, oppure pin a sha-XXXXXXX -IMAGE_TAG=latest -IMAGE_PREFIX=git.tielogic.xyz/adriano/cerbero-mcp - -# Environment exchange (true=testnet, false=mainnet) -DERIBIT_TESTNET=true -BYBIT_TESTNET=true -HYPERLIQUID_TESTNET=true -ALPACA_PAPER=true - -# Watchtower polling interval (sec). 300=5min default. -WATCHTOWER_POLL_INTERVAL=300 -``` - -### e) Avvio - -```bash -docker compose -f docker-compose.prod.yml --env-file .env pull -docker compose -f docker-compose.prod.yml --env-file .env up -d -docker compose -f docker-compose.prod.yml logs -f gateway -``` - -Caddy chiede automaticamente il certificato Let's Encrypt al primo -contatto su `https://cerbero-mcp.tielogic.xyz`. - -## 6. Auto-update via Watchtower - -Watchtower (servizio `watchtower` nel compose) polla il registry ogni -`WATCHTOWER_POLL_INTERVAL` secondi. Se trova un nuovo digest dietro al tag -`:latest` di un container etichettato `com.centurylinklabs.watchtower.enable=true`, -fa: - -1. `docker pull` della nuova image -2. `docker stop` graceful del container vecchio -3. `docker rm` + start del nuovo container con stessa config + secret + volumi -4. Cleanup image vecchia (`WATCHTOWER_CLEANUP=true`) - -I container con label sono: `gateway`, `mcp-deribit`, `mcp-bybit`, -`mcp-hyperliquid`, `mcp-alpaca`, `mcp-macro`, `mcp-sentiment`. Il container -`watchtower` stesso non si auto-aggiorna (per evitare loop). - -### Disabilitare auto-update temporaneamente - -Pin a uno SHA specifico nel `.env`: - -```bash -IMAGE_TAG=sha-6b7b3f7 -docker compose -f docker-compose.prod.yml --env-file .env up -d -``` - -In questo modo `:latest` non viene più seguito; per riattivare il rollover -automatico ripristina `IMAGE_TAG=latest`. - -### Disabilitare auto-update per un singolo servizio - -Rimuovi la label `com.centurylinklabs.watchtower.enable=true` per quel -servizio nel compose (oppure imposta `=false`). Watchtower lo ignora ma -continua a tenere aggiornati gli altri. - -## 7. Rollback - -```bash -# Trova lo SHA della versione precedente -docker images "git.tielogic.xyz/adriano/cerbero-mcp/*" --format "{{.Tag}}" - -# Pin nel .env -IMAGE_TAG=sha-XXXXXXX - -docker compose -f docker-compose.prod.yml --env-file .env up -d -``` - -Watchtower NON downgraderà perché il digest del tag pin corrisponde a quello -locale. - -## 8. Smoke test post-deploy - -```bash -# Da fuori VPS (laptop) -curl -s https://cerbero-mcp.tielogic.xyz/mcp-macro/health -# {"status":"ok",...} - -# Test write endpoint allowlist (deve rispondere 403 da IP esterno): -curl -X POST https://cerbero-mcp.tielogic.xyz/mcp-deribit/tools/place_order \ - -H "Authorization: Bearer $(cat secrets/core.token)" \ - -d '{"instrument_name":"BTC-PERPETUAL","side":"buy","amount":1}' -# 403 forbidden: source ip not in allowlist ← OK - -# Sul VPS: -GATEWAY=http://localhost bash tests/smoke/run.sh -``` - -## 9. Sicurezza VPS - -- Firewall `ufw`: `allow 22, 80, 443`. Tutto il resto deny in. -- `fail2ban` su SSH e (opz) sul log Caddy 401. -- Secret rotation manuale: aggiorna i file `secrets/*.token` → - `docker compose restart` (i token vengono ricaricati al boot di ogni - servizio MCP). -- Audit log in `docker compose logs | grep audit_event` — per - produzione meglio redirezionare a syslog o a un servizio dedicato. - -## 10. Note Traefik / reverse proxy davanti a Gitea - -Gitea è esposto via Traefik (ROOT_URL `https://git.tielogic.xyz`). Per il push -di image Docker il reverse proxy deve consentire upload di body grossi (un -singolo layer può superare i 100MB). - -Traefik default va bene, ma se vedi `413 Request Entity Too Large` durante -`docker push` aumenta il limite nel middleware: - -```yaml -# traefik dynamic config -http: - middlewares: - gitea-upload: - buffering: - maxRequestBodyBytes: 524288000 # 500MB -``` - -Applicalo come middleware al router Gitea. - -## 11. Aggiornamento del compose stesso (file YAML) - -Watchtower aggiorna le **image**, non `docker-compose.prod.yml` né -`Caddyfile`. Se cambi struttura (nuovi servizi, nuove env var, modifiche -al gateway), ri-esegui sul VPS lo script no-clone, che ri-scarica i file -di config dal branch `main` di Gitea e applica: - -```bash -/tmp/deploy-noclone.sh -``` - -Lo script è idempotente: preserva `.env` e `secrets/`, aggiorna solo i -file di config + fa `pull` + `up -d`. diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml deleted file mode 100644 index 0902643..0000000 --- a/docker-compose.prod.yml +++ /dev/null @@ -1,205 +0,0 @@ -# docker-compose.prod.yml — deploy su VPS produzione. -# -# Differenze vs docker-compose.yml (dev): -# - Niente `build:`, solo `image:` dal registry Gitea. -# - Tag `latest` (Watchtower polla per nuove versioni). -# - Aggiunge servizio `watchtower` che auto-aggiorna i container etichettati -# `com.centurylinklabs.watchtower.enable=true` quando il tag latest cambia. -# - Auth registry: `docker login git.tielogic.xyz` una sola volta sull'host -# (Watchtower legge ~/.docker/config.json bind-mounted in /config.json). -# -# Uso sul VPS: -# docker login git.tielogic.xyz -# docker compose -f docker-compose.prod.yml --env-file .env up -d -# -# Override variabili in `.env` accanto al compose: -# ACME_EMAIL=adrianodalpastro@tielogic.com -# WRITE_ALLOWLIST="127.0.0.1/32 ::1/128 172.16.0.0/12" -# GATEWAY_HTTP_PORT=80 -# GATEWAY_HTTPS_PORT=443 -# IMAGE_TAG=latest # o sha-XXXXXXX per pin specifico - -networks: - internal: - driver: bridge - -volumes: - caddy-data: - caddy-config: - -secrets: - deribit_credentials: - file: ./secrets/deribit.json - hyperliquid_wallet: - file: ./secrets/hyperliquid.json - bybit_credentials: - file: ./secrets/bybit.json - alpaca_credentials: - file: ./secrets/alpaca.json - macro_credentials: - file: ./secrets/macro.json - sentiment_credentials: - file: ./secrets/sentiment.json - core_token: - file: ./secrets/core.token - observer_token: - file: ./secrets/observer.token - -x-common-security: &common-security - cap_drop: [ALL] - security_opt: - - no-new-privileges:true - restart: unless-stopped - networks: [internal] - labels: - com.centurylinklabs.watchtower.enable: "true" - volumes: - - ${AUDIT_LOG_DIR:-/var/log/cerbero-mcp}:/var/log/cerbero-mcp:rw - -x-image-prefix: &image_prefix git.tielogic.xyz/adriano/cerbero-mcp - -services: - gateway: - image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/gateway:${IMAGE_TAG:-latest} - restart: unless-stopped - networks: [internal] - security_opt: - - no-new-privileges:true - labels: - com.centurylinklabs.watchtower.enable: "true" - ports: - - "${GATEWAY_HTTP_PORT:-80}:80" - - "${GATEWAY_HTTPS_PORT:-443}:443" - environment: - ACME_EMAIL: ${ACME_EMAIL:-adrianodalpastro@tielogic.com} - WRITE_ALLOWLIST: ${WRITE_ALLOWLIST:-127.0.0.1/32 ::1/128 172.16.0.0/12} - volumes: - - ./gateway/Caddyfile:/etc/caddy/Caddyfile:ro - - ./gateway/public:/srv:ro - - caddy-data:/data - - caddy-config:/config - depends_on: - mcp-deribit: { condition: service_healthy } - mcp-hyperliquid: { condition: service_healthy } - mcp-bybit: { condition: service_healthy } - mcp-alpaca: { condition: service_healthy } - mcp-macro: { condition: service_healthy } - mcp-sentiment: { condition: service_healthy } - healthcheck: - test: ["CMD", "wget", "-q", "--spider", "http://localhost/"] - interval: 30s - timeout: 5s - retries: 3 - - mcp-deribit: - image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-deribit:${IMAGE_TAG:-latest} - <<: *common-security - user: "1000:1000" - read_only: true - tmpfs: - - /tmp:rw,size=64M,mode=1777 - secrets: [deribit_credentials, core_token, observer_token] - environment: - CREDENTIALS_FILE: /run/secrets/deribit_credentials - CORE_TOKEN_FILE: /run/secrets/core_token - OBSERVER_TOKEN_FILE: /run/secrets/observer_token - DERIBIT_TESTNET: "${DERIBIT_TESTNET:-true}" - ROOT_PATH: /mcp-deribit - AUDIT_LOG_FILE: /var/log/cerbero-mcp/deribit.audit.jsonl - - mcp-hyperliquid: - image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-hyperliquid:${IMAGE_TAG:-latest} - <<: *common-security - user: "1000:1000" - read_only: true - tmpfs: - - /tmp:rw,size=64M,mode=1777 - secrets: [hyperliquid_wallet, core_token, observer_token] - environment: - HYPERLIQUID_WALLET_FILE: /run/secrets/hyperliquid_wallet - CORE_TOKEN_FILE: /run/secrets/core_token - OBSERVER_TOKEN_FILE: /run/secrets/observer_token - HYPERLIQUID_TESTNET: "${HYPERLIQUID_TESTNET:-true}" - ROOT_PATH: /mcp-hyperliquid - AUDIT_LOG_FILE: /var/log/cerbero-mcp/hyperliquid.audit.jsonl - - mcp-bybit: - image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-bybit:${IMAGE_TAG:-latest} - <<: *common-security - user: "1000:1000" - read_only: true - tmpfs: - - /tmp:rw,size=64M,mode=1777 - secrets: [bybit_credentials, core_token, observer_token] - environment: - BYBIT_CREDENTIALS_FILE: /run/secrets/bybit_credentials - CORE_TOKEN_FILE: /run/secrets/core_token - OBSERVER_TOKEN_FILE: /run/secrets/observer_token - BYBIT_TESTNET: "${BYBIT_TESTNET:-true}" - ROOT_PATH: /mcp-bybit - AUDIT_LOG_FILE: /var/log/cerbero-mcp/bybit.audit.jsonl - PORT: "9019" - - mcp-alpaca: - image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-alpaca:${IMAGE_TAG:-latest} - <<: *common-security - user: "1000:1000" - read_only: true - tmpfs: - - /tmp:rw,size=64M,mode=1777 - secrets: [alpaca_credentials, core_token, observer_token] - environment: - ALPACA_CREDENTIALS_FILE: /run/secrets/alpaca_credentials - CORE_TOKEN_FILE: /run/secrets/core_token - OBSERVER_TOKEN_FILE: /run/secrets/observer_token - ALPACA_PAPER: "${ALPACA_PAPER:-true}" - ROOT_PATH: /mcp-alpaca - AUDIT_LOG_FILE: /var/log/cerbero-mcp/alpaca.audit.jsonl - PORT: "9020" - - mcp-macro: - image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-macro:${IMAGE_TAG:-latest} - <<: *common-security - user: "1000:1000" - read_only: true - tmpfs: - - /tmp:rw,size=64M,mode=1777 - secrets: [macro_credentials, core_token, observer_token] - environment: - MACRO_CREDENTIALS_FILE: /run/secrets/macro_credentials - CORE_TOKEN_FILE: /run/secrets/core_token - OBSERVER_TOKEN_FILE: /run/secrets/observer_token - ROOT_PATH: /mcp-macro - - mcp-sentiment: - image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-sentiment:${IMAGE_TAG:-latest} - <<: *common-security - user: "1000:1000" - read_only: true - tmpfs: - - /tmp:rw,size=64M,mode=1777 - secrets: [sentiment_credentials, core_token, observer_token] - environment: - SENTIMENT_CREDENTIALS_FILE: /run/secrets/sentiment_credentials - CORE_TOKEN_FILE: /run/secrets/core_token - OBSERVER_TOKEN_FILE: /run/secrets/observer_token - ROOT_PATH: /mcp-sentiment - - # ======================================================== - # WATCHTOWER — auto-update container con label enable=true - # ======================================================== - watchtower: - image: containrrr/watchtower:latest - restart: unless-stopped - networks: [internal] - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ${HOME}/.docker/config.json:/config.json:ro - environment: - WATCHTOWER_LABEL_ENABLE: "true" - WATCHTOWER_CLEANUP: "true" - WATCHTOWER_POLL_INTERVAL: "${WATCHTOWER_POLL_INTERVAL:-300}" - WATCHTOWER_INCLUDE_RESTARTING: "false" - WATCHTOWER_NOTIFICATIONS_LEVEL: info - WATCHTOWER_LOG_LEVEL: info - command: --interval ${WATCHTOWER_POLL_INTERVAL:-300} diff --git a/docker-compose.traefik.yml b/docker-compose.traefik.yml deleted file mode 100644 index 5f81303..0000000 --- a/docker-compose.traefik.yml +++ /dev/null @@ -1,60 +0,0 @@ -# docker-compose.traefik.yml — overlay per integrare Cerbero_mcp con un -# Traefik già esistente sull'host (es. lo stesso VPS che ospita Gitea). -# -# USO: -# docker compose -f docker-compose.prod.yml -f docker-compose.traefik.yml \ -# --env-file .env up -d -# -# Differenze vs docker-compose.prod.yml standalone: -# - Gateway Caddy NON ha ports binding host (Traefik è il punto di ingresso -# pubblico su 80/443). -# - Gateway è connesso anche alla network esterna `traefik` (override env -# TRAEFIK_NETWORK se diversa, es. `gitea_traefik-public`). -# - Caddy NON fa auto-TLS — Traefik termina TLS e fa ACME Let's Encrypt. -# Caddy ascolta in chiaro su :80 dentro Docker network. -# - Trusted proxies: Caddy rispetta X-Forwarded-For ricevuto da Traefik -# per il match `remote_ip` (rate limit + WRITE_ALLOWLIST). -# - Labels Traefik su gateway: routing Host(`cerbero-mcp.tielogic.xyz`) + -# TLS automatic. -# -# Variabili .env aggiuntive richieste: -# TRAEFIK_NETWORK=gitea_traefik-public # nome network di Traefik -# TRAEFIK_CERTRESOLVER=letsencrypt # nome resolver in tua config Traefik -# TRAEFIK_ENTRYPOINT=websecure # entrypoint HTTPS Traefik - -networks: - traefik: - external: true - name: ${TRAEFIK_NETWORK:-gitea_traefik-public} - -services: - gateway: - # Override: niente port binding host, traffica solo via Traefik - ports: !reset [] - networks: - - internal - - traefik - environment: - ACME_EMAIL: ${ACME_EMAIL:-adrianodalpastro@tielogic.com} - WRITE_ALLOWLIST: ${WRITE_ALLOWLIST:-127.0.0.1/32 ::1/128 172.16.0.0/12} - # Mode behind-proxy: Caddy ascolta plain HTTP su :80, no auto_https - LISTEN: ":80" - AUTO_HTTPS: "off" - # Traefik è il proxy che inoltra; trusta range privati + opz. CIDR Traefik - TRUSTED_PROXIES: ${TRUSTED_PROXIES:-private_ranges} - labels: - com.centurylinklabs.watchtower.enable: "true" - traefik.enable: "true" - traefik.docker.network: ${TRAEFIK_NETWORK:-gitea_traefik-public} - traefik.http.routers.cerbero-mcp.rule: "Host(`cerbero-mcp.tielogic.xyz`)" - traefik.http.routers.cerbero-mcp.entrypoints: ${TRAEFIK_ENTRYPOINT:-websecure} - traefik.http.routers.cerbero-mcp.tls: "true" - traefik.http.routers.cerbero-mcp.tls.certresolver: ${TRAEFIK_CERTRESOLVER:-letsencrypt} - traefik.http.services.cerbero-mcp.loadbalancer.server.port: "80" - # Security headers a livello Traefik (ridondante con Caddy ma utile se - # in futuro Caddy viene rimosso). Commenta se non vuoi duplicazione. - traefik.http.routers.cerbero-mcp.middlewares: cerbero-mcp-secheaders@docker - traefik.http.middlewares.cerbero-mcp-secheaders.headers.stsSeconds: "31536000" - traefik.http.middlewares.cerbero-mcp-secheaders.headers.stsIncludeSubdomains: "true" - traefik.http.middlewares.cerbero-mcp-secheaders.headers.contentTypeNosniff: "true" - traefik.http.middlewares.cerbero-mcp-secheaders.headers.referrerPolicy: "no-referrer"