Publisher

The Publisher is a small dashboard server that turns "content changed" into "site re-rendered and deployed" without you running bun ssg and wrangler deploy by hand. It watches the git branch you publish from, re-renders when a commit lands (or when something signals it), shows you exactly which generated files changed, lets you preview the candidate build, and deploys on a button press.

It exists for the Reepolee-as-data-source setup: a Reepolee app holds the content, and every time an editor saves a record Reepolee pings the Publisher, which re-renders the static site from the fresh data. It works just as well standalone - driven by git commits or a manual button - when there is no Reepolee in the picture.

The Publisher is a development/operations tool you run on a machine that can deploy. It is not part of the served static site.

Running it

bun publisher

That starts the dashboard on PUBLISHER_PORT. Open http://localhost:<PUBLISHER_PORT>/ to see status, the git state, the pending file changes, a candidate-preview link, and a Deploy button.

To force a render against an already-running Publisher (for example from a script):

bun publisher --render

Configuration

The Publisher reads four ports/branch values from .env; all three ports must be distinct from each other:

VariableExampleRole
PUBLISHER_PORT3011Port the dashboard and its API listen on
PUBLISHER_PREVIEW_PORT3012Port the candidate-build preview (Wrangler) is served on
PUBLISHER_BRANCHmainOnly clean commits on this branch are eligible for rendering
PORT3000The dev server's port - used to trigger a live reload after work

PUBLISHER_BRANCH is the safety gate: the Publisher refuses to render a release unless the working tree is clean and HEAD is on that branch, so a half-committed experiment never becomes a candidate build.

How it works

  1. Watch. The runner polls git every couple of seconds. When the clean PUBLISHER_BRANCH head moves - or a render is requested - it schedules a render after a short quiet period (so a burst of commits collapses into one build).
  2. Render. It runs bun run ssg into dist/, then diffs the result against the last deployed snapshot (kept in .deployed/). The dashboard lists exactly which generated files were added, changed, or removed - the candidate, not a guess.
  3. Preview. A candidate preview is served on PUBLISHER_PREVIEW_PORT (via Wrangler, using .dev.vars.release-preview) so you can click through the built site before it goes live.
  4. Deploy. The Deploy button runs bun run cf:deploy (see Cloudflare Workers) and, on success, updates the .deployed/ snapshot so the next diff is measured from what is actually live. Deploy is only enabled when a render is ready and there are changes to ship.

Rendering never touches dist/ destructively on failure: a failed build restores the previous dist/ so a broken commit can't leave you with a half-generated site.

Dashboard API

The dashboard is backed by a tiny HTTP API, which is also what a paired Reepolee app talks to:

Method + pathPurpose
GET /The HTML dashboard
GET /api/healthLiveness + current status
GET /api/stateFull runner view (status, branch, head, changes, output) as JSON
POST /api/render-signalRequest a render (debounced); returns 202. This is the signal endpoint.
POST /api/renderForce a render now (dashboard button)
POST /api/deployRun cf:deploy and snapshot the deployment

Wiring a Reepolee app to signal it

A Reepolee app can auto-trigger a re-render whenever content changes. Point Reepolee at the Publisher's signal endpoint in the Reepolee .env:

REEWEB_PUBLISHER_URL=http://localhost:3011/api/render-signal

With that set, Reepolee's per-route publisher-signal middleware fires a best-effort POST to the endpoint after every successful mutation (a create/update/delete that returns 201, 204, or a redirect). The signal is fire-and-forget with a 1-second timeout: if the Publisher is down, Reepolee logs a warning and the user's request is unaffected. Leave REEWEB_PUBLISHER_URL unset to disable signalling entirely.

The result is a hands-off loop: an editor saves a record in the Reepolee admin → Reepolee signals the Publisher → the Publisher re-renders the static site from the updated data → you review the candidate and deploy (or wire deploy into the same flow). See ReeWeb & Reepolee for the data side of that pairing.