Where each part of the system is deployed, what triggers a deploy, and how each environment gets its content and configuration.

Reference Value
Subject Environments, deploy triggers, build configuration and domains; for anyone shipping code or content
Verified source apps/web/vercel.json, flows.json, unchanged since f40e8704; cms.yml at main 7a71e081, last changed by 7d7a0211 (#646), which removed the buildbot-cover-permission lane; its jobs are test, live-diff, push, permission-control-audit and flows-push; apps/web/package.json build script on main at 9dc18495, checked 2026-11-07; deploy-now.yml (#680) at main 6840b0fa and the Railway source (deployment 68cc5432 from yesid-dev/yesid.dev, live 07:55 UTC) checked 2026-11-10; apps/cms/Dockerfile (article editor stage, #693), the web build script and image publishing re-checked on main at 0461e045, checked 2026-11-10
Support state Build/source contracts checked against main 9dc18495. Public requests on November 7, 2026 returned this revision's project redirect and robots tag. Provider settings are separate configuration evidence

Deploy map

flowchart TD
  PR["Pull request to main"] --> PV["Vercel preview<br/>fixture, noindex"]
  PR --> Q["merge queue"] --> M["main"]
  ADM["deploy-now.yml<br/>admin, any ref"] -- "web" --> VP
  ADM -- "cms" --> CP
  M --> VP["Vercel production<br/>yesid.dev, live export"]
  M -- "CMS changes" --> CP["Railway<br/>cms.yesid.dev"]
  CP --> NP[("Neon Postgres")]
  CP --> R2[("Cloudflare R2")]
  CP -- "Publish site<br/>deploy hook" --> VP
  M -- "push" --> LD["cms.yml live-diff<br/>read-only"]

Environments

Environment What it is Content Credentials
Local Developer machine Fixture by default; explicit operator live export None by default
CI GitHub Actions on PRs Fixture None for web; cms read and write lanes use the production token in the production environment
Vercel Preview One deployment per PR to main; this is staging Fixture No build token, no live flag, no analytics; noindex by host
Vercel Production yesid.dev, built from main Live export or the build fails Build token, OpenWeather key, Plausible domain
CMS production One Directus service on Railway Editorial source Directus runtime variables on Railway

Realistic content checks use the fixture, which is refreshed from a live export.

Web deploys (Vercel)

Setting Value
Project config apps/web/vercel.json
Install From the repo root with Bun 1.4.4 through npx, bun install --frozen-lockfile
Build bun run build: exporter pre-hook (on production it also publishes referenced CMS images into static/images/cms/), vite build, the Node SSR runtime check (scripts/check-ssr-runtime.mjs), the emitted page contract test (scripts/check-page-contracts.test.mjs), then the sitemap coverage test
Runtime @sveltejs/adapter-vercel with nodejs22.x for endpoints; pages are prerendered
Rewrites Unknown /og/blog/* and /og/project/* paths serve /og/default.en.png
Headers Security headers on every path; OG images revalidate in browsers and cache a year at the CDN
Triggers Merge to main through the merge queue; the Publish site deploy hook; PR pushes for previews; admin deploy-now.yml with target web or both

The Publish site flow calls the deploy hook with buildCache=false. Saving CMS content does not itself start a build. A production build uses live CMS content, whether triggered by Publish site or a merge to main. A merge after a CMS content write therefore publishes that content when the production build succeeds; the code diff does not bound the content published. Confirm completion and public content in Vercel; build duration varies.

CMS deploys (Railway)

The Railway service builds apps/cms/Dockerfile with apps/cms as its only context and redeploys from main of yesid-dev/yesid.dev (source switched from the old owner on November 10, 2026; deployment 68cc5432 live 07:55 UTC). Admin deploy-now.yml with target cms or both redeploys it at once with the RAILWAY_TOKEN project token. Which paths trigger a redeploy is a Railway setting. Health checks use /server/ping. The image also builds the Article Block Editor interface extension from apps/cms/extensions/article-editor. Schema and configuration changes do not deploy with the image: the four field interface changes that select the editor apply only through the confirmed owner packet (Operations and recovery). The cms.yml push dispatch takes push_scope: full (default; snapshot plus configuration collections) or schema-only (snapshot only, sync:push -- --no-collections) (cms.yml:51). Confirmed production writes use the owner's authenticated browser session and sync maps, followed by the read-only cms diff.

Shipping checklist

Ship Steps
Code PR to main, review the preview, wait for gitleaks, ci (which includes the four E2E shards), test and CodeQL code scanning, then enqueue with gh pr merge --auto --squash; the merge queue re-runs the checks on the combined result and lands it. Vercel builds production with live content. An admin can ship any ref at once with deploy-now.yml (target web, cms or both; build, then deploy)
Content Save in Directus. Publish site or a merge to main triggers a production build from live content. Inspect Vercel and the page; the manual Publish path provides delayed CMS version feedback
Images Upload in Directus, reference the file in published content, then Publish site; the production build publishes raster images. New SVGs and pinned mirrors go through a reviewed code PR (Operations and recovery)
Schema or flows Snapshot PR, post-merge read-only diff, confirmed owner-browser packet, register new configuration maps and read back; operation graphs cannot use the flows-only lane
Design release One adoption PR bumping all @yesid/* URLs, then merge. For a contract change, that PR starts as a draft on the rc.N candidate and switches to the stable release in the same PR after promotion

Domains

Host Serves
yesid.dev Vercel production; the only indexable host along with www
www.yesid.dev Redirects to the apex
cms.yesid.dev Directus; noindex on every response
transit.yesid.dev Separate Transit project; the home hero reads its public KPI API

Cloudflare proxies the apex and www in front of Vercel. On November 10, 2026 yesid.dev resolved to a Cloudflare address (172.64.80.1), and both hosts answered with Server: cloudflare and a CF-RAY header (the apex with x-vercel-cache behind it, www with a 308 to the apex). The analytics relay's visitor-address forwarding (#650, #651) assumes this proxy. Repository source does not establish provider DNS settings; read them at the provider before changing them. Railway's current read-only configuration confirms one production replica, /apps/cms root/watch scope and /server/ping health check.