orchestrator.dev architecture

3 min read
architecture astro cloudflare content search

Overview

This post documents how orchestrator.dev is structured: the stack, content model, build pipeline, search, security, and how drafts and deployments work.

Stack

  • Framework: Astro (static output)
  • Styling: Tailwind CSS
  • Content: Markdown collections (blog, portfolio) with Zod schemas
  • Search: Pagefind (static index, client-only bundle)
  • Hosting: Cloudflare Pages (static deploy)
  • Deploy tooling: GitHub Actions (optional), Wrangler CLI only if manually deploying assets
  • Node target: 22.x (matches Cloudflare Pages)

Content model

  • Blog posts live in src/content/blog/ and portfolio items in src/content/portfolio/.
  • Each file uses frontmatter validated by Astro content collections:
    • Required: title, description, pubDate
    • Optional: updatedDate, heroImage, tags, draft
  • Draft gating: draft: true excludes from listings and static paths; an action can also remove drafts before deploy on main.

Branching and workflow

  • main: published content; Cloudflare Pages deploys from here.
  • draft: work-in-progress; safe to keep drafts here or in PRs.
  • Optional GitHub Action filter-drafts.yml removes draft: true content before production deploy on main.

Build and deploy (Pages)

Author markdown

git push

Cloudflare Pages build

Install deps

astro build -> dist/

pagefind --site dist -> dist/pagefind/

Pages deploys dist/ to CDN

Key commands:

  • npm run build -> astro build + npm run pagefind
  • Output: dist/ (static HTML, assets, pagefind bundle)

If using Wrangler manually instead of Pages auto-deploy:

  • npm run build
  • npx wrangler pages deploy dist --project-name orchestrator-dev
  • wrangler.jsonc points Wrangler to ./dist assets.

Runtime request path (static)

pagefind bundlePages static assetsCloudflare CDNUserpagefind bundlePages static assetsCloudflare CDNUserGET https://orchestrator.dev/searchCache miss -> serve /search/index.htmlHTML + assetsCached responseGET /pagefind/pagefind.js (deferred)Search bundle loads index from /pagefind/*
  • Pagefind CLI runs after build to generate dist/pagefind/.
  • In src/pages/search.astro, we load /pagefind/pagefind.js at runtime and call window.pagefind.init(); this avoids bundling the Node package and keeps Vite from resolving it.
  • The index is fully static; no backend is required.

SEO and LLM friendliness

  • JSON-LD via generateJsonLd helpers in BaseLayout.
  • sitemap-index.xml from @astrojs/sitemap.
  • robots.txt in public/.
  • Canonicals, Open Graph, Twitter cards, RSS feed (/rss.xml).

Security and performance

  • Security headers via src/middleware.ts: CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy.
  • Additional headers in public/_headers for Pages.
  • Static output keeps the attack surface minimal; Cloudflare CDN adds edge caching and TLS.

Project layout (high level)

src/
  content/
    blog/            # markdown posts
    portfolio/       # markdown projects
  components/        # Header, Footer, ThemeToggle, etc.
  layouts/           # BaseLayout
  pages/             # routes (blog, portfolio, search, about)
  utils/             # seo.ts helpers
  middleware.ts      # security headers
public/
  images/            # static assets
  _headers           # extra headers for Pages
  robots.txt
dist/                # build output (generated)
dist/pagefind/       # search index (generated)

Local development

npm install
npm run dev
# Build + search index
npm run build

Domain and redirects

  • Primary domain: orchestrator.dev
  • Redirect www -> apex can be set in Cloudflare Pages Redirects (or a _redirects file in public/ if desired).
  • Update astro.config.mjs site and public/robots.txt to match the chosen domain.

Notes on Pages vs. Wrangler

  • Preferred: let Cloudflare Pages deploy the static dist/ (no Worker entry point needed).
  • If using Wrangler CLI, keep wrangler.jsonc with assets.directory: "./dist"; do not expect workers-site/index.js (that is for deprecated Workers Sites).