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.

A board of homemade cured sausages and charcuterie

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.

Fresh Italian pork sausage links Classic Italian Sausage — one of the seeded demo recipes.

Tech stack

LayerChoice
ClientReact 19 + Vite + TypeScript, React Query, Zustand, Tailwind, PWA (vite-plugin-pwa)
ServerExpress + TypeScript, Prisma ORM, Sentry
DatabasePostgreSQL 16
TestsVitest (unit), Playwright (E2E, with a Postgres service container in CI)
AuthJWT 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).

Sliced dry-cured salami 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.