---
id: building-the-game/002
title: The Builder's read side, and the hub pages that show every block live
epic: building-the-game
state: review
priority: 1
blocked_by: ["building-the-game/001"]
estimate: L
created: 2026-09-03
updated: 2026-09-03
claimed_by: null
claimed_at: null
delivers: []
review_artifact: builder/README.md
---

## What to do

Stand up `builder/` (TypeScript, the director's toolchain, its own `package.json`) as a
separate process on the droplet: `lor-builder.service`, Caddy `spellgrove.com/builder/*` →
its port, `/etc/lor/builder.env` (`DATABASE_URL` for `lor_builder`, `GAME_DATABASE_URL` as a
**read-only** Postgres role on `lor_game`, `CONTENT_CHECKOUT=/opt/lor/content`). Bare
`http.createServer`, no framework, the director's test conventions (embedded Postgres).

Public reads, CORS `*`, ETag + 60 s memo (copy the pattern from
`game/director/src/publicMap.ts`, do not import it):
`GET /builder/content/elements.json` (evaluated from the checkout's `game/shell/elements.js`),
`ground.json` (the meanings, via `game/shell/ground_rules.mjs`), `creatures.json` (kinds parsed
from `game/world/entity_factory.gd`, models listed from disk), `world.json` (authored fields
from the game DB, public Areas only), `areas/<slug>.json` (properties, rules, portals, ground —
public Areas only; private ones name/type/size/position).

Site: `src/lib/builderApi.ts`; pages `building/elements.astro` (element table + ground table,
rows lower trigram, columns upper; a cell opens a card), `building/creatures.astro`,
`building/areas/[slug].astro`; the `/building` landing shows live counts; `/world` balls link
to Area pages; the nav's *soon* entries become links.

The local hub (`tools/hub-ui/`) is the offline mirror: its routes and this task's
endpoints should agree on shapes.

## Definition of done

`builder/test/content.test.ts` green; `curl https://spellgrove.com/builder/content/elements.json`
returns the table; changing a name in the checkout shows on `/building/elements` with no site
rebuild; the site build still passes.

## Built (2026-09-03)

`builder/` is live at `spellgrove.com/builder/` (`lor-builder.service`, port 8090, database
`lor_builder`, read-only role `lor_builder_ro` on `lor_game`, `/etc/lor/builder.env`), 13
tests green against embedded Postgres loading the real world through the director's loader.
The site's `/building` landing shows live counts and the public areas; `/building/elements`
draws the 64 twice (elements, ground) with a card per cell; `/building/creatures`;
`/building/areas/<slug>` with the ground drawn from the database; the world map's panel links
to an area's page. Checked in headless Chrome against the live Builder.

Interpretations worth a glance: portals whose far end is private are omitted rather than
blanked; `/health` answers 503 when the game DB is unreachable; an `elements.js` edit shows
within the 60 s memo, not instantly.