Deliberately boring.
Production-shaped by default.
A full-stack starter running entirely on Bun:Hono for HTTP, bun:sqlite for data,Inertia v3 for server-driven UI — with React, Svelte, and Vue templates. Auth, uploads, migrations, tests, Docker — wired end to end.
Everything wired, nothing invented
The guardrails a deployed app needs, from day one.
Auth, complete
Register, login, logout, forgot/reset password and Google OAuth — argon2id, DB-backed sessions, CSRF, rate-limited.
Inertia v3 + SSR
Full HTML on first load, SPA after. In-process SSR, 409 version negotiation, partial reloads, one-shot flash.
Resumable uploads
tus protocol v1: creation, resume, checksum, termination, expiration — proven by the avatar upload.
One server, three clients
React, Svelte 5, and Vue 3 templates with Tailwind CSS v4 — same auth, SSR, and test suite.
Migrations & SQL
Versioned SQL applied at startup in transactions. Zero-ORM bun:sqlite, prepared statements only.
Production-shaped ops
Batched logging, gzip, security headers, /health, graceful shutdown, multi-stage Docker, CI.
One server. Three clients.
Pick a frontend — the same Login page, in your framework.
<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>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><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>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>import { Head, Link, useForm } from "@inertiajs/react";import AuthLayout from "../components/AuthLayout";import Field from "../components/Field";
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>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> );}Ship your next app. Boringly.
One command. Auth, uploads, migrations, tests, Docker — already wired.
$ bun create dulak my-app