An age-gated e-commerce & digital-delivery platform, production-ready in seven weeks.

Built by the one-man factory for an independent European content creator in an age-restricted (18+) segment. The client stays anonymous by policy — the engineering doesn’t have to. Every number below was measured against the shipped main branch on 15 July 2026, with the method stated per figure. Nothing is estimated; what couldn’t be measured reproducibly was left out, and it says so where it was.

A shop that hands over digital goods — safely, without accounts.

A production, bilingual (German/English) direct-to-consumer webshop and digital-delivery platform: product catalogue, cart and configurable orders, an age-verification gate before first paint — and the core: a token-authorised delivery portal where a buyer accesses their digital product through a one-time link, no account required. On top sits an administrative layer for manual access grants and an honest evidence trail of when access was granted and first opened.

Choices, and the trade-offs behind them.

LayerChoiceWhy (the trade-off)
Frontend / runtimeNext.js 15 (App Router), React 19, TypeScript 5.8 strictServer components by default — secrets and data access stay on the server and never enter the browser bundle. TypeScript strict is used as a contract enforcer, not decoration (see the decisions below).
i18nnext-intl 4 (de/en)A fully localised route tree rather than runtime string swapping — SEO-correct localised URLs. The delivery portal deliberately sits outside the localised tree, locking language via an explicit, non-secret query parameter, so a future e-mail link can be constructed deterministically without changing the route.
Data layerPostgres via Supabase (SSR cookie refresh)Row-Level Security default-deny on every table; reads are anon-tier and tenant-scoped; privileged write paths run server-side only. Schema truth lives in 26 versioned SQL migrations, not ad-hoc changes.
Serverless2 Supabase Edge FunctionsPayment/mapping logic isolated in a separate runtime with its own bounded auth — the shop’s Next runtime never touches that service-role boundary directly.
ObservabilitySentry 10Error events are sanitised at the source — error code and message only, never raw PII or tokens.
Hosting / deployVercel (app) + Supabase (DB/functions); GitHub as source of truthPreview deploys per PR; staging kept in sync with main by automation. No direct pushes to main — ever.

The architectural spine: raw access tokens are never stored — only their sha256 hash. The token is the sole authority (never an internal UUID), and it never crosses the server/client boundary.

Every change travels the same road. No shortcuts — not even for the owner.

  1. Design before code. Non-trivial features start as a ratified design document — the repo holds 147 markdown documents — reviewed before a line of code is written.
  2. Bounded pull requests. Everything lands via PR; main accepts only squash merges. Measured: 104 of 104 commits on main are merges carrying a PR number — the entire history is reviewed, nothing bypassed it.
  3. CI gates that block merge — 7 workflows: full-repo typecheck, the Vitest suite and a build smoke on every PR; SAST (Semgrep); secret scanning (TruffleHog) that blocks if a key leaks in; a deterministic staging-promotion gate; automated dependency updates with auto-merge only for verified bumps; and an automated review layer commenting on every PR.
  4. Test coverage in breadth: 72 dedicated test files, 30 of them security tests — RLS denial, service-role containment, token hashing, ingress guards. The suite runs ~880 tests across Vitest, Testing Library and Playwright browser flows.

The ones a senior engineer would nod at.

1 · Token-as-authority, hash-only storage, and a boundary the compiler enforces.

The access token is stored only as a sha256 hash and resolved in one place, once per request. It must never reach the browser — and that isn’t an intention, it’s a compile-time contract: a test asserts the component exposes no token prop, so the typecheck fails if anyone reintroduces it. The first-open transition is race-safe via a conditional UPDATE (WHERE status='issued' AND last_opened_at IS NULL) — two concurrent requests cannot both “win” the first opening.

2 · Security-by-default, enforced by tests — not by memory.

RLS is default-deny. The privileged service-role client may only be constructed in a single module — and a containment regression test fails if anyone constructs it elsewhere. Thirty security tests continuously confirm that anonymous and authenticated reads are denied where they should be. Security is a property CI can prove, not a note in a wiki.

3 · A verification regime that doesn’t trust green unit tests alone.

Customer-facing UI changes require reproducible browser evidence — Playwright, mobile and desktop, both languages — before merge, because a green unit test doesn’t prove a page actually renders. The same scepticism applies everywhere: in one case the full-repo typecheck caught a contract break that both the test suite and the build had missed. The discipline is “several independent axes must be green” — never “one green = done.”

Measured, not marketed.

MetricValueHow it was measured
Development period27 May → 15 July 2026 (~7 weeks)First/latest commit date on main
Commits / merged PRs on main104 — all squash mergesgit rev-list --count; PR numbers verified in every merge subject
Application modules92 TypeScript modules (excl. tests)File tree listing against main
Test files72 — of which 30 securityTest-file glob + security suite directory
Database migrations26 versioned SQLMigrations directory against main
Edge functions2Functions directory against main
CI workflows7Workflows directory against main
Design & ops documents147 markdownDocs directory against main

Deliberately left out: the total number of PRs ever opened (including closed/superseded ones) — it can’t be reproducibly derived from git alone, so only the 104 that landed on main are claimed. A precise “green test count” is not quoted as an absolute either: a handful of timeout-sensitive tests can flake under full parallel runs (green in isolation), so the stable, static measurement — 72 test files — is the number that gets cited. That’s what “with receipts” means in practice.

One thing deliberately not built.

Payment and dispute automation — not yet, on purpose.

The foundation exists: webhook ingestion and transaction mapping sit ready in an isolated runtime. But no live payment gateway is wired into production, and the fully designed “evidence pack” for defending a dispute is designed, not implemented — precisely because the system cannot yet mechanically prove a payment. The principle is reality-first: the platform only promises what it can trace to a concrete source. Persistent immutable evidence storage was likewise deferred in favour of an on-demand model — the least irreversible surface first. It’s boring to sell. It’s also how you avoid building a very precise machine for overclaiming.

This is one product from the factory.

Seven weeks, one operator, a fleet of AI agents — and an enforcement layer that keeps the speed honest. See how the whole thing works, or talk to me about what disciplined AI delivery would look like in your organisation.