Skip to content

Runbook: deploy Lens (design-gallery) to Cloudflare Pages

Ships packages/design-gallery (Vite + Lit, rendered through @celestial/lens) as a static SPA to the Cloudflare Pages project celestial-lens, served from https://lens.celestialintelligence.co/.

Pattern: ADR-018 (cloudflare-pages artifact target). Same shape as marketing-site. No backend, CDN-fronted, free tier.

  • ss CLI on PATH (pnpm cli:link from repo root, or installed binary).
  • Cloudflare credentials in the vault — already provisioned for the platform; reused as-is:
    ss vault list-keys cloudflare
    # expected: api_bootstrap_token, dns_token, account_id
  • DNS already wired. The CNAME for lens is declared in infra-domains/infra-domains.ssmod.yaml (service tooling-co-dns) and is currently live:
    dig +short lens.celestialintelligence.co
    # → Cloudflare proxy IPs (104.21.… / 172.67.…)
  • Project name on CF Pages is celestial-lens. CF assigned the .pages.dev subdomain as celestial-lens.pages.dev because the bare celestial-lens was already taken in the global subdomain namespace. The CNAME in tooling-co-dns points to the subdomain (celestial-lens.pages.dev); project_name in packages/design-gallery/lens.ssmod.yaml is the project name (celestial-lens). They’re different fields — don’t conflate.
packages/design-gallery/dist/
pnpm --filter @celestial/design-gallery build
# 2. Ship the dist/ to Cloudflare Pages.
ss artifact deploy lens.site \
--env=prod \
--workspace-file=platform.prod.ssws.yaml

The composed service id is lens.site (module lens, service site).

The artifact target wraps packages/design-gallery/dist with deploy_to: /packages/design-gallery/dist, so the dist directory becomes the site root (i.e. dist/index.html/index.html).

Terminal window
# Apex returns 200 and serves the SPA shell.
curl -sSI https://lens.celestialintelligence.co/ | head -1
# → HTTP/2 200
# Response references the Vite module bundle.
curl -sS https://lens.celestialintelligence.co/ \
| grep -o '<script type="module"[^>]*>'
# → <script type="module" crossorigin src="/assets/index-<hash>.js">
# Bundle itself resolves.
curl -sSI "https://lens.celestialintelligence.co/$(
curl -sS https://lens.celestialintelligence.co/ \
| grep -oE '/assets/index-[A-Za-z0-9_-]+\.js' | head -1
)" | head -1
# → HTTP/2 200

Open https://lens.celestialintelligence.co/ in a browser — Lens should render the cel-* story gallery (sidebar + center stage). Theme switcher in the top-right should swap palettes.

CF Pages keeps every deployment. To roll back:

Terminal window
# List deployments.
ss vault get-key cloudflare api_bootstrap_token --raw \
| xargs -I{} curl -sS -H "Authorization: Bearer {}" \
"https://api.cloudflare.com/client/v4/accounts/$(ss vault get-key cloudflare account_id --raw)/pages/projects/celestial-lens/deployments" \
| jq '.result[] | {id, created_on, url}'

Or use the CF dashboard → Pages → celestial-lens → Deployments → “Rollback to this deployment”.

DNS check (only if smoke fails with cert / NXDOMAIN)

Section titled “DNS check (only if smoke fails with cert / NXDOMAIN)”
Terminal window
dig +short lens.celestialintelligence.co
# Expect Cloudflare proxy IPs.
# To re-reconcile the zone if a record drifted:
ss deploy --service=infra-domains.tooling-co-dns \
--env=prod --workspace-file=platform.prod.ssws.yaml

The custom domain on the Pages project (lens.celestialintelligence.co) is added automatically by the cloudflare-pages artifact target on every deploy — no manual binding needed.