Runbook: web deployment topology
Reference for every web property Celestial Intelligence deploys — where it’s hosted, how the dev→prod deploy model works, and how the marketing site stays hidden behind a placeholder until launch.
This is a reference runbook (not a step-by-step). Read the relevant section before deploying; steps marked ⚠ change what the public sees.
Properties
Section titled “Properties”| Property | Package | CF Pages project | Domain | Pipelines |
|---|---|---|---|---|
| Marketing site | packages/marketing-site | celestial-marketing | celestialintelligence.co, www. | marketing-dev, marketing-promote, marketing-maintenance |
| Docs | docs/ (docs-site) | celestial-docs | docs.celestialintelligence.co | docs-dev, docs-promote |
| Lens (design gallery) | packages/lens | celestial-lens (-9p1 suffix) | lens.celestialintelligence.co | lens-dev, lens-promote |
| Schema host | packages/schema-api etc. | celestial-schemas | schema.celestialintelligence.co | manual |
| Dashboard | packages/dashboard-server | — (Hetzner 5.161.83.131, systemd) | dashboard.celestialintelligence.co (planned) | deploy-dashboard |
All Pages properties are static-asset deploys via the cloudflare-pages
artifact target. Lens, docs, and marketing each declare a module
(*.ssmod.yaml), composed into platform.prod.ssws.yaml and
platform.dev.ssws.yaml.
The deploy model — dev on push, gated promote
Section titled “The deploy model — dev on push, gated promote”Each web property has two pipelines, broken out per module so they ship independently — never all-or-nothing:
<property>-dev— triggered on every push tomain(path-filtered to that property + its dependencies). Deploys--env=dev→ a preview atdev.<project>.pages.dev. Safe and ungated: a dev deploy is never publicly visible (custom domains stay bound to the production deploy).<property>-promote— manual only. Builds the current commit, pauses on a durable human-approvalwaitstep, then deploys--env=prod. This is the only path to production.
So: pushing main refreshes the dev previews; production is always a
deliberate, human-approved *-promote run. Pushing never changes what
the public sees.
Pipelines
Section titled “Pipelines”| Pipeline | Deploys | Trigger |
|---|---|---|
marketing-dev | Marketing → dev preview | push→main, paths packages/marketing-site/**, packages/components/**, packages/theme/**; + manual |
marketing-promote | Marketing → production | manual (human-gated) |
marketing-maintenance | Toggle the coming-soon placeholder | manual (mode=on / mode=off) |
docs-dev | Docs → dev preview | push→main, paths docs/src/**, docs/public/**, docs/astro.config.mjs, docs/package.json; + manual |
docs-promote | Docs → production | manual (human-gated) |
lens-dev | Lens → dev preview | push→main, paths packages/lens/**, packages/components/**, packages/theme/**; + manual |
lens-promote | Lens → production | manual (human-gated) |
deploy-scribe | Scribe services → production | manual (human-gated) |
deploy-starsystem | Starsystem agent → production | manual (human-gated) |
deploy-starflow | Starflow server → production | manual (human-gated) |
deploy-dashboard | Dashboard server → production | manual (human-gated) |
deploy-migrations | DB migrations → production | manual (human-gated) |
All web pipelines deploy via ss artifact deploy — the cloudflare-pages
target creates the Pages project and binds custom domains, so no
wrangler / CF-credential / provisioning machinery is needed.
The backend tier (deploy-scribe/-starsystem/-starflow/-dashboard/
-migrations) replaced the old monolithic deploy-prod — each module
deploys on its own, never all-or-nothing. It’s manual-only by design:
backend services have no dev-preview equivalent, and a deploy restarts a
live Hetzner service, so the timing should be deliberate. Each runs
starsystem deploy scoped to that module’s services via the
starsystem-deploy step’s services: selector. (Spacecraft and the
ss/sf CLIs aren’t here — they ship as binaries via release-mac, not
as deployed services.)
One Cloudflare zone, celestialintelligence.co, owned entirely by the
infra-domains.tooling-co-dns dns-zone service. Records: @ + www →
celestial-marketing, schema → celestial-schemas, lens →
celestial-lens-9p1, docs → celestial-docs. The registrar is
Porkbun; NS is delegated to Cloudflare (ADR-025). Seven brand-defense
domains are registered at Porkbun and URL-forward to .co — no CF zone.
Apply DNS changes with:
ss deploy --service=infra-domains.tooling-co-dns --env=prod \ --workspace-file=platform.prod.ssws.yamlThe coming-soon gate + maintenance toggle
Section titled “The coming-soon gate + maintenance toggle”marketing-site.ssmod.yaml declares two services on the same
celestial-marketing Pages project:
marketing-site.placeholder— the staticplaceholder/dir (a “coming soon” page,noindex).marketing-site.site— the real built site.
Whichever was last deployed with --env=prod is what the public
domain serves. It’s a deploy swap — no env flag, no code toggle.
- Public domain currently serves: placeholder (pre-launch).
- The real site is continuously refreshed on
dev.celestial-marketing.pages.devbymarketing-devon every push.
⚠ To launch — publish the real site for the first time:
sf run marketing-promote # builds, waits for your approval, deploys prodTo hide / restore the live site (post-launch incidents — “take it
down for a sec”), the marketing-maintenance pipeline is a toggle:
sf run marketing-maintenance --input mode=on # ⚠ hide behind the placeholdersf run marketing-maintenance --input mode=off # restore the live sitemode=on is fast — the placeholder is static, no build. Pre-launch the
site simply sits in the mode=on state (placeholder), and the first
marketing-promote is the launch.
Dev vs. prod deploys
Section titled “Dev vs. prod deploys”--env=dev deploys to the Pages project’s dev branch — a preview at
dev.<project>.pages.dev. Custom domains stay bound to the production
deployment, so a dev deploy is never publicly visible. --env=prod
deploys to the production branch and binds the custom domains.
Manual deploy commands
Section titled “Manual deploy commands”The pipelines are the normal path, but any property can be deployed by
hand. Build, then ss artifact deploy:
npm run build -w packages/lens # → packages/lens/distnpm run build -w docs-site # → docs/distnpm run build -w packages/marketing-site # → packages/marketing-site/dist
node packages/starsystem-cli/dist/cli.js artifact deploy lens.site \ --env=dev --workspace-file=platform.dev.ssws.yaml # or --env=prod / platform.prodss artifact deploy does not require a git push — it ships the local
dist/. Pushing main is what triggers the *-dev pipelines.
Component architecture
Section titled “Component architecture”The marketing site and Lens are static (no framework). UI is built from
@celestial/components — the cel-* Lit web components — which both
import, alongside @celestial/theme for the design tokens. Lens is the
gallery for those components; the marketing site composes them into a
static index.html. Neither ships React. A change under
packages/components/** therefore refreshes both the Lens and marketing
dev previews.