Skip to main content

Workers overview

Vessl's server-side logic lives in thirteen Cloudflare Workers under workers/. Each is an independent TypeScript Worker with its own wrangler.toml, sharing a CORS-locked helper module in workers/_shared. They exist because the browser cannot safely hold secrets, run cron jobs, or proxy third-party APIs — so those responsibilities move to the edge.

Why Workers and not Firebase Functions

Cloudflare's free Worker plan covers this workload, whereas Firebase Functions require the paid Blaze plan. Keeping the backend on Workers preserves Vessl's zero-cost posture. Every Worker validates input with Zod, locks CORS to the app origins, and exposes a GET /health endpoint returning { ok, worker, version }.

Authentication

Endpoints authenticate one of two ways depending on what they do. User-facing actions expect a Firebase ID token in the Authorization: Bearer ... header, which the Worker verifies before acting. Internal or cron endpoints use a shared service token in an X-Service-Token header. The chosen scheme is documented per endpoint in docs/api/openapi.yaml.

Secret naming

Because all of the author's projects share one Cloudflare account, every Worker secret is prefixed with the project name — VESSL_RESEND_API_KEY, VESSL_GOOGLE_CALENDAR_REFRESH_TOKEN, and so on — and Worker names are prefixed too (vessl-contact-form, vessl-email-sender). This prevents collisions across projects in the same account.

The thirteen Workers

WorkerResponsibility
contact-formContact, quote, and inquiry intake into Firestore
email-senderTransactional email via Resend
google-calendar-proxyConsultation booking + Google Calendar event creation
gdrive-token-proxyPer-user Google Drive OAuth and uploads
fileshub-proxyAdmin file uploads proxied to FilesHub
currency-ratesDaily FX-rate cron, cached in KV
og-generatorDynamic Open Graph SVG image generation
indexnow-pusherSearch-engine ping (IndexNow / Bing / Yandex)
sitemap-workerDynamic sitemap.xml and RSS feed.xml from Firestore
account-purge-cronDaily hard-delete of accounts soft-deleted over 30 days
marketing-cronExecutes due marketing sequence steps
team-invitesIssues and validates team invitation tokens
tenant-resolverResolves a host or slug to a tenant configuration

The Workers reference describes each one's endpoints, auth, and secrets in detail.

Frequently asked questions

Do I need all thirteen Workers to run the app? No. The SPA boots and the storefront works without any Worker. Workers add the server-side flows — intake, email, calendar, uploads, currency, SEO, marketing, tenancy, and the purge cron.

How is CORS handled? A shared helper in workers/_shared locks every Worker's CORS to the app's origins, so the endpoints cannot be called from arbitrary sites.

How do I deploy them? With Wrangler, one Worker at a time or in bulk. See Workers deployment.