Kilat

Edge-native. Zero ops.
A full-stack starter that runs at the speed of light.

One Cloudflare Worker: Hono +D1 + Inertia v3 with in-process SSR. React, Svelte, or Vue — your choice. Auth, migrations, tests — wired end to end. wrangler deploy and you're live on 300+ edge locations.

zsh
◆ Select a JavaScript framework:
│ ○ React 19
│ ● Svelte 5
│ ○ Vue 3
│ ↑/↓ to navigate • Enter: confirm
◆ Select a styling approach:
│ ● Vanilla CSS
│ ○ Tailwind CSS v4
↓ Downloading Kilat (Svelte 5 + Vanilla CSS)...
└ Kilat project created! Svelte 5 + Vanilla CSS
WorkersHonoInertia v3D1Web Crypto
1Worker, three clients
300+edge locations
6templates
0servers to manage

Working code, not wiring homework

Auth, migrations, SSR, tests — running end to end on Workers, not left for you to wire.

Auth, complete

Register, login, logout, forgot/reset password and Google OAuth — PBKDF2 (Web Crypto), DB-backed sessions, CSRF.

Inertia v3 + SSR

Full HTML on first load, SPA after. In-process SSR inside the Worker, 409 version negotiation, partial reloads, flash.

Edge-native D1

SQLite at the edge via D1. Versioned migrations via Wrangler. Raw prepared statements — no ORM, zero abstraction tax.

One Worker, three clients

React 19, Svelte 5, and Vue 3 templates with Tailwind CSS v4 — same auth, SSR, and test suite.

Web Crypto passwords

PBKDF2-HMAC-SHA256 via crypto.subtle — zero-dependency, runs natively in Workers. No WASM, no native modules.

Production-grade ops

Per-request logging, security headers (CSP, nosniff, frame denial), /health check, HSTS. wrangler deploy = live.

Why Kilat exists

Because your first week should be business logic, not infrastructure.

Edge-native means zero ops

No Docker, no VPS, no process supervisor. The whole stack is Cloudflare Workers: wrangler dev for local, wrangler deploy for production. D1 is the database, Workers Static Assets serves the client bundle, Web Crypto handles password hashing.

Boring means predictable

Routes in one place, SQL in one file, env in one module — no guessing where things live. No clever tricks to figure out. Whoever comes next — human or AI — can read it and change it without fear of breaking it.

Built for AI agents

Conventions codified in AGENTS.md, validation errors with exact shapes, strict TypeScript that fails at compile time, deterministic tests as the safety net. A codebase an agent can extend without inventing conventions stays coherent.

One Worker. Three clients.

Pick a framework and styling — the same Login page, in your stack.

default (React 19 + vanilla CSS)
import { Head, Link, useForm } from "@inertiajs/react";
import AuthLayout from "../components/AuthLayout";
import Field from "../components/Field";
import "../components/AuthLayout.css";
export default function Login() {
const form = useForm({ email: "", password: "" });
const submit = (e: React.FormEvent) => {
e.preventDefault();
form.post("/login");
};
return (
<AuthLayout>
<Head title="Login" />
<h1 className="auth-sub">Welcome back</h1>
<form onSubmit={submit} noValidate>
<Field id="email" label="Email" error={form.errors.email}>
<input
id="email" type="email" autoComplete="email"
value={form.data.email}
onChange={(e) => form.setData("email", e.target.value)}
/>
</Field>
<button className="btn btn-primary btn-block" type="submit"
disabled={form.processing}>
{form.processing ? "Signing in…" : "Sign in"}
</button>
</form>
</AuthLayout>
);
}
svelte-vanilla (Svelte 5 + scoped <style>)
<script lang="ts">
import { Link, useForm } from '@inertiajs/svelte'
import AuthLayout from '../components/AuthLayout.svelte'
import Field from '../components/Field.svelte'
let { googleEnabled = false, notice = null } = $props()
const form = useForm({ email: '', password: '' })
function submit(e: SubmitEvent) {
e.preventDefault()
form.post('/login')
}
</script>
<AuthLayout>
<h1 class="auth-sub">Welcome back</h1>
<form onsubmit={submit} novalidate>
<Field id="email" label="Email" error={form.errors.email}>
<input id="email" type="email" bind:value={form.email}
onchange={() => form.clearErrors('email')} />
</Field>
<button class="btn btn-primary btn-block" type="submit"
disabled={form.processing}>
{form.processing ? 'Signing in…' : 'Sign in'}
</button>
</form>
</AuthLayout>
vue-vanilla (Vue 3 + scoped <style>)
<script setup lang="ts">
import { Head, Link, useForm } from "@inertiajs/vue3";
import AuthLayout from "../components/AuthLayout.vue";
import Field from "../components/Field.vue";
defineProps<{ googleEnabled?: boolean; notice?: string | null }>();
const form = useForm({ email: "", password: "" });
function submit() {
form.post("/login");
}
</script>
<template>
<Head><title>Login</title></Head>
<AuthLayout>
<h1 class="auth-sub">Welcome back</h1>
<form @submit.prevent="submit" novalidate>
<Field id="email" label="Email" :error="form.errors.email">
<input id="email" type="email" v-model="form.email"
@change="form.clearErrors('email')" />
</Field>
<button class="btn btn-primary btn-block" type="submit"
:disabled="form.processing">
{{ form.processing ? "Signing in…" : "Sign in" }}
</button>
</form>
</AuthLayout>
</template>

Start in one command.

bun create kilat@latest

FAQ

The short version. For the long version, read the comparison article.

How is Kilat different from Next.js, Nuxt, or SvelteKit?

Those are meta-frameworks that couple a server model to one client framework and leave auth and persistence to you. Kilat is a single Cloudflare Worker with Hono + D1 + Inertia v3, auth already wired, raw SQL instead of an ORM, and React, Svelte, or Vue as a swappable template. Pick a meta-framework for RSC or edge middleware; pick Kilat for a wired, ownable, edge-native stack.

How is Kilat different from Dulak?

Dulak runs on Bun (Bun.serve + bun:sqlite + Bun.build). Kilat runs on Cloudflare Workers (Wrangler + D1 + esbuild). Same philosophy, same structure, same Inertia v3 SSR pattern — different runtime. Dulak is for self-hosting on a VPS; Kilat is for zero-ops edge deployment. The dual esbuild build pattern (pre-build SSR to dist/ssr.js) is Kilat's key innovation — Wrangler's internal esbuild can't compile .svelte or .vue files, so we pre-build SSR to plain JS.

Why Cloudflare Workers and not Bun?

Zero ops. No Docker, no VPS, no process supervisor. wrangler deploy and you're live on 300+ edge locations. D1 replicates read-only copies to the nearest edge automatically. The free tier is enough for most apps. Pick Bun (Dulak) for maximum control and synchronous SQLite; pick Workers (Kilat) for zero-ops edge deployment.

Do I have to use React?

No. Kilat ships templates for React 19, Svelte 5, and Vue 3 — each with vanilla CSS or Tailwind CSS v4. The server side (Hono, D1, auth) is identical across all of them.

Why no ORM?

ORMs were built to help humans avoid writing SQL. In 2026, AI generates correct, optimized raw SQL on demand — the human-friction problem ORMs solved is gone. What remains is the cost: an ORM is a dependency you upgrade, audit, and debug, and it adds a layer between you and the database that hides the actual query plan. D1 with prepared statements is explicit and zero-dependency. You see the exact SQL that runs.

Why PBKDF2 and not argon2?

Workers doesn't have native argon2. PBKDF2 via Web Crypto (crypto.subtle) is zero-dependency and runs natively in the Workers runtime. Workers caps PBKDF2 at 100K iterations (OWASP recommends 600K, which throws NotSupportedError) — 100K is still OWASP-acceptable for PBKDF2-HMAC-SHA256.

Is it production-ready?

Yes — every guardrail a deployed app needs is wired and tested, not scaffolded: PBKDF2 passwords (Web Crypto), DB-backed sessions (not JWT), CSRF origin checks, security headers (CSP, nosniff, frame denial), versioned migrations, HSTS, and an E2E suite that boots the full app. The server side is not a demo — it is the same code path that runs in production. What is missing is your business logic, and that is the point.

Will this still work in 5–10 years?

Yes, and here is why: Kilat is a boilerplate you fork and own, not a framework that can be deprecated out from under you. The stack is chosen for longevity — Cloudflare Workers, Hono, D1, and Inertia are stable, pinned versions with no pending rewrites. When a dependency does release a new major, it is a deliberate migration you schedule and test — not an emergency. And because there is no ORM, no SDK, no proprietary abstraction layer, the code you own is just TypeScript, SQL, and HTTP — readable and maintainable by any developer or AI agent, now and in 2036.