Deployment and DevOps
**Referenced Files in This Document** - [README.md](file://README.md) - [package.json](file://package.json) - [.eleventy.js](file://.eleventy.js) - [worker.js](file://worker.js) - [wrangler.jsonc](file://wrangler.jsonc) - [cloudflare-pages.toml](file://cloudflare-pages.toml) - [netlify.toml](file://netlify.toml) - [src/admin/config.yml](file://src/admin/config.yml) - [tina/config.ts](file://tina/config.ts)Table of Contents
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendices
Introduction
This document provides comprehensive deployment and operational procedures for the platform. It explains the Cloudflare Workers deployment model, the Eleventy static build pipeline, Pagefind indexing, environment and secret management, and the continuous deployment workflow. It also covers monitoring, maintenance, scaling, and best practices for managing multiple environments.
Project Structure
The project is a hybrid static site with dynamic routing via a Cloudflare Worker:
- Eleventy builds static HTML into the _site/ directory.
- Pagefind runs post-build to add a client-side search index under _site/pagefind/.
- The Cloudflare Worker serves as the runtime for member authentication, OAuth, and APIs, while delegating all other requests to the static assets bound as ASSETS.
graph TB
Dev["Developer"] --> NPM["NPM Scripts<br/>package.json"]
NPM --> Eleventy[".eleventy.js<br/>Static build to _site/"]
Eleventy --> Pagefind["Pagefind CLI<br/>Add search index to _site/pagefind/"]
Pagefind --> Worker["worker.js<br/>Dynamic routes + APIs"]
Worker --> Assets["ASSETS binding<br/>_site/"]
Worker --> KV["KV Namespaces<br/>MEMBER_EMAILS, MAGIC_TOKENS"]
Worker --> Secrets["Worker Secrets<br/>SESSION_SECRET, RESEND_API_KEY,<br/>GOOGLE_SHEETS_ID, GOOGLE_SHEETS_API_KEY,<br/>GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET"]
Worker --> External["External Services<br/>Resend, Google Sheets, GitHub OAuth"]
Diagram sources
- [package.json:6-12](file://package.json#L6-L12)
- [.eleventy.js:267-283](file://.eleventy.js#L267-L283)
- [worker.js:1-321](file://worker.js#L1-L321)
- [wrangler.jsonc:9-15](file://wrangler.jsonc#L9-L15)
Section sources
- [README.md:556-587](file://README.md#L556-L587)
- [package.json:6-12](file://package.json#L6-L12)
- [.eleventy.js:267-283](file://.eleventy.js#L267-L283)
- [wrangler.jsonc:9-15](file://wrangler.jsonc#L9-L15)
Core Components
- Static build pipeline: Eleventy configuration and transforms, plus Pagefind indexing.
- Cloudflare Worker: authentication, OAuth, and APIs; delegates to static assets.
- Wrangler configuration: binds static assets and declares KV namespaces and compatibility flags.
- Environment and secrets: Worker secrets and KV namespaces for member authentication and external integrations.
- Optional CMS: Sveltia CMS and TinaCMS configurations for content editing and local regeneration.
Section sources
- [.eleventy.js:1-283](file://.eleventy.js#L1-L283)
- [worker.js:1-321](file://worker.js#L1-L321)
- [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
- [README.md:497-521](file://README.md#L497-L521)
Architecture Overview
The runtime architecture combines a static site with a Cloudflare Worker that intercepts specific routes and serves the rest from ASSETS.
sequenceDiagram
participant U as "User"
participant W as "Cloudflare Worker"
participant KV as "KV Namespaces"
participant R as "Resend API"
participant G as "Google Sheets API"
participant A as "ASSETS (_site/)"
U->>W : Request /alliance/members/*
W->>W : Parse cookies/session
alt Valid session
W->>A : Fetch static asset
A-->>U : HTML/CSS/JS
else No/invalid session
W-->>U : 302 redirect to /alliance/login/?next=...
end
U->>W : POST /alliance/login/ (email)
W->>KV : Lookup MEMBER_EMAILS
alt Approved
W->>R : Send magic-link email
R-->>W : OK
W-->>U : 302 redirect to /alliance/login/?sent=1
else Not approved
W-->>U : 302 redirect to /alliance/login/?sent=1
end
U->>W : GET /alliance/verify/?token=...
W->>KV : Get MAGIC_TOKENS
alt Valid token
W->>KV : Delete token
W-->>U : 302 redirect to /alliance/members/ with session cookie
else Expired/invalid
W-->>U : 302 redirect to /alliance/login/?error=expired
end
U->>W : GET /api/polling.json
W->>G : Fetch Google Sheets data
G-->>W : JSON
W-->>U : JSON (with caching headers)
U->>W : GET /api/auth/*
W-->>U : OAuth start/callback (legacy)
U->>W : Other routes
W->>A : Fetch static asset
A-->>U : HTML/CSS/JS
Diagram sources
- [worker.js:78-321](file://worker.js#L78-L321)
- [wrangler.jsonc:9-15](file://wrangler.jsonc#L9-L15)
Section sources
- [worker.js:1-321](file://worker.js#L1-L321)
- [README.md:638-652](file://README.md#L638-L652)
Detailed Component Analysis
Static Build Pipeline (Eleventy + Pagefind)
- Build script invokes Eleventy to generate static HTML into _site/, followed by Pagefind to embed a client-side search index.
- Eleventy configuration sets input/output directories, adds passthrough copies for assets and admin, and registers transforms and filters.
- Production mode enables HTML minification.
flowchart TD
Start(["Build Start"]) --> RunEleventy["Run Eleventy<br/>.eleventy.js config"]
RunEleventy --> EleventyOutput["_site/ generated"]
EleventyOutput --> RunPagefind["Run Pagefind CLI<br/>pagefind --site _site"]
RunPagefind --> SearchIndex["_site/pagefind/ created"]
SearchIndex --> End(["Build Complete"])
Diagram sources
- [package.json:6-12](file://package.json#L6-L12)
- [.eleventy.js:267-283](file://.eleventy.js#L267-L283)
Section sources
- [package.json:6-12](file://package.json#L6-L12)
- [.eleventy.js:1-283](file://.eleventy.js#L1-L283)
Cloudflare Worker Runtime
- Intercepts member authentication routes, logout, magic link verification, and legacy OAuth endpoints.
- Delegates unmatched routes to ASSETS for static delivery.
- Uses KV namespaces for approved member emails and one-time magic tokens.
- Reads Worker secrets for session signing, email delivery, and external API access.
flowchart TD
Req["Incoming Request"] --> Match["Match route pattern"]
Match --> "|/alliance/members/*|" CheckSession["Check session cookie"]
Match --> "|/alliance/login/ (POST)|" Login["Issue magic link"]
Match --> "|/alliance/verify/|" Verify["Validate token"]
Match --> "|/alliance/logout/|" Logout["Clear session"]
Match --> "|/api/polling.json|" Polling["Fetch Google Sheets"]
Match --> "|/api/auth*|" OAuth["Legacy OAuth handlers"]
Match --> "|Other|" Static["Serve ASSETS"]
CheckSession --> "|Valid|" Static
CheckSession --> "|Invalid|" RedirectLogin["Redirect to login with next param"]
Login --> KVWrite["Write token to MAGIC_TOKENS KV"]
Login --> Email["Send magic link via Resend"]
Verify --> KVRead["Read token from MAGIC_TOKENS KV"]
Verify --> "|Valid|" IssueCookie["Set session cookie"]
Polling --> Cache["Apply caching headers"]
OAuth --> Respond["Respond to opener"]
Diagram sources
- [worker.js:78-321](file://worker.js#L78-L321)
Section sources
- [worker.js:1-321](file://worker.js#L1-L321)
- [wrangler.jsonc:9-15](file://wrangler.jsonc#L9-L15)
Environment Variables and Secrets Management
- Worker secrets (never committed): SESSION_SECRET, RESEND_API_KEY, GOOGLE_SHEETS_ID, GOOGLE_SHEETS_API_KEY, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET.
- KV namespaces: MEMBER_EMAILS (approved member emails), MAGIC_TOKENS (temporary tokens with TTL).
- Wrangler configuration binds ASSETS directory and declares KV namespaces and compatibility flags.
graph LR
Secrets["Worker Secrets"] --> Env["Runtime env in worker.js"]
KV["KV Namespaces"] --> Env
Env --> Worker["worker.js handlers"]
Env --> Static["ASSETS (_site/)"]
Diagram sources
- [worker.js:4-11](file://worker.js#L4-L11)
- [wrangler.jsonc:17-34](file://wrangler.jsonc#L17-L34)
Section sources
- [worker.js:4-11](file://worker.js#L4-L11)
- [wrangler.jsonc:17-34](file://wrangler.jsonc#L17-L34)
- [README.md:497-521](file://README.md#L497-L521)
Continuous Deployment Workflow
- Cloudflare Workers Builds is connected to the repository.
- Push to main deploys to production; pushes to other branches create preview deployments.
- The npm deploy script builds and deploys via Wrangler.
sequenceDiagram
participant Dev as "Developer"
participant Repo as "Git Repository"
participant CF as "Cloudflare Workers Builds"
participant Prod as "Production Worker"
participant Preview as "Preview Worker"
Dev->>Repo : Push to main
Repo-->>CF : Trigger build
CF->>Prod : Deploy production
Dev->>Repo : Push to feature-branch
Repo-->>CF : Trigger build
CF->>Preview : Deploy preview
Diagram sources
- [README.md:570-578](file://README.md#L570-L578)
- [package.json:11-12](file://package.json#L11-L12)
Section sources
- [README.md:556-587](file://README.md#L556-L587)
- [README.md:570-578](file://README.md#L570-L578)
- [package.json:11-12](file://package.json#L11-L12)
Optional CMS Integrations
- Sveltia CMS: Git-backed editor at /admin/, configured via src/admin/config.yml.
- TinaCMS: Local-only regeneration via tina/config.ts and npm run build:cms.
Section sources
- [src/admin/config.yml:1-774](file://src/admin/config.yml#L1-L774)
- [tina/config.ts:1-331](file://tina/config.ts#L1-L331)
- [README.md:174-204](file://README.md#L174-L204)
Dependency Analysis
- Build-time dependencies: Eleventy, Pagefind, wrangler.
- Runtime dependencies: Worker runtime, KV namespaces, external APIs (Resend, Google Sheets, GitHub OAuth).
- Wrangler configuration ties together assets, KV namespaces, and compatibility flags.
graph TB
Pkg["package.json scripts"] --> Eleventy[".eleventy.js"]
Pkg --> Pagefind["Pagefind CLI"]
Eleventy --> Site["_site/"]
Pagefind --> Site
Wrangler["wrangler.jsonc"] --> Worker["worker.js"]
Worker --> Site
Worker --> KV["KV Namespaces"]
Worker --> Ext["External APIs"]
Diagram sources
- [package.json:6-12](file://package.json#L6-L12)
- [.eleventy.js:267-283](file://.eleventy.js#L267-L283)
- [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
Section sources
- [package.json:6-12](file://package.json#L6-L12)
- [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
Performance Considerations
- Static delivery: All non-auth/API routes are served from ASSETS, minimizing compute usage.
- Caching: Polling API responses include cache headers suitable for CDN caching.
- Asset optimization: Eleventy minifies HTML in production; Pagefind’s WASM search index is efficient client-side.
- KV TTL: Magic link tokens expire after 15 minutes, reducing long-term storage overhead.
- Compatibility: nodejs_compat flag supports Node-like APIs during migration; evaluate necessity for future-proofing.
[No sources needed since this section provides general guidance]
Troubleshooting Guide
Common deployment and runtime issues:
-
KV namespaces not configured
- Symptom: 503 responses indicating KV namespaces not configured.
- Resolution: Create KV namespaces and bind them in wrangler.jsonc; redeploy.
- Section sources
- [worker.js:70-75](file://worker.js#L70-L75)
- [wrangler.jsonc:17-26](file://wrangler.jsonc#L17-L26)
-
Missing Worker secrets
- Symptom: Authentication failures or API errors due to missing secrets.
- Resolution: Set required secrets via wrangler secret put; redeploy.
- Section sources
- [worker.js:4-11](file://worker.js#L4-L11)
- [README.md:503-511](file://README.md#L503-L511)
-
Polling data endpoint errors
- Symptom: 503 or 500 responses from /api/polling.json.
- Resolution: Verify GOOGLE_SHEETS_ID and GOOGLE_SHEETS_API_KEY secrets; confirm Sheets API is enabled and accessible.
- Section sources
- [worker.js:233-276](file://worker.js#L233-L276)
- [README.md:507-508](file://README.md#L507-L508)
-
Preview vs production mismatch
- Symptom: Differences between preview and production.
- Resolution: Ensure secrets and KV bindings are identical; confirm wrangler.jsonc matches environment.
- Section sources
- [README.md:570-578](file://README.md#L570-L578)
- [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
-
Rollback procedure
- Use Cloudflare dashboard to roll back to a previous deployment version of the Worker.
- Section sources
- [README.md:570-578](file://README.md#L570-L578)
-
Monitoring and observability
- Enable Worker observability in wrangler.jsonc; review logs and metrics in Cloudflare dashboard.
- Section sources
- [wrangler.jsonc:6-8](file://wrangler.jsonc#L6-L8)
Conclusion
The platform leverages a static-first architecture with a Cloudflare Worker for dynamic functionality. The deployment pipeline is streamlined via Wrangler and Cloudflare Builds, enabling reliable production and preview deployments. Robust secret and KV management, combined with clear operational procedures, ensures secure and scalable operations.
[No sources needed since this section summarizes without analyzing specific files]
Appendices
Build and Deployment Commands
- Build: npm run build
- Preview locally via Wrangler: npm run preview
- Deploy to production: npm run deploy
- Clean build output: npm run clean
Section sources
- [package.json:6-12](file://package.json#L6-L12)
Environment Setup Checklist
- Authenticate with Wrangler
- Create KV namespaces MEMBER_EMAILS and MAGIC_TOKENS
- Bind KV namespaces in wrangler.jsonc
- Set Worker secrets: SESSION_SECRET, RESEND_API_KEY, GOOGLE_SHEETS_ID, GOOGLE_SHEETS_API_KEY, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
- Deploy: npm run deploy
Section sources
- [README.md:523-548](file://README.md#L523-L548)
- [wrangler.jsonc:17-34](file://wrangler.jsonc#L17-L34)
CI/CD Notes
- Cloudflare Workers Builds is connected to the repository.
- Push to main: production deployment.
- Push to other branches: preview deployments.
Section sources
- [README.md:570-578](file://README.md#L570-L578)
Historical Configurations
- cloudflare-pages.toml: Deprecated Pages config retained for reference.
- netlify.toml: Alternative static hosting configuration with security headers and redirects.
Section sources
- [cloudflare-pages.toml:1-17](file://cloudflare-pages.toml#L1-L17)
- [netlify.toml:1-26](file://netlify.toml#L1-L26)