[object Object]

← back to Dw Signup Fulfillment

TK-11120: server.js baseUrl() also fails closed on missing PUBLIC_URL (defense-in-depth)

52afecd00f65f9859dd5525eadc12a1a1f56b556 · 2026-09-02 14:01:55 -0700 · Steve Abrams

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017j4qS38tWq21qYdjxMcFTy

Files touched

Diff

commit 52afecd00f65f9859dd5525eadc12a1a1f56b556
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 2 14:01:55 2026 -0700

    TK-11120: server.js baseUrl() also fails closed on missing PUBLIC_URL (defense-in-depth)
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_017j4qS38tWq21qYdjxMcFTy
---
 server.js | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/server.js b/server.js
index 98ddf8b..1b32382 100644
--- a/server.js
+++ b/server.js
@@ -214,7 +214,13 @@ app.get('/verify', async (req, res) => {
 });
 
 // Public base the emailed Approve/Reject buttons point at (Kamatera host at go-live).
-function baseUrl() { return config.PUBLIC_URL || `http://127.0.0.1:${config.PORT}`; }
+// FAIL-CLOSED in LIVE (TK-11120): never fall back to the loopback host in a real send —
+// a localhost link mailed to a human is dead. Only DRY_RUN/dev may use the loopback base.
+function baseUrl() {
+  if (config.PUBLIC_URL) return config.PUBLIC_URL;
+  if (config.DRY_RUN) return `http://127.0.0.1:${config.PORT}`;
+  return '';
+}
 
 // Send the trade-application review card to the office inbox via George.
 async function notifyTradeApplication(application) {

← a6edf22 TK-11120: corrected resend tool + apology template (retail-v  ·  back to Dw Signup Fulfillment  ·  auto-data-snapshot: 2026-09-02T14:03:59 (1 data files) — ver e288df7 →