Skip to content

feat(deploy): production on tablo.run + preview-<N>.tablo.run subdomains#18

Merged
i11v merged 2 commits into
mainfrom
worktree-tablo-run-domain
Jun 19, 2026
Merged

feat(deploy): production on tablo.run + preview-<N>.tablo.run subdomains#18
i11v merged 2 commits into
mainfrom
worktree-tablo-run-domain

Conversation

@i11v

@i11v i11v commented Jun 19, 2026

Copy link
Copy Markdown
Owner

What

Moves tablo's production custom domain to the apex tablo.run and gives every PR preview its own preview-<N>.tablo.run hostname (previews were workers.dev-only before). Also lands the @distilled.cloud/core patch that makes custom-domain deletes (preview teardown + the production cutover itself) survive a beta CF-client bug.

Why two commits, in this order

  1. fix(deploy): patch @distilled.cloud/core empty-body DELETE crash — Cloudflare answers DELETE /workers/domains/{id} with an empty 200 body; the beta CF client falls through to envelope-schema decode, fails, and aborts the whole deploy/destroy with the misleading CloudflareHttpError: null, orphaning the worker + its state. This fires on every custom-domain teardown (the PR-close cleanup job) and on any production domain switch (reconcileDomains deletes the old domain). Patched via bun patch to treat an empty/null 2xx body as no-content, mirroring the client's own 204 branch. bun install --frozen-lockfile re-applies it on every CI runner. Drop once upstream alchemy-run/distilled#344 ships in a released @distilled.cloud/core.

  2. feat(deploy): serve production on tablo.run, previews on preview-<N>.tablo.run — new workerDomain() helper co-located with workerName(): production → tablo.run, pr-<N> → preview-<N>.tablo.run, everything else → none. The worker name keeps the pr- scheme (tablo-pr-<N>); only the public hostname reads preview-. Both workers.dev URLs stay live via url: true, so the health smoke test keeps hitting the deterministic workers.dev URL while the PR comment now advertises the custom domain (with workers.dev as a fallback line).

The patch lands first so the production cutover — which detaches tablo.i11v.com — and every preview teardown are both safe.

Deploy / cutover

Merging triggers the existing CI prod deploy (--stage production), which attaches tablo.run and detaches tablo.i11v.com (replace, not keep-both). The tablo.run zone already exists in the account, so Cloudflare auto-provisions the proxied DNS record + TLS cert — no per-deploy dashboard or DNS steps.

One manual prerequisite (dashboard)

tablo.run is apex and Workers Custom Domains create the apex DNS record. Before merging, confirm the tablo.run zone has no conflicting proxied A/AAAA/CNAME at the apex (a parking/placeholder record), or the first prod deploy's domain attach will fail. preview-<N>.tablo.run subdomains are covered by Universal SSL's *.tablo.run.

Verification

typecheck clean, 79/79 unit tests pass (incl. 4 new workerDomain cases), and the patch was confirmed to re-apply on a clean bun install --frozen-lockfile with alchemy resolving to the patched copy. Live domain provisioning + teardown against the Cloudflare API only run in CI — this PR's own preview deploy exercises a preview-<N>.tablo.run attach, and closing it exercises the patched teardown.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Gp4ZeUYKtUbPkhjM8Gb4TN

i11v and others added 2 commits June 19, 2026 11:37
Cloudflare answers DELETE /workers/domains/{id} with an empty 200 body.
The beta CF client falls through to envelope-schema decode, fails, and
aborts the whole deploy/destroy with the misleading
`CloudflareHttpError: null` — orphaning the worker and its state. This
fires on every custom-domain teardown (the PR-close cleanup job) and on
any production domain switch (reconcileDomains deletes the old domain).

Patch the client (via `bun patch`) to treat an empty/null 2xx body as
no-content, mirroring its own 204 branch. `bun install --frozen-lockfile`
re-applies it on every CI runner. Drop once the upstream fix
alchemy-run/distilled#344 ships in a released @distilled.cloud/core.

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

Move the production custom domain from tablo.i11v.com to the apex
tablo.run, and give every PR preview its own preview-<N>.tablo.run
hostname (previously workers.dev only). The hostname is derived from the
deploy stage by a new workerDomain() helper co-located with workerName():
production -> tablo.run, pr-<N> -> preview-<N>.tablo.run, everything else
-> none. The worker name keeps the pr- scheme (tablo-pr-<N>); only the
public hostname reads preview-.

Both workers.dev URLs stay live via `url: true`, so the health smoke test
keeps hitting the deterministic workers.dev URL while the PR comment now
advertises the custom domain (workers.dev as a fallback line).

Switching production's domain detaches tablo.i11v.com on the next prod
deploy; that delete is made safe by the @distilled.cloud/core patch in the
preceding commit. The tablo.run zone already exists in the account, so
Cloudflare auto-provisions DNS + TLS on deploy.

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

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown

🚀 Preview deployed: https://preview-18.tablo.run
fallback: https://tablo-pr-18.i11v.workers.dev

@i11v i11v closed this Jun 19, 2026
@i11v i11v reopened this Jun 19, 2026
@i11v i11v merged commit 70b41aa into main Jun 19, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant