Skip to content

Latest commit

 

History

History
128 lines (99 loc) · 6.29 KB

File metadata and controls

128 lines (99 loc) · 6.29 KB

Architecture and deployment

This document describes the public production architecture of digows.com. Private credentials, account identifiers, personal destinations, dashboard links, backup locations, and editorial automation are intentionally outside this repository.

Topology

flowchart TD
    GitHub["GitHub repository<br/>digows/digows.com"]
    Actions["GitHub Actions<br/>validation"]
    Builds["Cloudflare Workers Builds<br/>build and deploy main"]
    Edge["Cloudflare edge routing"]
    Redirects["Single Redirects<br/>www and blog to apex"]
    Worker["Cloudflare Worker<br/>dynamic routes only"]
    Assets["Workers Static Assets"]
    API["Worker APIs"]
    D1["Cloudflare D1"]
    Turnstile["Cloudflare Turnstile"]
    Email["Cloudflare Email Service"]
    Resend["Resend<br/>welcome email and broadcasts"]
    Visitor["Visitor"]

    GitHub --> Actions
    GitHub --> Builds
    Builds --> Worker
    Visitor --> Edge
    Edge --> Redirects
    Edge --> Assets
    Edge --> Worker
    Worker --> API
    API --> D1
    API --> Turnstile
    API --> Email
    API --> Resend
Loading

The site uses Cloudflare Workers with Static Assets rather than Cloudflare Pages. Static HTML, CSS, JavaScript, images, feeds, and sitemaps are generated by Astro and served directly by Static Assets. run_worker_first contains only the dynamic /api/* and /moderate/comment routes. Canonical redirects from www.digows.com and blog.digows.com are Cloudflare Single Redirects backed by proxied placeholder DNS records, so those requests never invoke the Worker. Exact legacy path redirects are handled by Static Assets through _redirects.

Sources of truth

Concern Source
Worker, routes, assets, bindings, and non-secret variables wrangler.jsonc
D1 schema migrations/
Static site and Worker code src/
Response security policy public/_headers
Exact legacy redirects public/_redirects
Canonical alias redirects and alias DNS Cloudflare zone configuration
Continuous validation .github/workflows/ci.yml
Production credentials and private destinations Cloudflare Worker secrets
Git deployment connection Cloudflare Workers Builds

The D1 database identifier and Turnstile site key present in versioned configuration are public resource identifiers, not credentials. Turnstile validation, comment fingerprints, moderation signatures, and the notification destination use Worker secrets and must never be committed.

Runtime APIs

Route Methods Responsibility
/api/comments GET, POST Approved comments and moderated submissions
/api/reactions GET, POST Anonymous article reactions
/api/contact POST Private contact submissions
/api/newsletter/subscriptions POST Turnstile-protected explicit opt-in; schedules welcome delivery and provider synchronization
/api/webhooks/resend POST Signed, idempotent preference and suppression events
/moderate/comment GET, POST Signed moderation confirmation

The APIs enforce same-origin checks, bounded bodies, Turnstile verification, parameterized D1 access, persistent abuse windows, and structured errors. Resend webhooks require a recent Svix HMAC signature and persist event IDs before processing. Logs must not include message bodies, email addresses, tokens, secrets, or raw fingerprints.

Newsletter delivery deliberately uses no Cloudflare Queue. A successful Turnstile-protected request persists the explicit single opt-in in D1 before returning. The welcome email and Resend contact synchronization then run through waitUntil; their independent delivery states are persisted so a six-hour cron can reconcile at most five failed or pending records per run. There is no unconfirmed-subscription retention job because single opt-in creates no pending confirmation state. The same cron removes expired abuse-window records after 30 days and processed webhook records after 90 days. Resend uses one shared segment and one Topic per locale, keeping the design within the Free plan's three-segment limit while preserving localized preference controls.

Validation

GitHub Actions validates every pull request and push to main:

  1. Install dependencies from the lockfile.
  2. Verify generated Cloudflare binding types.
  3. Type-check Astro and the Worker.
  4. Run Worker and newsletter tests.
  5. Apply all D1 migrations to an isolated local database.
  6. Build and audit the static site.
  7. Produce a Wrangler deployment dry-run.

Run the same checks locally:

pnpm install --frozen-lockfile
pnpm run types:check
pnpm run check
pnpm run test
pnpm run d1:migrate:local
pnpm run build
pnpm run audit
pnpm exec wrangler deploy --dry-run

Production deployment

Cloudflare Workers Builds follows the main branch with repository root / and non-production builds disabled.

Build command:  pnpm run types:check && pnpm run check && pnpm run test && pnpm run build && pnpm run audit
Deploy command: pnpm run d1:migrate:remote && pnpm exec wrangler deploy

The migration command precedes the Worker upload. Production migrations must therefore remain backward-compatible with both the previous and next Worker versions.

Manual deployment is reserved for recovery:

pnpm run deploy

Secret contract

The Worker requires these production secrets:

  • TURNSTILE_SECRET_KEY
  • COMMENT_FINGERPRINT_SECRET
  • COMMENT_MODERATION_SECRET
  • NOTIFICATION_EMAIL_DESTINATION
  • RESEND_API_KEY
  • RESEND_MANAGEMENT_API_KEY
  • RESEND_WEBHOOK_SECRET

Local development uses .dev.vars, copied from .dev.vars.example and ignored by Git. Production secrets are set directly in Cloudflare and survive normal Wrangler deployments.

Data and recovery boundaries

D1 is the source of truth for comments, contact messages, reactions, newsletter consent and synchronization state, abuse windows, and stable post localization records. Resend is the delivery and marketing-preference provider, not the consent ledger. A Worker rollback does not reverse D1 migrations or data changes. Review destructive data operations separately and export production data to an access-controlled location before executing them.