WurstWright
A community-driven sausage and charcuterie recipe application with cure calculators, batch scaling, and timers. It does the math that matters for safe curing (nitrite ppm by meat weight and cure type), scales batches up or down, and walks you through a cook with temperature + humidity targets and safety notes.
Live demo: wurstwright.app — try the recipes and the cure calculator. Seeded with two sample recipes (Classic Italian Sausage, Traditional Hungarian Salami) so there’s something to click through.

What it does
- Cure calculators — nitrite ppm by meat weight and cure type (Prague Powder #1 vs #2), with safety bounds so you don’t under- or over-cure.
- Batch scaling — scale a recipe to any meat weight; ingredients, cure, and salt scale proportionally.
- Timers & readings — step timers with temperature/humidity targets, plus a batch-reading log (pH, temperature, weight) for fermentation and drying runs.
- Recipe versioning — publish/fork/revisions, so a community recipe can evolve without losing the original.
- Wiki + inventory — a reference wiki for techniques/equipment, and an equipment inventory tied to your account.
- Auth, billing (Stripe), email (Resend), web push (VAPID) — full SaaS scaffold, though the demo runs in a no-payments, no-email, no-push mode.
Classic Italian Sausage — one of the seeded demo recipes.
Tech stack
| Layer | Choice |
|---|---|
| Client | React 19 + Vite + TypeScript, React Query, Zustand, Tailwind, PWA (vite-plugin-pwa) |
| Server | Express + TypeScript, Prisma ORM, Sentry |
| Database | PostgreSQL 16 |
| Tests | Vitest (unit), Playwright (E2E, with a Postgres service container in CI) |
| Auth | JWT in HttpOnly cookies; Stripe billing, Resend email, VAPID push |
How it’s hosted
This is the part I’m fondest of: the live demo isn’t on a cloud PaaS — it’s a Docker image I built and run on a UGREEN NAS (ARM64) in my basement, fronted by a Cloudflare Tunnel. Same-origin (the Express app serves both the React SPA and the API), so no CORS, no cross-subdomain cookie gymnastics. A Postgres sidecar holds the data on an SSD volume. Zero hosting cost; ~$10/yr for the domain renewal.
The migration itself was a worthwhile project: it moved off a split Cloudflare-Pages
(client) + Railway (API) setup onto the single self-hosted origin, which caught two
interesting bugs along the way (a stale seed script out of sync with the Prisma
schema, and a build-time Vite env var that had baked localhost into the production
bundle).
Traditional Hungarian Salami — the second seeded recipe.
Source
The repo is private (the live demo is the portfolio piece); happy to share access on request to [email protected]. If you’re hiring and want to see code, say so and I’ll open it up.