From ac1180326bfdff4d3e89285467a543e1e3e6ba6d Mon Sep 17 00:00:00 2001 From: pedrofrxncx Date: Fri, 24 Jul 2026 15:18:53 -0300 Subject: [PATCH] fix(settings): treat bracketed IPv6 loopback DATABASE_URL/NATS_URL as local --- apps/api/src/settings/resolve-config.test.ts | 28 ++++++++++++++++++++ apps/api/src/settings/resolve-config.ts | 4 ++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/apps/api/src/settings/resolve-config.test.ts b/apps/api/src/settings/resolve-config.test.ts index 22e300e49b..f26a75c486 100644 --- a/apps/api/src/settings/resolve-config.test.ts +++ b/apps/api/src/settings/resolve-config.test.ts @@ -218,6 +218,34 @@ describe("resolveConfig Studio environment aliases", () => { }); }); +describe("resolveConfig external database/nats URL detection", () => { + it("treats a bracketed IPv6 loopback DATABASE_URL as local, not external", () => { + const result = resolveConfig(flags, { + DATABASE_URL: "postgres://[::1]:5432/postgres", + }); + + expect(result.externalDatabaseUrl).toBeNull(); + }); + + it("treats a bracketed IPv6 loopback NATS_URL as local, not external", () => { + const result = resolveConfig(flags, { + NATS_URL: "nats://[::1]:4222", + }); + + expect(result.externalNatsUrl).toBeNull(); + }); + + it("still treats a genuinely remote host as external", () => { + const result = resolveConfig(flags, { + DATABASE_URL: "postgres://db.example.com:5432/postgres", + }); + + expect(result.externalDatabaseUrl).toBe( + "postgres://db.example.com:5432/postgres", + ); + }); +}); + describe("resolveConfig NODE_ENV", () => { it("defaults to development when unset", () => { const result = resolveConfig(flags, {}); diff --git a/apps/api/src/settings/resolve-config.ts b/apps/api/src/settings/resolve-config.ts index c0d8b2909f..6c9fc0c3b4 100644 --- a/apps/api/src/settings/resolve-config.ts +++ b/apps/api/src/settings/resolve-config.ts @@ -123,7 +123,9 @@ function externalUrlOrNull(url: string | undefined): string | null { if (!url) return null; try { const parsed = new URL(url); - const host = parsed.hostname; + // `URL#hostname` keeps the brackets for a bracketed IPv6 host (e.g. + // "[::1]"), so strip them before comparing against the bare loopback form. + const host = parsed.hostname.replace(/^\[|\]$/g, ""); if (host === "localhost" || host === "127.0.0.1" || host === "::1") { return null; }