From 6f19e8a84dab0117748ab88d20604e0197951f70 Mon Sep 17 00:00:00 2001 From: Guilherme Gomes Date: Thu, 16 Apr 2026 13:18:40 -0300 Subject: [PATCH 1/6] fix(compose): default BOT_RUNTIME_SECRET on evo-bot-runtime service MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align evo-bot-runtime with evo-crm and evo-crm-sidekiq, which already fall back to the shared dev secret when BOT_RUNTIME_SECRET is absent from .env. Previously the bot-runtime depended solely on env_file, so a .env copied from an older template left it with an empty secret — every event from the CRM then failed the X-Bot-Runtime-Secret check with pipeline.auth.unauthorized and the AI silently never replied. --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 7b0d11a..64e2748 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -245,6 +245,7 @@ services: LISTEN_ADDR: 0.0.0.0:8080 REDIS_URL: redis://:${REDIS_PASSWORD:-evoai_redis_pass}@redis:6379 AI_PROCESSOR_URL: http://evo-processor:8000 + BOT_RUNTIME_SECRET: ${BOT_RUNTIME_SECRET:-evo-bot-runtime-dev-secret} depends_on: redis: condition: service_healthy From 8b6fa89ac82670dfa1db873f2464804fce4a66b9 Mon Sep 17 00:00:00 2001 From: Guilherme Gomes Date: Thu, 16 Apr 2026 13:48:41 -0300 Subject: [PATCH 2/6] feat(gateway): make upstream service names configurable via env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gateway nginx config had the five backend service names (evo_auth, evo_crm, evo_core, evo_processor, evo_bot_runtime) baked into the image, which broke any deployment that uses a different naming convention — requests would hit DNS-NOT-FOUND upstreams and surface as 502s. Replace the hardcoded host:port pairs with ${AUTH_UPSTREAM}, ${CRM_UPSTREAM}, ${CORE_UPSTREAM}, ${PROCESSOR_UPSTREAM} and ${BOT_RUNTIME_UPSTREAM}, rendered at container start by the stock nginx-image envsubst entrypoint. Defaults in the Dockerfile match the previous hardcoded values exactly, so deployments that follow the reference stack keep working with zero changes. NGINX_ENVSUBST_FILTER is scoped to the five *_UPSTREAM names so the rendering pass does not touch nginx's own runtime variables like $host or $request_uri. --- nginx/Dockerfile | 16 +++++++++++++++- nginx/{nginx.conf => default.conf.template} | 14 +++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) rename nginx/{nginx.conf => default.conf.template} (97%) diff --git a/nginx/Dockerfile b/nginx/Dockerfile index 47d11be..4d65ad9 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -1,3 +1,17 @@ FROM nginx:alpine -COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Default upstream targets. Override any of these at deploy time when the +# backend service names in your stack differ from the defaults below. +ENV AUTH_UPSTREAM=evo_auth:3001 \ + CRM_UPSTREAM=evo_crm:3000 \ + CORE_UPSTREAM=evo_core:5555 \ + PROCESSOR_UPSTREAM=evo_processor:8000 \ + BOT_RUNTIME_UPSTREAM=evo_bot_runtime:8080 + +# Limit envsubst to the upstream vars so nginx's own $variables (e.g. $host, +# $request_uri) are not touched at render time. +ENV NGINX_ENVSUBST_FILTER="^(AUTH|CRM|CORE|PROCESSOR|BOT_RUNTIME)_UPSTREAM$" + +COPY default.conf.template /etc/nginx/templates/default.conf.template + EXPOSE 3030 diff --git a/nginx/nginx.conf b/nginx/default.conf.template similarity index 97% rename from nginx/nginx.conf rename to nginx/default.conf.template index fa240f5..86087fa 100644 --- a/nginx/nginx.conf +++ b/nginx/default.conf.template @@ -21,12 +21,16 @@ server { # ============================================================================= # Upstream targets as variables + # + # Hosts/ports are rendered at container start by envsubst from the + # *_UPSTREAM env vars (see Dockerfile for defaults). Override them + # when the backend service names in your stack differ from the defaults. # ============================================================================= - set $auth_service http://evo_auth:3001; - set $evoai_service http://evo_core:5555; - set $crm_service http://evo_crm:3000; - set $processor_service http://evo_processor:8000; - set $bot_runtime_service http://evo_bot_runtime:8080; + set $auth_service http://${AUTH_UPSTREAM}; + set $evoai_service http://${CORE_UPSTREAM}; + set $crm_service http://${CRM_UPSTREAM}; + set $processor_service http://${PROCESSOR_UPSTREAM}; + set $bot_runtime_service http://${BOT_RUNTIME_UPSTREAM}; underscores_in_headers on; From a61cf6cea872cf386951fc7ecc1f3578b15d6a5d Mon Sep 17 00:00:00 2001 From: Guilherme Gomes Date: Thu, 16 Apr 2026 13:51:25 -0300 Subject: [PATCH 3/6] ci(gateway): build and publish image on pushes to develop Adds develop to the trigger branches and emits evoapicloud/evo-crm-gateway:develop plus :develop- so pre-release gateway builds can be pulled and tested before cutting a tag. The main and tag paths are unchanged and :latest stays reserved for main/tag builds. --- .github/workflows/gateway-publish.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gateway-publish.yml b/.github/workflows/gateway-publish.yml index f4f6cf6..6c65ef5 100644 --- a/.github/workflows/gateway-publish.yml +++ b/.github/workflows/gateway-publish.yml @@ -2,7 +2,7 @@ name: "Build & Publish Gateway Image" on: push: - branches: [main] + branches: [main, develop] tags: ["v*.*.*"] paths: - "nginx/**" @@ -95,6 +95,8 @@ jobs: TAGS="${IMAGE}:${VERSION},${IMAGE}:latest" elif [[ "${GH_REF}" == refs/heads/main ]]; then TAGS="${IMAGE}:latest,${IMAGE}:main-${SHA_SHORT}" + elif [[ "${GH_REF}" == refs/heads/develop ]]; then + TAGS="${IMAGE}:develop,${IMAGE}:develop-${SHA_SHORT}" fi echo "tags=${TAGS}" >> "$GITHUB_OUTPUT" From 1f620a97482d9db0c977f1460c0543a972361bc3 Mon Sep 17 00:00:00 2001 From: Guilherme Gomes Date: Thu, 16 Apr 2026 15:29:56 -0300 Subject: [PATCH 4/6] docs(gateway): document the configurable upstream env vars Adds nginx/README.md with the full upstream table, default values, the renaming scenario that motivated the feature, and verification steps. Also leaves a commented-out override block inline in the swarm stack (plus a note in the gateway service) so operators who rename the backend services see the escape hatch without having to dig into the nginx image. --- docker-compose.swarm.yaml | 12 +++++ nginx/README.md | 97 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 nginx/README.md diff --git a/docker-compose.swarm.yaml b/docker-compose.swarm.yaml index 76eb085..cf344d5 100644 --- a/docker-compose.swarm.yaml +++ b/docker-compose.swarm.yaml @@ -30,6 +30,18 @@ services: # --------------------------------------------------------------------------- evo_gateway: image: evoapicloud/evo-crm-gateway:latest + # The gateway routes to the 5 backend services by their docker service + # names. Defaults match the names used in this stack (evo_auth, evo_crm, + # evo_core, evo_processor, evo_bot_runtime). If you rename the services + # in your own stack (e.g. using a different prefix), uncomment and set + # the matching *_UPSTREAM env var — otherwise the gateway cannot resolve + # them and every request returns 502. See nginx/README.md for details. + # environment: + # AUTH_UPSTREAM: my_auth:3001 + # CRM_UPSTREAM: my_crm:3000 + # CORE_UPSTREAM: my_core:5555 + # PROCESSOR_UPSTREAM: my_processor:8000 + # BOT_RUNTIME_UPSTREAM: my_bot_runtime:8080 networks: - network_public deploy: diff --git a/nginx/README.md b/nginx/README.md new file mode 100644 index 0000000..2f47052 --- /dev/null +++ b/nginx/README.md @@ -0,0 +1,97 @@ +# Evo CRM — API Gateway + +Single-entrypoint nginx that dispatches incoming requests to the five +backend services by URL path. Packaged as +`evoapicloud/evo-crm-gateway` and meant to sit behind a TLS-terminating +reverse proxy (Traefik, Caddy, an ALB, etc.). + +## Routing + +Targets are declared as nginx variables at the top of the rendered config: + +| Variable | Receives requests for | +|---------------------------|------------------------------------------------------| +| `$auth_service` | `/oauth`, `/.well-known`, `/setup/*`, `/api/v1/auth`, `/api/v1/users`, `/api/v1/accounts`, `/platform/*`, `/api/v1/super_admin` (most) | +| `$crm_service` | `/cable`, `/webhooks/*`, `/rails/active_storage`, `/api/v1/accounts/:id/webhooks`, `/api/v1/super_admin/{whitelabel,app_configs,upload,agent_bots,installation_configs,account_users}`, catch-all for unmatched `/api/v1/*` and `/` | +| `$evoai_service` | `/api/v1/{agents,folders,mcp-servers,custom-mcp-servers,custom-tools}` | +| `$processor_service` | `/api/v1/{chat,a2a,sessions,tools,clients}`, `/api/v1/agents/:id/integrations`, `/api/v1/integrations/:provider/callback`, `/api/v1/custom-mcp-servers/discover-tools`, `/health`, `/ready` | +| `$bot_runtime_service` | `/api/v1/bot-runtime` | + +Full path matrix is in [`default.conf.template`](./default.conf.template). + +## Configurable upstreams + +Each of the five variables above is rendered at container start from an +environment variable. Defaults match the service names used by the +reference `docker-compose.yaml` and `docker-compose.swarm.yaml`. + +| Env var | Default | Points to | +|-------------------------|-------------------------|-----------| +| `AUTH_UPSTREAM` | `evo_auth:3001` | `evo-auth-service-community` | +| `CRM_UPSTREAM` | `evo_crm:3000` | `evo-ai-crm-community` | +| `CORE_UPSTREAM` | `evo_core:5555` | `evo-ai-core-service-community` | +| `PROCESSOR_UPSTREAM` | `evo_processor:8000` | `evo-ai-processor-community` | +| `BOT_RUNTIME_UPSTREAM` | `evo_bot_runtime:8080` | `evo-bot-runtime` | + +Format is always `host:port`, no scheme. The gateway concatenates `http://` +at the front when rendering. + +### When you need to override + +If your deployment renames any of the backend services (e.g. applying a +`evocrm_` prefix, or shortening to `auth`/`crm`/…) the gateway cannot +resolve the default hostnames and every proxied request returns **502 Bad +Gateway**. Set the matching `*_UPSTREAM` env vars on the gateway service +to the service names you actually used. + +### Example — prefixed service names + +Stack uses `evocrm_auth`, `evocrm_crm`, `evocrm_core`, `evocrm_processor`, +`evocrm_bot_runtime`: + +```yaml +services: + evocrm_gateway: + image: evoapicloud/evo-crm-gateway:latest + environment: + AUTH_UPSTREAM: evocrm_auth:3001 + CRM_UPSTREAM: evocrm_crm:3000 + CORE_UPSTREAM: evocrm_core:5555 + PROCESSOR_UPSTREAM: evocrm_processor:8000 + BOT_RUNTIME_UPSTREAM: evocrm_bot_runtime:8080 +``` + +### Example — non-standard ports + +If you expose the auth service on `4001` instead of `3001`: + +```yaml +environment: + AUTH_UPSTREAM: evo_auth:4001 +``` + +## Verifying the rendered config + +To confirm the variables were substituted correctly, inspect the rendered +file inside a running container: + +```bash +docker exec \ + grep -E "set \$(auth|crm|evoai|processor|bot_runtime)_service" \ + /etc/nginx/conf.d/default.conf +``` + +You should see the expected hostnames. If any line still contains +`${VARNAME}` literally, the env var was not set and envsubst skipped it. + +## Build + +```bash +docker build -t evo-crm-gateway:local nginx/ +``` + +The image uses the stock `nginx:alpine` base and the image's built-in +template rendering — templates in `/etc/nginx/templates/*.template` are +processed by envsubst at container start. `NGINX_ENVSUBST_FILTER` is +restricted to the five `*_UPSTREAM` vars so the substitution pass does +not touch nginx's own runtime variables (`$host`, `$request_uri`, etc.). From d049048a9d9afc96f41579fc74eb80db2e1a672c Mon Sep 17 00:00:00 2001 From: Guilherme Gomes Date: Thu, 16 Apr 2026 16:51:54 -0300 Subject: [PATCH 5/6] chore: remove dependabot config from the monorepo The monorepo wraps git submodules whose own repos already run their own dependency updates. Running dependabot at this layer only produced PRs against committed submodule pointers, never the submodules themselves, which is not useful. --- .github/dependabot.yml | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 84da8d0..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: 2 -updates: - - package-ecosystem: github-actions - directory: / - schedule: - interval: weekly - - - package-ecosystem: docker - directory: / - schedule: - interval: weekly From 1d0a5f3560e959dcf352f5cef59a27bf8ee76f5a Mon Sep 17 00:00:00 2001 From: Guilherme Gomes Date: Thu, 16 Apr 2026 16:55:29 -0300 Subject: [PATCH 6/6] feat: load AI models dynamically from the provider's API Bumps evo-ai-core-service-community and evo-ai-frontend-community to their feat/dynamic-ai-models tips. Together the two bumps wire the agent model picker to GET /api/v1/agents/apikeys/:id/models, which queries the provider's own models endpoint (OpenAI, Anthropic, Gemini, OpenRouter, DeepSeek, Together AI, Fireworks AI) with the stored API key. The frontend falls back to its hardcoded catalog for providers without a public listing endpoint or if the call fails, so nothing regresses for existing stacks. This ends the churn where the hardcoded model list had to be edited every time a provider shipped a new family (the immediate trigger was GPT-5 not appearing). --- evo-ai-core-service-community | 2 +- evo-ai-frontend-community | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evo-ai-core-service-community b/evo-ai-core-service-community index 0dd157c..746adc2 160000 --- a/evo-ai-core-service-community +++ b/evo-ai-core-service-community @@ -1 +1 @@ -Subproject commit 0dd157cb4e5b4505efbf2d4c8785935fe27208fb +Subproject commit 746adc20b9df23cf5860f1233a90c7884d1ebc8f diff --git a/evo-ai-frontend-community b/evo-ai-frontend-community index 4485997..6c2cc86 160000 --- a/evo-ai-frontend-community +++ b/evo-ai-frontend-community @@ -1 +1 @@ -Subproject commit 4485997fedab18df01ca906b20663f6388ddfebc +Subproject commit 6c2cc86e164ffc93adf8a177fe87ab8be39a8ab3