lowsbarrel/sveltekit-cf-template

Easily launch your next SaaS with a feature-complete solution

TypeScript

0

5 commits

updated Sep 29, 2026

See the code

See what people are saying

SourceMessageScoreDate

sveltekit-cf-template - An MIT SaaS template for SvelteKit on Cloudflare Workers + Postgres, looking for feedback (r/SideProject)

Hi, I'm Alessandro. I wanted a SaaS foundation where every piece is chosen for running cost and ease of use, so I made one and open-sourced it under MIT: https://github.com/lowsbarrel/sveltekit-cf-template How it was built: with AI coding agents in a close loop. I directed each change, reviewed it…

1

Sep 30, 2026

README

The template's pricing page in dark mode, shown in a tilted browser window

sveltekit-cf-template

A production-ready SaaS template for SvelteKit on Cloudflare Workers. Auth, organizations, billing, email and tests are already done right; an AI coding agent builds the rest in the right place.

English | Italiano

sveltekit-cf-template is the part of a SaaS that is hard to get right and tedious to rebuild: a real auth boundary, organizations with role-based permissions, Merchant-of-Record billing with idempotent webhooks, layered rate limiting, transactional email, consent-gated analytics, tests that run in the real Workers runtime, and CI with gated deploys.

It is also a setup an AI coding agent can drive. AGENTS.md states the architectural invariants, docs/ explains every subsystem, and the skills in .agents/skills/ walk the agent through setup, new features and pricing changes, asking you whenever a decision is genuinely yours to make.

Start a new project with Use this template on GitHub.

Table of Contents:

Features

  • Accounts - Email and password, magic links, Google OAuth, two-factor (TOTP) and passkeys, email verification, breached-password rejection, and a settings area for profile, avatar, sessions and account deletion.

  • Organizations - Teams with roles and typed permissions enforced in services, invitations with a public accept flow, an org switcher and first-run onboarding.

  • Multi-tenancy - Every org-scoped query is isolated in the service layer, with an optional Postgres row-level-security backstop.

  • Billing - Creem as Merchant of Record, so it remits VAT and sales tax for you. Free and paid tiers, trials, one-time lifetime purchases, per-plan quotas and HMAC-verified, idempotent webhooks.

  • Notifications - Real-time in-app notifications over SSE: a persistent inbox, toasts and refresh signals.

  • Email - Cloudflare Email Service with translatable templates for every auth flow, a double opt-in newsletter, and a fallback that logs instead of sending until you configure it.

  • Marketing and content - Prerendered landing, pricing and legal pages and a Markdown blog, served as static assets with canonical URLs, Open Graph, JSON-LD and a sitemap.

  • Analytics - PostHog, loaded only after the visitor accepts the consent banner, plus first-party server-side events.

  • Background work - Cron, queues and durable Workflows as ready seams, with a live-progress SSE pattern.

  • Release safety - Per-PR Worker Previews, feature flags with gradual rollout, versioned rollback, nightly encrypted off-provider backups, and account erasure that survives a restore.

  • Tests and CI - Server tests inside the Workers runtime against real Postgres, component tests in Chromium, Playwright end-to-end tests, a portability build on plain Node, and a post-deploy smoke test.

Getting Started

You need Node 24 or later, Bun (the version is pinned in app/package.json) and Docker for the local database. A Cloudflare account is needed only to deploy.

git clone https://github.com/lowsbarrel/sveltekit-cf-template.git my-app
cd my-app/app
bun install
bun run db:up
bun run db:migrate
bun run dev

The app runs at http://localhost:5173. The first bun run dev creates .dev.vars from .dev.vars.example, which sets EMAIL_DEBUG=true so magic links and verification emails are printed to the log instead of sent.

Everything lives in app/; the repository root holds only documentation, agent skills and CI.

Make it yours

Two placeholders must be replaced before CI passes on your copy:

  1. sveltekit-cf-template, the identity token, anywhere in app/package.json, app/wrangler.jsonc and app/src/ (the site name in site.ts, the consent cookie, the sample blog posts). CI searches for it case-insensitively.
  2. __HYPERDRIVE_ID__ in app/wrangler.jsonc, once you create your Hyperdrive config.

PR previews also need __PREVIEW_HYPERDRIVE_ID__ in the previews block of app/wrangler.jsonc pointed at a preview Hyperdrive config; CI does not check that one.

The todos feature is a worked example of the architecture (schema, actor-scoped service, protected page). Copy its shape, then delete it. docs/setup.md covers the rest.

Build with an AI agent

The skills live in .agents/skills/ and follow the Agent Skills format. In agents that support slash commands, type / and the name:

SkillWhat it does
setupInterviews you about the product, applies your answers to the codebase, and writes a personal checklist of the accounts to create, the paid plans you actually need (most start free) and the commands to run
add-featureBuilds a feature end to end: database model, service with authorization, rate limits, validation, UI, translated strings and tests, placed where the architecture expects. It asks you about real product decisions instead of guessing
add-planAdds or changes pricing tiers, limits, trials and entitlement rules; the pricing page and in-app upgrades follow
upgrade-templatePulls later template improvements into your project and resolves the conflicts by the rules in docs/upgrading.md

A pre-commit hook runs the same gate as CI (lint, check, check:invariants, check:secrets, test), so whatever the agent adds stays consistent with everything already here.

Commands

Run these from app/.

CommandAction
bun run devVite dev server with hot reload
bun run previewBuild and serve in the real Workers runtime
bun run testMigrate, then run server tests in workerd and component tests in Chromium
bun run test:e2ePlaywright against wrangler dev
bun run checkGenerate types and run svelte-check
bun run lint / bun run formatPrettier and ESLint
bun run db:generateCreate a migration from schema changes
bun run db:migrateApply migrations (local, or DATABASE_URL)
bun run db:studioBrowse the local database
bun run deploy:rollbackRoll production back to the previous version

Deploy

Merging to main deploys: CI migrates the production database, deploys the Worker and smoke-tests the live URL. Pull requests get their own preview URL. The first-time provisioning of Postgres, Hyperdrive, secrets and repository policy is one copy-paste CLI block in docs/deploy.md.

Architecture

flowchart LR
  Browser --> Assets["Static assets<br/>marketing, blog"]
  Browser --> Hooks["hooks.server.ts<br/>session → locals.user"]
  Hooks --> Route["Route adapter<br/>builds Ctx + Actor"]
  Jobs["src/worker.ts<br/>cron, queues, workflows"] --> Service
  Route --> Service["Service<br/>logic + authorization"]
  Service --> DB[("Postgres<br/>via Hyperdrive")]
  Service --> Creem["Creem"]
  Service --> Email["Email Service"]
  Service --> R2["R2<br/>presigned uploads"]

Identity flows one way: hooks.server.ts resolves the session into locals.user, the route turns it into an Actor, and the service scopes every query by it. Routes are thin adapters; logic and authorization live in $lib/server/<feature>/service.ts, so a route cannot forget a permission check. Public pages are prerendered and served from static assets without invoking the Worker.

.
├── app/             the SvelteKit project: src/, tests, migrations, scripts, wrangler.jsonc
├── docs/            one page per subsystem, for people and agents
├── .agents/skills/  setup, add-feature, add-plan, upgrade-template
├── .github/         CI, previews, backups, branch ruleset
└── AGENTS.md        the invariants every change follows
LayerChoice
FrameworkSvelteKit (Svelte 5 runes) on Workers via @sveltejs/adapter-cloudflare
DatabasePostgres through Hyperdrive: Neon, Supabase, RDS, self-hosted
ORMDrizzle (postgres.js, drizzle-kit migrations, drizzle-zod)
Authbetter-auth with the organization plugin
BillingCreem (Merchant of Record)
i18nParaglide JS 2, English only and wired for more
Formssveltekit-superforms + zod v4
StylingTailwind CSS v4 + @lucide/svelte
AIVercel AI SDK + workers-ai-provider (opt-in)
TestsVitest (workerd + Chromium), fast-check, Playwright

Documentation

Every page is written for both you and the agent, and checked against the code.

DocWhat's inside
setup.mdZero to running app to deployed
architecture.mdRepository layout, the Ctx/Actor pattern, errors, scripts
adding-features.mdThe recipe: model, service, route, UI, tests
database.mdHyperdrive caching, migrations, Neon, money types
auth.mdSign-in methods, organizations and permissions, email
accounts.mdSettings, avatars, invites, onboarding, deletion
multi-tenancy.mdOrg as tenant, app-layer isolation, RLS
billing.md · plans.mdCreem, webhooks, tiers, trials, quotas
notifications.md · streaming.md · realtime.mdSSE, reconnects, the WebSocket sidecar
cloudflare.mdKV, R2, cron, queues, workflows, AI, debugging
blog.md · newsletter.md · analytics.mdContent, double opt-in, consent-gated PostHog
i18n.md · ui.mdParaglide, the component kit, styling rules
testing.mdThe test layers and how to write each
security.md · ai-compliance.mdEvery defense, and AI transparency duties
deploy.md · flags.md · backups.mdProvisioning, CI, flags, rollback, backups
upgrading.mdPulling later template improvements into your project

Contributing

Contributions are welcome. Work on a branch off main and open a pull request: CI checks Conventional Commit titles, then runs the invariant and secret checks, lint, types, tests, a build with a bundle-size guardrail, end-to-end tests, a plain-Node portability build and a dependency audit. AGENTS.md describes how the code is organised and what "done" means.

Security

  • Authorization lives in services, never routes, and organization roles are always loaded from the database rather than trusted from the client.
  • Auth endpoints are rate limited, no form reveals whether an email has an account, and passwords found in known breaches are rejected.
  • Unexpected errors return only a correlation id, and security headers cover both Worker responses and static assets.
  • Secrets live in .dev.vars locally and in wrangler secret put in production; check:secrets blocks committed credentials, and new npm releases wait a week before Bun installs them.

Please report vulnerabilities privately through a GitHub security advisory rather than a public issue.

License

This repository is available under the MIT License.

better-auth
cloudflare
saas
startup
svelte
sveltekit

lowsbarrel/sveltekit-cf-template

Easily launch your next SaaS with a feature-complete solution

TypeScript

0

5 commits

updated Sep 29, 2026

See the code

See what people are saying

SourceMessageScoreDate

sveltekit-cf-template - An MIT SaaS template for SvelteKit on Cloudflare Workers + Postgres, looking for feedback (r/SideProject)

Hi, I'm Alessandro. I wanted a SaaS foundation where every piece is chosen for running cost and ease of use, so I made one and open-sourced it under MIT: https://github.com/lowsbarrel/sveltekit-cf-template How it was built: with AI coding agents in a close loop. I directed each change, reviewed it…

1

Sep 30, 2026

README

The template's pricing page in dark mode, shown in a tilted browser window

sveltekit-cf-template

A production-ready SaaS template for SvelteKit on Cloudflare Workers. Auth, organizations, billing, email and tests are already done right; an AI coding agent builds the rest in the right place.

English | Italiano

sveltekit-cf-template is the part of a SaaS that is hard to get right and tedious to rebuild: a real auth boundary, organizations with role-based permissions, Merchant-of-Record billing with idempotent webhooks, layered rate limiting, transactional email, consent-gated analytics, tests that run in the real Workers runtime, and CI with gated deploys.

It is also a setup an AI coding agent can drive. AGENTS.md states the architectural invariants, docs/ explains every subsystem, and the skills in .agents/skills/ walk the agent through setup, new features and pricing changes, asking you whenever a decision is genuinely yours to make.

Start a new project with Use this template on GitHub.

Table of Contents:

Features

  • Accounts - Email and password, magic links, Google OAuth, two-factor (TOTP) and passkeys, email verification, breached-password rejection, and a settings area for profile, avatar, sessions and account deletion.

  • Organizations - Teams with roles and typed permissions enforced in services, invitations with a public accept flow, an org switcher and first-run onboarding.

  • Multi-tenancy - Every org-scoped query is isolated in the service layer, with an optional Postgres row-level-security backstop.

  • Billing - Creem as Merchant of Record, so it remits VAT and sales tax for you. Free and paid tiers, trials, one-time lifetime purchases, per-plan quotas and HMAC-verified, idempotent webhooks.

  • Notifications - Real-time in-app notifications over SSE: a persistent inbox, toasts and refresh signals.

  • Email - Cloudflare Email Service with translatable templates for every auth flow, a double opt-in newsletter, and a fallback that logs instead of sending until you configure it.

  • Marketing and content - Prerendered landing, pricing and legal pages and a Markdown blog, served as static assets with canonical URLs, Open Graph, JSON-LD and a sitemap.

  • Analytics - PostHog, loaded only after the visitor accepts the consent banner, plus first-party server-side events.

  • Background work - Cron, queues and durable Workflows as ready seams, with a live-progress SSE pattern.

  • Release safety - Per-PR Worker Previews, feature flags with gradual rollout, versioned rollback, nightly encrypted off-provider backups, and account erasure that survives a restore.

  • Tests and CI - Server tests inside the Workers runtime against real Postgres, component tests in Chromium, Playwright end-to-end tests, a portability build on plain Node, and a post-deploy smoke test.

Getting Started

You need Node 24 or later, Bun (the version is pinned in app/package.json) and Docker for the local database. A Cloudflare account is needed only to deploy.

git clone https://github.com/lowsbarrel/sveltekit-cf-template.git my-app
cd my-app/app
bun install
bun run db:up
bun run db:migrate
bun run dev

The app runs at http://localhost:5173. The first bun run dev creates .dev.vars from .dev.vars.example, which sets EMAIL_DEBUG=true so magic links and verification emails are printed to the log instead of sent.

Everything lives in app/; the repository root holds only documentation, agent skills and CI.

Make it yours

Two placeholders must be replaced before CI passes on your copy:

  1. sveltekit-cf-template, the identity token, anywhere in app/package.json, app/wrangler.jsonc and app/src/ (the site name in site.ts, the consent cookie, the sample blog posts). CI searches for it case-insensitively.
  2. __HYPERDRIVE_ID__ in app/wrangler.jsonc, once you create your Hyperdrive config.

PR previews also need __PREVIEW_HYPERDRIVE_ID__ in the previews block of app/wrangler.jsonc pointed at a preview Hyperdrive config; CI does not check that one.

The todos feature is a worked example of the architecture (schema, actor-scoped service, protected page). Copy its shape, then delete it. docs/setup.md covers the rest.

Build with an AI agent

The skills live in .agents/skills/ and follow the Agent Skills format. In agents that support slash commands, type / and the name:

SkillWhat it does
setupInterviews you about the product, applies your answers to the codebase, and writes a personal checklist of the accounts to create, the paid plans you actually need (most start free) and the commands to run
add-featureBuilds a feature end to end: database model, service with authorization, rate limits, validation, UI, translated strings and tests, placed where the architecture expects. It asks you about real product decisions instead of guessing
add-planAdds or changes pricing tiers, limits, trials and entitlement rules; the pricing page and in-app upgrades follow
upgrade-templatePulls later template improvements into your project and resolves the conflicts by the rules in docs/upgrading.md

A pre-commit hook runs the same gate as CI (lint, check, check:invariants, check:secrets, test), so whatever the agent adds stays consistent with everything already here.

Commands

Run these from app/.

CommandAction
bun run devVite dev server with hot reload
bun run previewBuild and serve in the real Workers runtime
bun run testMigrate, then run server tests in workerd and component tests in Chromium
bun run test:e2ePlaywright against wrangler dev
bun run checkGenerate types and run svelte-check
bun run lint / bun run formatPrettier and ESLint
bun run db:generateCreate a migration from schema changes
bun run db:migrateApply migrations (local, or DATABASE_URL)
bun run db:studioBrowse the local database
bun run deploy:rollbackRoll production back to the previous version

Deploy

Merging to main deploys: CI migrates the production database, deploys the Worker and smoke-tests the live URL. Pull requests get their own preview URL. The first-time provisioning of Postgres, Hyperdrive, secrets and repository policy is one copy-paste CLI block in docs/deploy.md.

Architecture

flowchart LR
  Browser --> Assets["Static assets<br/>marketing, blog"]
  Browser --> Hooks["hooks.server.ts<br/>session → locals.user"]
  Hooks --> Route["Route adapter<br/>builds Ctx + Actor"]
  Jobs["src/worker.ts<br/>cron, queues, workflows"] --> Service
  Route --> Service["Service<br/>logic + authorization"]
  Service --> DB[("Postgres<br/>via Hyperdrive")]
  Service --> Creem["Creem"]
  Service --> Email["Email Service"]
  Service --> R2["R2<br/>presigned uploads"]

Identity flows one way: hooks.server.ts resolves the session into locals.user, the route turns it into an Actor, and the service scopes every query by it. Routes are thin adapters; logic and authorization live in $lib/server/<feature>/service.ts, so a route cannot forget a permission check. Public pages are prerendered and served from static assets without invoking the Worker.

.
├── app/             the SvelteKit project: src/, tests, migrations, scripts, wrangler.jsonc
├── docs/            one page per subsystem, for people and agents
├── .agents/skills/  setup, add-feature, add-plan, upgrade-template
├── .github/         CI, previews, backups, branch ruleset
└── AGENTS.md        the invariants every change follows
LayerChoice
FrameworkSvelteKit (Svelte 5 runes) on Workers via @sveltejs/adapter-cloudflare
DatabasePostgres through Hyperdrive: Neon, Supabase, RDS, self-hosted
ORMDrizzle (postgres.js, drizzle-kit migrations, drizzle-zod)
Authbetter-auth with the organization plugin
BillingCreem (Merchant of Record)
i18nParaglide JS 2, English only and wired for more
Formssveltekit-superforms + zod v4
StylingTailwind CSS v4 + @lucide/svelte
AIVercel AI SDK + workers-ai-provider (opt-in)
TestsVitest (workerd + Chromium), fast-check, Playwright

Documentation

Every page is written for both you and the agent, and checked against the code.

DocWhat's inside
setup.mdZero to running app to deployed
architecture.mdRepository layout, the Ctx/Actor pattern, errors, scripts
adding-features.mdThe recipe: model, service, route, UI, tests
database.mdHyperdrive caching, migrations, Neon, money types
auth.mdSign-in methods, organizations and permissions, email
accounts.mdSettings, avatars, invites, onboarding, deletion
multi-tenancy.mdOrg as tenant, app-layer isolation, RLS
billing.md · plans.mdCreem, webhooks, tiers, trials, quotas
notifications.md · streaming.md · realtime.mdSSE, reconnects, the WebSocket sidecar
cloudflare.mdKV, R2, cron, queues, workflows, AI, debugging
blog.md · newsletter.md · analytics.mdContent, double opt-in, consent-gated PostHog
i18n.md · ui.mdParaglide, the component kit, styling rules
testing.mdThe test layers and how to write each
security.md · ai-compliance.mdEvery defense, and AI transparency duties
deploy.md · flags.md · backups.mdProvisioning, CI, flags, rollback, backups
upgrading.mdPulling later template improvements into your project

Contributing

Contributions are welcome. Work on a branch off main and open a pull request: CI checks Conventional Commit titles, then runs the invariant and secret checks, lint, types, tests, a build with a bundle-size guardrail, end-to-end tests, a plain-Node portability build and a dependency audit. AGENTS.md describes how the code is organised and what "done" means.

Security

  • Authorization lives in services, never routes, and organization roles are always loaded from the database rather than trusted from the client.
  • Auth endpoints are rate limited, no form reveals whether an email has an account, and passwords found in known breaches are rejected.
  • Unexpected errors return only a correlation id, and security headers cover both Worker responses and static assets.
  • Secrets live in .dev.vars locally and in wrangler secret put in production; check:secrets blocks committed credentials, and new npm releases wait a week before Bun installs them.

Please report vulnerabilities privately through a GitHub security advisory rather than a public issue.

License

This repository is available under the MIT License.

better-auth
cloudflare
saas
startup
svelte
sveltekit

Languages

TypeScript

58.3%

Svelte

36.0%

JavaScript

4.5%