Three small sites, each a Cloudflare Worker serving static assets and deployed through the shared workflow in ship-kit, the way the sync service is. Each has a staging Worker and a production Worker, each on its own custom domain and both named in its wrangler.jsonc; a staging address is the production one with staging. in front, as the app’s is (staging.app.datler.ca, Synced Databases), and a bare wrangler deploy is not configured: every deploy names its environment. The shape is the same as Vodler’s sites, so a fix in one carries to the other.
| app | production | staging | what it is |
|---|---|---|---|
apps/web (@datler/web) | datler.ca, www.datler.ca | staging.datler.ca | the landing page and a small API |
apps/docs | docs.datler.ca | staging.docs.datler.ca | the docs, built by Quartz from the vault |
apps/storybook | components.datler.ca | staging.components.datler.ca | the component kitchen sink (Storybook) |
The landing page
A Vite and TanStack Router site on the app’s own design tokens and the shared component library, so its controls are the app’s (the email box has a visible label). Its words come from the vault’s description of the product (Vision), its pictures are screenshots of Chad’s demo profile, and it carries an Open Graph image for link previews. Static files are served by the assets layer; the Worker runs first only for /api/*, so a page request never wakes it, and any other /api/ path answers 404.
GET /api/release— the latest GitHub release ofjeliroco-multimedia/datler, with a download for each platform whose file the release carries (the.exeinstaller for Windows, the.apkfor Android) and the release notes as escaped HTML, cached for five minutes. A private repository, a release with neither file, or any failure answers that there is no build yet, so the card says a build is coming rather than showing an error.POST /api/waitlist— a signup, relayed to Jeliroco Mail’s subscribe endpoint as appdatler, listwaitlist, with a key kept server-side (JELIROCO_MAIL_KEY). The key is required per environment, so a deploy without it fails naming it. An address that does not look like one is refused (400 invalid); without the Mail address or key the waitlist says it is not configured (503); when Mail cannot take a signup it is kept in an optionalWAITLISTKV namespace if one is bound, and otherwise the visitor is asked to try again (502 temporary).
The docs
apps/docs is a vendored Quartz 4 checkout, outside the pnpm workspace, Biome and Vitest; it installs itself with npm from its own lockfile. Before each build, sync-content.mjs copies the vault’s public notes into content/, with any image a public note names, and writes a homepage that links only published notes; it never changes the vault.
What is public is decided once, by the visibility block in vault.config.json: concept, system, pattern and devlog notes are public; decision, task, audit and index notes are private; a note’s own visibility field comes first (Vault Curation). The script applies that policy itself, because the deploy builds where vault-kit is not installed, and keeps two guards: a visibility rule it does not know stops the build, and wherever vault-kit is on the path, its publish list must match the script’s exactly. A public note that linked a private one would show a link to nothing, which is why public notes never link private ones. The site wears Datler’s mark and the app’s dark palette and fonts. pnpm run build:docs builds it.
Deploying
- Staging: promoting
developontomaindeploys whichever of the three changed, each workflow filtered to its own paths (deploy-web-staging,deploy-docs-staging,deploy-components-staging). - Production: a
site-v*tag deploys all three together (deploy-site-production). Anapp-v*tag deploys the sync service, and a plainv*is kept for the app’s own releases. - One app on its own:
gh workflow run "deploy docs (staging)" --ref main. Without GitHub: build, thennpx wrangler deploy --env stagingor--env productionfrom the app’s folder. - Two traps: a
workers.devaddress is behind the account’s Zero Trust Access gate and sends a visitor to a login page, so each site is checked at its custom domain; and an existing DNS record on a hostname blocks the custom-domain claim, so the record is deleted and the deploy run again.
Code
apps/web—src/(the site),worker/index.ts(the API),wrangler.jsonc.apps/docs—sync-content.mjs,quartz.config.ts,quartz.layout.ts,wrangler.jsonc.apps/storybook/wrangler.jsonc;.github/workflows/deploy-*-staging.yml,deploy-site-production.yml.
Related
Storybook · Vault Curation · Vision · Synced Databases · Distribution