Skip to content

v1.2.0 — The Boilerplate Audited Itself

Choose a tag to compare

@SeanningTatum SeanningTatum released this 15 Jul 19:53
613c790

🛡️ The Boilerplate Audited Itself

Four AI auditors read every line. Five more fixed what they found. A sixth tried to refute the fixes. This release is what survived — a security-hardened, Effect-disciplined, fully-tested starter you can trust as a foundation, not just a demo.

Security holes, closed

  • 🔒 No more anonymous user dumps — the public getUsers endpoint leaked every user's email, role, and ban status to unauthenticated callers. It's now auth-gated and returns only a safe projection (id, name, image, createdAt).
  • 📦 Uploads are actually protected/api/upload-file now requires a session, enforces a 10MB cap and a content-type allowlist, and sniffs magic bytes so a spoofed Content-Type header can't smuggle an HTML payload into R2. SVG is deliberately off the list (stored-XSS vector) — with a comment explaining exactly when it's safe to re-add.
  • 🤐 Internal errors stay internal — the tRPC fallback no longer echoes raw exception text to clients. Real detail goes to structured logs; clients get a clean 500 (or a 503 when the failure is transient and worth retrying).

Effect TS, the way the docs said all along

  • The canonical tryQuery / tryUpdate / tryDelete / requireFound helpers had zero production call sites — every repository hand-rolled the same boilerplate. All three repos now use them; ~30 duplicated blocks gone.
  • runProcedure dropped its dishonest as ManagedRuntime<_, never> cast. Layer-construction failures (a missing D1 binding at deploy time) now flow through the same error→HTTP mapping as everything else instead of crashing raw.
  • Session resolution went from defect-swallowing Effect.promise to the tested Session service; production auth is built through AuthApiLive instead of a bare createAuth() that could throw outside any Effect.
  • Unregistered error tags now degrade to a logged 500 instead of a runtime defect — forgetting to register a new tagged error is no longer a crash.

A UI that stopped pretending

  • The admin sidebar showed a hardcoded shadcn / m@example.com — it now shows the actual logged-in user, and "Log out" actually logs you out.
  • Fixed the ban-user bug where the success toast fired even when the ban failed — all four admin actions now run through one runAdminAction helper that checks the response first.
  • Five copy-pasted loader auth checks became three tested helpers: requireSession, requireAdmin, redirectIfAuthenticated.
  • One shared FeatureCard, one theme-switcher source, one locale cookie, locale-aware dates (zh dates finally render in Chinese), and a full i18n sweep — the upload flow, admin table, nav, and toasts are now fully translated in English and 简体中文.

Tests that earn the word

157 → 228 unit tests. The gaps were the scary kind: bulk-mutation paths that only tested the empty-array short-circuit, repositories with untested error branches, schemas with unexercised refinements. All covered now — plus spy-capable Drizzle stubs so tests can assert which write happened, not just that something resolved.

For the agent-first crowd

  • The whole release was produced by the .brain/ harness working as designed: audit agents → fixer waves with disjoint file ownership → an adversarial enforcer pass (it caught 4 cross-agent seams) → verify-done → pre-PR Greptile review.
  • CI's non-negotiables sweep got smarter: test files are excluded by pathspec and the tRPC boundary's contractual throw new TRPCError is allowlisted — no more false-positive red X's.
  • Playwright now pins its dev-server port (--strictPort + E2E_PORT). Found the hard way: Vite silently port-bumps when 5173 is busy, and e2e was testing a different project's app. Documented in the brain so nobody relearns it.

Full diff: v1.1.0...v1.2.0