---
id: server-architecture/016
title: Split PUBLIC_BASE_URL into EMAIL_VERIFY_BASE_URL and LINK_CODE_BASE_URL
epic: server-architecture
state: done
priority: 2
blocked_by: []
estimate: S
created: 2026-08-13
updated: 2026-08-18
claimed_by: null
claimed_at: null
delivers: []
review_artifact: land-of-lor/docs/handoff/historic/016-base-url-split.md
---

## What this is

The follow-up task 012 flagged and deliberately did not fix. The director built
two different public links from a single `PUBLIC_BASE_URL`:

- **verify / password-reset links**, which must reach the director itself —
  exposed by Caddy only under `/director/*` on `lor-server.cocreations.com.au`;
- **the link-code QR** ("attach this device"), which must reach the **web
  client**, served from the workspace site at `lor.kris.ai-task-runner.com/play/`.

Those are different hosts, so one value could never be right for both. Setting
the variable to the director's origin (012's choice, correct for the
security-relevant links) made the QR resolve to `…/director/play?link=…`, which
nothing serves.

## What was done

Two variables replace the one, each falling back *specific → `PUBLIC_BASE_URL`
→ `http://localhost:8081`*, so an older env file behaves exactly as before:

- `EMAIL_VERIFY_BASE_URL` — `emailVerifyBaseUrl()` in `director/src/email.ts`,
  feeding `buildVerifyUrl` / `buildResetUrl`.
- `LINK_CODE_BASE_URL` — `linkCodeBaseUrl()` in `director/src/server.ts`,
  feeding the `create_link_code` URL and its QR PNG.

`game.env.example` documents both and marks `PUBLIC_BASE_URL` deprecated. The
live droplet env was updated and the service restarted.

## Definition of done

- Both variables read, with the documented fallback chain. ✓
- Live droplet set to `https://lor-server.cocreations.com.au/director` and
  `https://lor.kris.ai-task-runner.com` respectively. ✓
- `verify-live-droplet-wss` and `verify-accounts-e2e` green against the live
  droplet after the change. ✓
- The QR URL resolves: `/play?link=CODE` 308s to `/play/?link=CODE`, 200. ✓

## Assumptions

That the workspace site (`lor.kris.ai-task-runner.com/play/`) is the current
web client, per `land-of-lor/docs/handoff/historic/010-accounts.md`'s deploy section —
**not** `lor-server.cocreations.com.au/play/`, which still serves a stale
`pocs/multiplayer-poc` build. Retiring that stale route is separate work, not
done here.