v1.2.0 — The Boilerplate Audited Itself
🛡️ 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
getUsersendpoint 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-filenow requires a session, enforces a 10MB cap and a content-type allowlist, and sniffs magic bytes so a spoofedContent-Typeheader 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/requireFoundhelpers had zero production call sites — every repository hand-rolled the same boilerplate. All three repos now use them; ~30 duplicated blocks gone. runProceduredropped its dishonestas 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.promiseto the testedSessionservice; production auth is built throughAuthApiLiveinstead of a barecreateAuth()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
runAdminActionhelper 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 (zhdates 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 TRPCErroris 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