Dev Tools

Go Performance Starter

A Go + HTMX SaaS starter with multi-tenancy proven end-to-end — real RLS, real identities, and budgets that fail the build.

Role · Creator and maintainer

Outcomes

  • Multi-tenancy enforced at the database layer — Postgres RLS scoped by real Supabase identities, integration-tested
  • Binary size, gzipped asset weight, and memory budgets fail the build, in CI and locally via one command
  • Public in preview (v0.8.0) under the same layered constitution as the Astro starter

Stack

  • Go
  • templ
  • sqlc
  • HTMX
  • Alpine.js
  • Supabase
  • Postgres RLS

Context

The app-stack counterpart to the Astro Performance Starter: a server-rendered Go + HTMX SaaS template, shipped as a Clownware product in preview. Where the Astro starter answers “how do I ship a content site with discipline,” this one answers “how do I ship a multi-tenant app the same way.”

The Problem

Every SaaS starter promises multi-tenancy. Almost none prove it — the isolation lives in a diagram or an adapter interface, not in anything you can test. And the anti-SPA stack (server-rendered HTML, minimal JavaScript) rarely comes with the budgets and gates that make discipline stick.

My Role

Creator and maintainer. Every load-bearing decision is recorded in the ADR log.

Approach

  • Multi-tenancy proven, not promised. Postgres Row Level Security policies scoped by real Supabase identities, enforced at the database layer and integration-tested. Auth claims ride into every database transaction, so RLS physically cannot be skipped.
  • The demo is the test suite you can click. Visitors get server-issued anonymous identities — no signup — and write real RLS-scoped rows. The reference implementation for tenant-scoped CRUD isn’t in the docs; it runs.
  • Type safety end to end. templ compiles the HTML to type-checked Go; sqlc generates the data layer — no map[string]interface{} anywhere in the request path. HTMX and Alpine cover interactivity without a bundler.
  • Budgets fail the build. Binary size, gzipped asset weight, and memory ceilings gate CI and run locally as one command — enforced in the merge, not lamented in the retro.
  • The committed bet. Supabase is load-bearing by design: swapping it means rewriting the auth middleware and the policies. That lock-in is the price of tenant isolation that’s proven by integration tests instead of asserted by a README — and if vendor-neutral auth is the requirement, this is the wrong starter. The trade-off is documented, not buried.
  • The same constitution, on an app stack. The layered AI constitution and halt-on-violation gates from the Astro starter, applied to Go: agents and humans obey the same CI.

The discipline running through this approach is the site’s thesis: Architecture Is the Interface Now.

What I’d Do Differently

Ship the demo deploy with the repo. The starter’s whole argument is “a running system you can poke,” and publishing the code ahead of the demo leaves the strongest proof point pending.

All work