From e719c2d239f6dad38a28a9edfa88fa530ca76d7d Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Mon, 9 Nov 2026 14:48:29 -0800 Subject: [PATCH] fix(trigger): keep each worker's egress gateway address out of the env sync The Trigger.dev env sync copies the app's OUTBOUND_GATEWAYS over the worker value. The app reaches a gateway through the VPC load balancer; workers reach it through their own private endpoint, so the copied URL is unreachable from a run and every gateway-routed request in a background run fails with GATEWAY_UNAVAILABLE after two connect timeouts. OUTBOUND_GATEWAYS is now Trigger-owned like REDIS_URL; the shared credentials and routing source still sync. --- .../runtime-secrets/src/trigger-env.test.ts | 19 ++++++++++++++++++- packages/runtime-secrets/src/trigger-env.ts | 3 +++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/runtime-secrets/src/trigger-env.test.ts b/packages/runtime-secrets/src/trigger-env.test.ts index 14432c12dfa..de54bd0fed0 100644 --- a/packages/runtime-secrets/src/trigger-env.test.ts +++ b/packages/runtime-secrets/src/trigger-env.test.ts @@ -1,5 +1,22 @@ import { describe, expect, it } from 'vitest' -import { parseTriggerEnvSyncTargets } from './trigger-env' +import { buildTriggerEnvVars, parseTriggerEnvSyncTargets } from './trigger-env' + +describe('buildTriggerEnvVars', () => { + it('leaves the worker-specific network endpoints to the Trigger.dev environment', () => { + const synced = buildTriggerEnvVars({ + REDIS_URL: 'rediss://vpc-redis:6379', + INTERNAL_API_BASE_URL: 'http://app.internal', + OUTBOUND_GATEWAYS: '{"gw":{"url":"https://vpc-lb:8443"}}', + OUTBOUND_GATEWAY_CREDENTIALS: '{"gw":{"token":"t"}}', + OUTBOUND_ROUTING_SOURCE: 'appconfig', + }).map((v) => v.name) + expect(synced).not.toContain('REDIS_URL') + expect(synced).not.toContain('INTERNAL_API_BASE_URL') + expect(synced).not.toContain('OUTBOUND_GATEWAYS') + expect(synced).toContain('OUTBOUND_GATEWAY_CREDENTIALS') + expect(synced).toContain('OUTBOUND_ROUTING_SOURCE') + }) +}) describe('parseTriggerEnvSyncTargets', () => { it('reads an exact comma list', () => { diff --git a/packages/runtime-secrets/src/trigger-env.ts b/packages/runtime-secrets/src/trigger-env.ts index c85d274b201..7188ca0f583 100644 --- a/packages/runtime-secrets/src/trigger-env.ts +++ b/packages/runtime-secrets/src/trigger-env.ts @@ -26,6 +26,9 @@ const TRIGGER_OWNED_KEYS: ReadonlySet = new Set([ 'REDIS_TLS_SERVERNAME', // A VPC-internal base URL is unreachable from Trigger.dev's cloud. 'INTERNAL_API_BASE_URL', + // The app dials an egress gateway through the VPC load balancer; workers dial it through their + // own endpoint, so each gateway's `url` differs. The credentials and routing source are shared. + 'OUTBOUND_GATEWAYS', // Schema-migration credential: runs never migrate, so they never hold it. 'MIGRATION_DATABASE_URL', // `syncEnvVars` refuses to sync it, so it can only ever be set by hand.