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 |
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"]
| 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.
| 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.
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.
| 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 |
| 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.