Skip to content
WielandTech

← All work

r12d

Privacy-first QR stickers, short links, a photo inventory and lost-and-found for households and small businesses.

Role
Sole developer: product, build, ops
Year
2026
Status
Live
Links
r12d.ch
A stylised QR code.

Problem

QR codes are everywhere, and most QR services make money by tracking whoever scans them. For a household labelling storage boxes, or a small business tagging equipment, that’s a bad trade. They need durable codes, an inventory behind them, and a way for an honest finder to report something lost. They don’t need an ad-tech profile of every scan.

Approach

One Django service with two faces, picked by the request’s host:

  1. Sticker scan

    r12d.ch/<code>

  2. Public host

    short codes, lost & found, marketing

  3. Anonymous daily count

    no IP, no UA, no per-scan row

  4. App host

    inventory, photos, CRM, loans

Host-based split: one deployment, two sites, with the privacy rules enforced in code and tests.
  • No visitor tracking, by construction. No IP address, user agent or per-scan row is ever stored. Scans become anonymous daily counts. The public host sets no cookies for anonymous visitors, and the app server runs without an access log. A dedicated test suite guards everything the app can enforce.
  • Workspaces as the tenancy boundary. Every query goes through one membership check, and child objects are always looked up scoped to that workspace, never by a bare ID. Non-members get a 404, not a 403, so the app doesn’t confirm that something exists.
  • Permanent codes. A printed sticker can’t be recalled, so short codes are generated once and never reused. A code is deactivated, never deleted.

Hard parts

  • Two hosts, one URL config. In production each host knows only its own routes, so a link that crosses hosts has to be built explicitly. Single-host tests miss that kind of bug, so a split-mode test exists for every cross-host path. That’s how a 500 error on every lost-and-found report was caught in review, before it shipped.
  • Back office off the internet. The admin is reachable only over a private network. Ingress routes by Host header, so even an internal-looking hostname would be publicly reachable. The app rejects admin requests on every other host.

Outcome

  • Live at r12d.ch, with the product on its own host.
  • Privacy is a set of tested rules, not a promise on a marketing page.
  • Ships through the same review-app → dev → prod pipeline as everything else on the homelab.

Stack

  • Django
  • PostgreSQL
  • S3
  • Kubernetes
  • Flux