№ X · 2026 · parked, not buried
Mintly
An approval-grade shell for a crypto-only get-paid-to site. 50 commits, 38 backend tests, 12 Playwright E2Es, all green. Built in five days end-to-end as a discipline study — and deliberately never launched. The code is clean. The market wasn't.
What it is
Mintly is the public-facing shell of a get-paid-to (GPT) site — landing, sign-up, verify, dashboard, withdraw, offer browse, the legal shelf. Real auth, real append-only ledger, a dev-only "complete this offer" button that exercises the full credit/release/confirm/withdraw lifecycle through a background scheduler. If an offerwall partner ever connected, the data shape on the dashboard would not change — only the source would flip from a JSON seed to a live API.
Why it exists
I wanted to find out how far I could push a discipline I'd been sketching for months: brainstorm to spec to plan to execution, with everything written down, every change reviewed, every test green. Crypto GPT was the test bed — a domain I understood, a market I knew was hard, a scope I could bound to one sub-project of seven without pretending I was launching a real business.
The output is the most production-ready unlaunched code I've ever written, and a clear answer to the implicit question: is the bottleneck building this, or distributing it? The bottleneck was never the build.
How it was built
Five days, one operator, one terminal session per phase. Process was rigid and deliberately so — the goal was as much "see if this loop scales" as "ship the site."
- Brainstorm. Niche, scope, stack, visual direction, brand placeholder — locked through a guided multiple-choice flow. No premature code.
- Spec. A single design document — architecture, data model, auth flows, ledger invariants, dev-complete mechanics, copy direction, testing strategy, deployment runbook, explicit out-of-scope. Reviewed and approved before any task was written.
- Plan. 46 tasks across 11 phases, each with exact files, exact code, exact commands, an explicit failing test before any implementation. Self-reviewed against the spec for placeholders, contradictions, and ambiguity.
- Execution. A controller dispatched a fresh subagent per task. Two-stage review after each: spec compliance first, then code quality. Implementer fixed, reviewer re-checked, and only then did the next task start. ~50 commits, no batching, no shortcuts.
- Polish. A second pass after the functional build, this time on visual quality — typography overhaul, asymmetric hero, refined offer grid with category tabs, split-context auth pages with a faux-dashboard preview, custom accordion, trimmed footer. The shell stopped reading as generic AI MVP and started reading as real product.
Architecture
┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Astro 4 SSR │ ←→ │ FastAPI 0.110+ │ ←→ │ Postgres 16 │
│ React islands│ │ async SQLAlchemy │ │ user_balances │
│ Tailwind 3 │ │ JWT-free sessions│ │ (SQL view) │
└──────────────┘ └────────┬─────────┘ └──────────────────┘
│
▼
┌──────────────────┐
│ APScheduler │
│ (hold release │
│ every 30s) │
└──────────────────┘
Single-VPS docker-compose: postgres + api + web behind nginx. Sessions are opaque
cookies stored as sha256(token) server-side, not JWTs — simpler
revocation, no parsing surface. The ledger is append-only; balance is a
VIEW, never a stored counter. Every dollar that enters the system
traverses production code paths, even in dev — the only thing the dev-complete
button does is shortcut which dollar.
The ledger invariants
The most load-bearing decision in the spec is that the ledger is append-only and the balance is derived. Three rules, non-negotiable:
- Append-only. No
UPDATEonledger_entriesfrom app code, ever. State transitions = new rows, linked throughparent_id. - Balance is a query. A SQL view sums signed amounts by entry kind. If it ever disagrees with reality, the bug is upstream of arithmetic.
- Money is
numeric(10,4)USD. Never float. Crypto conversion happens at withdraw time, not in the ledger.
The withdraw endpoint refuses negative available_balance at the
application layer; a property test asserts no user-reachable code path can drive
it negative. The endpoint exercises the full hold→reverse round trip even though
payouts aren't wired — when they are, the only change is replacing the immediate
reverse with a real settlement marker.
Why it isn't live
The shell is the easy 30% of "make this work." The hard 70% is operational and external:
- Partner approval. Offerwall providers (AdGate, Lootably, BitLabs, CPX Research) gate API access behind manual site review. Most expect a real legal entity on the operator side before they'll pay out.
- Distribution. Crypto GPT is a brutally contested space with established players (Cointiply, Freecash, FreeBitco.in) and the only realistic on-ramps are SEO (3-6 month payoff) or influencer affiliates (cold outreach with no warm channel).
- Legal entity. SK s.r.o. or offshore — €500-1500 plus 3-6 weeks plus an accountant retainer.
None of these are technical problems. All of them are real ones. With Receptiv as the active income grind and no warm crypto distribution to leverage, the opportunity cost was clear. The right move was to bank the polish as a study, write down what would have come next, and not pretend a partial answer was the whole one.
Stack
- Backend: Python 3.12, FastAPI 0.110, SQLAlchemy 2.0 async, asyncpg, Alembic, slowapi (rate limiting), bcrypt, APScheduler, Resend (transactional email), hCaptcha
- Frontend: Astro 4 SSR (node adapter), React 18 islands, Tailwind 3 with a custom token system + typography plugin, vitest, @testing-library/react
- DB: Postgres 16 with citext + pgcrypto, append-only ledger, SQL view for derived balances
- Tests: 38 backend (pytest async), 4 web unit (vitest), 12 web E2E (Playwright) — every flow covered including signup → verify → reset → re-login and earn → dev-complete → balance → withdraw round-trip
- Infra: Single VPS, docker-compose, nginx reverse proxy, certbot for TLS, runbook written for `--expand` SAN extension
What's actually here
The repo is a self-contained reference for solo full-stack work at a quality bar
I'd be willing to ship. Spec, plan, runbook, and acceptance template all live in
docs/. Every commit message is a complete sentence. No TODOs, no
placeholder TODOs, no "Add error handling" in any handler. The five
post-build polish commits are explicitly labelled — Header logout fix, dev-tools
env-source fix, pytest baked into Dockerfile, Resend bypass logging, password
reset E2E coverage — so future-me knows exactly what was punted and why.
If the conditions ever change — a warm distribution channel appears, an influencer asks the right question, the regulatory picture shifts — the shell is already there. Until then it's a study in shipping discipline more than a study in get-paid-to.