Easily launch your next SaaS with a feature-complete solution
TypeScript
0
5 commits
updated Sep 29, 2026
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:
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.
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.
Two placeholders must be replaced before CI passes on your copy:
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.__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.
The skills live in .agents/skills/ and follow the Agent Skills format. In agents that support slash commands, type / and the name:
| Skill | What it does |
|---|---|
setup | Interviews 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-feature | Builds 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-plan | Adds or changes pricing tiers, limits, trials and entitlement rules; the pricing page and in-app upgrades follow |
upgrade-template | Pulls 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.
Run these from app/.
| Command | Action |
|---|---|
bun run dev | Vite dev server with hot reload |
bun run preview | Build and serve in the real Workers runtime |
bun run test | Migrate, then run server tests in workerd and component tests in Chromium |
bun run test:e2e | Playwright against wrangler dev |
bun run check | Generate types and run svelte-check |
bun run lint / bun run format | Prettier and ESLint |
bun run db:generate | Create a migration from schema changes |
bun run db:migrate | Apply migrations (local, or DATABASE_URL) |
bun run db:studio | Browse the local database |
bun run deploy:rollback | Roll production back to the previous version |
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.
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
| Layer | Choice |
|---|---|
| Framework | SvelteKit (Svelte 5 runes) on Workers via @sveltejs/adapter-cloudflare |
| Database | Postgres through Hyperdrive: Neon, Supabase, RDS, self-hosted |
| ORM | Drizzle (postgres.js, drizzle-kit migrations, drizzle-zod) |
| Auth | better-auth with the organization plugin |
| Billing | Creem (Merchant of Record) |
| i18n | Paraglide JS 2, English only and wired for more |
| Forms | sveltekit-superforms + zod v4 |
| Styling | Tailwind CSS v4 + @lucide/svelte |
| AI | Vercel AI SDK + workers-ai-provider (opt-in) |
| Tests | Vitest (workerd + Chromium), fast-check, Playwright |
Every page is written for both you and the agent, and checked against the code.
| Doc | What's inside |
|---|---|
| setup.md | Zero to running app to deployed |
| architecture.md | Repository layout, the Ctx/Actor pattern, errors, scripts |
| adding-features.md | The recipe: model, service, route, UI, tests |
| database.md | Hyperdrive caching, migrations, Neon, money types |
| auth.md | Sign-in methods, organizations and permissions, email |
| accounts.md | Settings, avatars, invites, onboarding, deletion |
| multi-tenancy.md | Org as tenant, app-layer isolation, RLS |
| billing.md · plans.md | Creem, webhooks, tiers, trials, quotas |
| notifications.md · streaming.md · realtime.md | SSE, reconnects, the WebSocket sidecar |
| cloudflare.md | KV, R2, cron, queues, workflows, AI, debugging |
| blog.md · newsletter.md · analytics.md | Content, double opt-in, consent-gated PostHog |
| i18n.md · ui.md | Paraglide, the component kit, styling rules |
| testing.md | The test layers and how to write each |
| security.md · ai-compliance.md | Every defense, and AI transparency duties |
| deploy.md · flags.md · backups.md | Provisioning, CI, flags, rollback, backups |
| upgrading.md | Pulling later template improvements into your project |
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.
.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.
This repository is available under the MIT License.
TypeScript
58.3%
Svelte
36.0%
JavaScript
4.5%
Easily launch your next SaaS with a feature-complete solution
TypeScript
0
5 commits
updated Sep 29, 2026
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:
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.
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.
Two placeholders must be replaced before CI passes on your copy:
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.__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.
The skills live in .agents/skills/ and follow the Agent Skills format. In agents that support slash commands, type / and the name:
| Skill | What it does |
|---|---|
setup | Interviews 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-feature | Builds 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-plan | Adds or changes pricing tiers, limits, trials and entitlement rules; the pricing page and in-app upgrades follow |
upgrade-template | Pulls 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.
Run these from app/.
| Command | Action |
|---|---|
bun run dev | Vite dev server with hot reload |
bun run preview | Build and serve in the real Workers runtime |
bun run test | Migrate, then run server tests in workerd and component tests in Chromium |
bun run test:e2e | Playwright against wrangler dev |
bun run check | Generate types and run svelte-check |
bun run lint / bun run format | Prettier and ESLint |
bun run db:generate | Create a migration from schema changes |
bun run db:migrate | Apply migrations (local, or DATABASE_URL) |
bun run db:studio | Browse the local database |
bun run deploy:rollback | Roll production back to the previous version |
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.
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
| Layer | Choice |
|---|---|
| Framework | SvelteKit (Svelte 5 runes) on Workers via @sveltejs/adapter-cloudflare |
| Database | Postgres through Hyperdrive: Neon, Supabase, RDS, self-hosted |
| ORM | Drizzle (postgres.js, drizzle-kit migrations, drizzle-zod) |
| Auth | better-auth with the organization plugin |
| Billing | Creem (Merchant of Record) |
| i18n | Paraglide JS 2, English only and wired for more |
| Forms | sveltekit-superforms + zod v4 |
| Styling | Tailwind CSS v4 + @lucide/svelte |
| AI | Vercel AI SDK + workers-ai-provider (opt-in) |
| Tests | Vitest (workerd + Chromium), fast-check, Playwright |
Every page is written for both you and the agent, and checked against the code.
| Doc | What's inside |
|---|---|
| setup.md | Zero to running app to deployed |
| architecture.md | Repository layout, the Ctx/Actor pattern, errors, scripts |
| adding-features.md | The recipe: model, service, route, UI, tests |
| database.md | Hyperdrive caching, migrations, Neon, money types |
| auth.md | Sign-in methods, organizations and permissions, email |
| accounts.md | Settings, avatars, invites, onboarding, deletion |
| multi-tenancy.md | Org as tenant, app-layer isolation, RLS |
| billing.md · plans.md | Creem, webhooks, tiers, trials, quotas |
| notifications.md · streaming.md · realtime.md | SSE, reconnects, the WebSocket sidecar |
| cloudflare.md | KV, R2, cron, queues, workflows, AI, debugging |
| blog.md · newsletter.md · analytics.md | Content, double opt-in, consent-gated PostHog |
| i18n.md · ui.md | Paraglide, the component kit, styling rules |
| testing.md | The test layers and how to write each |
| security.md · ai-compliance.md | Every defense, and AI transparency duties |
| deploy.md · flags.md · backups.md | Provisioning, CI, flags, rollback, backups |
| upgrading.md | Pulling later template improvements into your project |
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.
.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.
This repository is available under the MIT License.
TypeScript
58.3%
Svelte
36.0%
JavaScript
4.5%