Cloudflare Workers Deployment

**Referenced Files in This Document** - [wrangler.jsonc](file://wrangler.jsonc) - [worker.js](file://worker.js) - [package.json](file://package.json) - [.eleventy.js](file://.eleventy.js) - [cloudflare-pages.toml](file://cloudflare-pages.toml) - [netlify.toml](file://netlify.toml) - [src/alliance-login.njk](file://src/alliance-login.njk) - [src/alliance-members.njk](file://src/alliance-members.njk) - [src/admin/index.html](file://src/admin/index.html) - [src/_data/site.json](file://src/_data/site.json) - [src/_data/allianceMembers.json](file://src/_data/allianceMembers.json) - [tina/config.ts](file://tina/config.ts)

Table of Contents

  1. Introduction
  2. Project Structure
  3. Core Components
  4. Architecture Overview
  5. Detailed Component Analysis
  6. Dependency Analysis
  7. Performance Considerations
  8. Troubleshooting Guide
  9. Conclusion
  10. Appendices

Introduction

This document explains how to configure, deploy, and manage a Cloudflare Worker that serves a static site with member authentication and dynamic APIs. It covers:

  • wrangler.jsonc configuration for account identification, worker script path, assets binding, compatibility flags, and KV namespace bindings
  • Environment variables and secrets required by the worker
  • Deployment from local development to production, including preview and release workflows
  • The worker.js implementation: route handlers for member authentication, API endpoints, and dynamic content delivery
  • Legacy Pages compatibility configuration and migration rationale
  • Practical deployment commands, environment-specific configuration tips, and troubleshooting
  • Performance optimization, cold start mitigation, resource limits, and static asset delivery via Cloudflare’s edge network

Project Structure

The project builds a static site with Eleventy and deploys it via a Cloudflare Worker that exposes:

  • Member authentication routes (/alliance/login, /alliance/verify, /alliance/logout, /alliance/members)
  • Public static assets via the ASSETS binding
  • Dynamic APIs (/api/polling.json, /api/auth, /api/auth/callback)
  • Optional legacy Pages configuration for historical reference
graph TB
Dev["Developer Machine"] --> Build["Eleventy Build<br/>_site output"]
Build --> Worker["Cloudflare Worker<br/>worker.js"]
Worker --> KV["KV Namespaces<br/>MEMBER_EMAILS, MAGIC_TOKENS"]
Worker --> Secrets["Secrets<br/>SESSION_SECRET, RESEND_API_KEY,<br/>GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET"]
Worker --> Assets["ASSETS Binding<br/>_site directory"]
Assets --> Edge["Cloudflare Edge Network"]
Edge --> Browser["Browser"]
Worker --> External["External Services<br/>Resend, GitHub OAuth, Google Sheets"]

Diagram sources

  • [wrangler.jsonc:9-15](file://wrangler.jsonc#L9-L15)
  • [worker.js:301-320](file://worker.js#L301-L320)
  • [.eleventy.js:267-282](file://.eleventy.js#L267-L282)

Section sources

  • [.eleventy.js:267-282](file://.eleventy.js#L267-L282)
  • [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
  • [package.json:5-12](file://package.json#L5-L12)

Core Components

  • wrangler.jsonc: Defines worker identity, compatibility date, main module, observability, assets binding, compatibility flags, and placeholders for KV namespaces and secrets.
  • worker.js: Implements routing, member authentication (magic-link login), session management, GitHub OAuth for CMS, and a live polling API backed by Google Sheets.
  • Eleventy configuration: Controls build outputs and passthrough copies for static assets.
  • Legacy Pages config: Retained for historical reference; canonical deployment is via Workers Assets.

Section sources

  • [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
  • [worker.js:1-321](file://worker.js#L1-L321)
  • [.eleventy.js:1-283](file://.eleventy.js#L1-L283)
  • [cloudflare-pages.toml:1-17](file://cloudflare-pages.toml#L1-L17)

Architecture Overview

The worker acts as a router and orchestrator:

  • Static routes are served via the ASSETS binding
  • Member-authenticated routes are gated by session cookies validated against a shared secret
  • Magic-link login uses two KV namespaces: one for approved emails and another for short-lived tokens
  • Dynamic endpoints include a CORS-enabled OAuth handshake for CMS and a cached polling API
sequenceDiagram
participant U as "User Agent"
participant W as "Worker (worker.js)"
participant K1 as "KV : MEMBER_EMAILS"
participant K2 as "KV : MAGIC_TOKENS"
participant R as "Resend API"
participant G as "Google Sheets API"
U->>W : GET /alliance/members/*
W->>W : Read cookie and verify signature
alt Cookie missing/expired
W-->>U : 302 Redirect to /alliance/login?next=...
else Valid session
W->>W : Fetch from ASSETS
W-->>U : 200 Static content
end
U->>W : POST /alliance/login/ {email,_gotcha}
W->>K1 : Lookup member : <email>
alt Approved
W->>K2 : Store token : <hex>=<email>, TTL=900s
W->>R : Send magic-link email
else Not approved
W->>R : Optionally skip sending
end
W-->>U : 302 Redirect to /alliance/login?sent=1
U->>W : GET /alliance/verify?token=<hex>
W->>K2 : Retrieve token : <hex>
alt Exists
W->>K2 : Delete token : <hex>
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?state=(federal|sa)
W->>G : Fetch spreadsheet data
G-->>W : JSON values
W-->>U : JSON with cache headers

Diagram sources

  • [worker.js:77-177](file://worker.js#L77-L177)
  • [worker.js:233-276](file://worker.js#L233-L276)

Detailed Component Analysis

wrangler.jsonc Configuration

  • Identity and compatibility
    • name: worker identifier used by Wrangler
    • compatibility_date: ensures runtime behavior stability
    • compatibility_flags: enables Node.js compatibility mode
  • Main module
    • main: path to the worker entrypoint
  • Observability
    • enabled: toggles OpenTelemetry-like observability
  • Assets binding
    • directory: static site output directory
    • binding: variable name used in worker to serve assets
  • KV namespaces and secrets
    • KV namespaces: bind MEMBER_EMAILS and MAGIC_TOKENS
    • Secrets: SESSION_SECRET, RESEND_API_KEY, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET

Operational notes:

  • KV namespaces and secrets are commented out as placeholders; uncomment and populate after creation
  • The assets binding allows serving prebuilt static content from _site

Section sources

  • [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)

worker.js Implementation

Highlights:

  • Session management
    • Cookie name, duration, and HMAC-signed payload
    • Constant-time verification to prevent timing attacks
  • Member authentication
    • POST /alliance/login/: validates email, checks membership in KV, stores ephemeral token, sends email via Resend
    • GET /alliance/verify: consumes token, deletes it, sets session cookie
    • GET /alliance/logout: clears session cookie
    • GET /alliance/members/*: enforces session gate
  • GitHub OAuth for CMS
    • OPTIONS preflight for CORS
    • GET /api/auth: redirects to GitHub OAuth
    • GET /api/auth/callback: exchanges code for access token and posts it to opener window
  • Live polling API
    • GET /api/polling.json: reads Google Sheets values and returns structured JSON with cache headers

Routing logic:

  • Match paths and methods, otherwise delegate to ASSETS

Security and correctness:

  • KV namespaces must be configured; otherwise returns 503
  • Secret values must be set; otherwise endpoints fail gracefully
  • Token TTL and session duration are enforced

Section sources

  • [worker.js:12-58](file://worker.js#L12-L58)
  • [worker.js:77-177](file://worker.js#L77-L177)
  • [worker.js:183-227](file://worker.js#L183-L227)
  • [worker.js:233-276](file://worker.js#L233-L276)
  • [worker.js:282-295](file://worker.js#L282-L295)
  • [worker.js:301-320](file://worker.js#L301-L320)

Frontend Templates and Data

  • Login page (/alliance/login/)
    • Renders a form posting to /alliance/login/
    • Shows “sent” view and error messages based on query params
  • Members portal (/alliance/members/)
    • Uses data from allianceMembers.json for dashboard tiles and resources
  • Admin page
    • Loads Sveltia CMS client for content editing

These templates are generated during Eleventy build and served via ASSETS.

Section sources

  • [src/alliance-login.njk:1-73](file://src/alliance-login.njk#L1-L73)
  • [src/alliance-members.njk:1-58](file://src/alliance-members.njk#L1-L58)
  • [src/admin/index.html:1-12](file://src/admin/index.html#L1-L12)
  • [src/_data/allianceMembers.json:1-40](file://src/_data/allianceMembers.json#L1-L40)

Legacy Pages Compatibility

  • cloudflare-pages.toml: Deprecated; kept for historical reference
  • netlify.toml: Alternative hosting configuration; not used for Workers deployment

Recommendation:

  • Prefer wrangler.jsonc for current deployment; keep Pages config only for reference.

Section sources

  • [cloudflare-pages.toml:1-17](file://cloudflare-pages.toml#L1-L17)
  • [netlify.toml:1-26](file://netlify.toml#L1-L26)

Dependency Analysis

  • Build pipeline
    • Eleventy generates _site from src
    • Passthrough copies assets, admin, robots, and favicon
  • Worker dependencies
    • ASSETS binding depends on _site output
    • KV namespaces depend on wrangler.jsonc bindings
    • Secrets depend on environment configuration
  • External integrations
    • Resend for email delivery
    • GitHub OAuth for CMS
    • Google Sheets for polling data
graph LR
Eleventy[".eleventy.js<br/>Build _site"] --> Site["_site"]
Site --> WorkerJS["worker.js"]
WorkerJS --> AssetsBind["ASSETS binding"]
WorkerJS --> KV["KV Namespaces"]
WorkerJS --> Secrets["Secrets"]
WorkerJS --> Ext1["Resend API"]
WorkerJS --> Ext2["GitHub OAuth"]
WorkerJS --> Ext3["Google Sheets"]

Diagram sources

  • [.eleventy.js:7-14](file://.eleventy.js#L7-L14)
  • [wrangler.jsonc:9-15](file://wrangler.jsonc#L9-L15)
  • [worker.js:301-320](file://worker.js#L301-L320)

Section sources

  • [.eleventy.js:1-283](file://.eleventy.js#L1-L283)
  • [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
  • [worker.js:1-321](file://worker.js#L1-L321)

Performance Considerations

  • Cold start mitigation
    • Keep worker.js minimal and avoid heavy initialization
    • Use KV for small, fast lookups; cache long-lived secrets in environment
    • Serve static assets via ASSETS binding to leverage edge caching
  • Resource limits
    • Respect CPU and memory quotas; avoid synchronous blocking operations
    • Use streaming responses for large payloads when possible
  • Caching strategy
    • Polling API returns cache headers to reduce load
    • Static assets benefit from immutable cache headers via build pipeline
  • Observability
    • Enable observability in wrangler.jsonc to monitor latency and errors

[No sources needed since this section provides general guidance]

Troubleshooting Guide

Common issues and resolutions:

  • KV namespaces not configured
    • Symptom: 503 responses from member routes
    • Action: Create KV namespaces and update wrangler.jsonc bindings
  • Missing secrets
    • Symptom: OAuth or email endpoints fail
    • Action: Set secrets via Wrangler CLI
  • Invalid or expired magic link
    • Symptom: Redirects to login with error query param
    • Action: Ensure token TTL and KV deletion occur correctly
  • CORS errors for OAuth callback
    • Symptom: Cross-origin postMessage blocked
    • Action: Verify preflight handling and origin constraints
  • Static assets not loading
    • Symptom: Blank pages or missing styles/scripts
    • Action: Confirm Eleventy build completes and ASSETS binding matches _site

Section sources

  • [worker.js:70-75](file://worker.js#L70-L75)
  • [worker.js:153-177](file://worker.js#L153-L177)
  • [worker.js:183-227](file://worker.js#L183-L227)
  • [worker.js:301-320](file://worker.js#L301-L320)

Conclusion

This project demonstrates a robust Cloudflare Workers deployment model:

  • Static site built by Eleventy and served via ASSETS
  • Member authentication powered by KV-backed magic links and signed sessions
  • Dynamic endpoints for CMS OAuth and live polling data
  • Clear separation of concerns and environment-driven configuration

Adopt the provided deployment commands, configure KV and secrets, and rely on the ASSETS binding for optimal edge delivery.

[No sources needed since this section summarizes without analyzing specific files]

Appendices

Deployment Commands

  • Local preview
    • Build and run dev server: see scripts
  • Production deployment
    • Build and deploy: see scripts

Environment variables and secrets:

  • SESSION_SECRET: random secret for HMAC signing
  • RESEND_API_KEY: for sending magic-link emails
  • GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET: for CMS OAuth
  • GOOGLE_SHEETS_ID, GOOGLE_SHEETS_API_KEY: for polling API

KV namespaces:

  • MEMBER_EMAILS: key format member:
  • MAGIC_TOKENS: key format token:, TTL 900 seconds

Section sources

  • [package.json:5-12](file://package.json#L5-L12)
  • [worker.js:4-11](file://worker.js#L4-L11)
  • [wrangler.jsonc:17-34](file://wrangler.jsonc#L17-L34)

Environment-Specific Configuration Tips

  • Use separate KV namespaces per environment (dev/stage/prod)
  • Store secrets in environment variables; never commit to source
  • Align compatibility_date with tested runtime behavior
  • Validate CORS origins and preflight handling for OAuth

Section sources

  • [worker.js:183-191](file://worker.js#L183-L191)
  • [wrangler.jsonc:3-5](file://wrangler.jsonc#L3-L5)

Static Asset Delivery Through Cloudflare Edge

  • Eleventy passthrough copies assets and admin folders
  • ASSETS binding serves _site directory from edge locations
  • Immutable cache headers for assets improve performance

Section sources

  • [.eleventy.js:7-14](file://.eleventy.js#L7-L14)
  • [wrangler.jsonc:9-12](file://wrangler.jsonc#L9-L12)