Secure customer accounts
Registration, email verification, login, logout, and password reset are implemented end to end on the Rust authority, with matching Next.js pages. Verification tokens are single-use with a 24-hour expiry, reset tokens with one hour, and no flow except login mints a session. A seeded verified admin and subscriber make the whole loop testable on first run.
Opaque, hashed sessions
Login returns a 32-byte random token once; the database stores only its SHA-256 hash. Sessions expire after 12 hours absolute or 30 minutes of inactivity, enforced on every account call. Logout revokes only the presented session and is idempotent. A completed password reset revokes every session for the account in the same transaction.
Password policy and breach screening
Passwords must be at least 12 characters, pass a common-password list, and survive Have I Been Pwned range screening that sends only the first five characters of the SHA-1 digest (k-anonymity). Hashes are argon2id at 64 MiB, 3 iterations, 1 lane. An HIBP outage fails open to the static policy and logs a warning instead of blocking signup.
Brute-force and enumeration defenses
A 15-minute sliding-window limiter (per process) covers per-IP traffic, login per email and globally, registration and resend, reset requests, and token attempts. Five consecutive failures lock a loginable account for 15 minutes and email the owner. Every login attempt runs exactly one argon2 verify and returns one 401 shape, so responses and timing stay uniform.
Non-enumerating email delivery
Verification, reset, already-registered, and suspicious-login messages go out through Resend over TLS with text and HTML bodies and escaped interpolation. Unknown addresses are shaded, responses are padded to a 750 ms anti-enumeration budget, and network calls time out after 2 seconds. Missing or short Resend configuration fails startup rather than failing silently.
Provider-agnostic billing foundation
The application depends on a BillingProvider trait, not a vendor SDK. The bundled mock provider signs and verifies HMAC-SHA256 webhooks in constant time. subscription.activated, subscription.cancelled, and payment.failed events update entitlement state, unknown customers are a no-op success, and adopting a real provider means implementing three trait methods. It is a foundation, not a payment integration.
Admin surface
An admin-only page renders account state for the signed-in administrator and redirects non-admins to the customer home. Capability is read from the backend /v1/me response, never from the page or the client. The seeded dev@example.test account exercises the admin path locally.
Structured logging and audit trail
tracing emits one JSON object per line in production and readable lines locally. A closed event catalog covers lifecycle, HTTP, mailer, and security events, with request_id echoed on responses for correlation. The audit.* stream attributes by customer UUID only — never emails, tokens, passwords, or IPs — and secrets, tokens, and raw request bodies are never logged.
Trust boundary and fail-closed configuration
The browser talks only to the Next.js server; the backend origin and application secret exist only in server-side environment values, with no NEXT_PUBLIC_* exposure. Every account call proves the trusted caller with a constant-time secret check plus an exact-origin check, then authorizes the acting user from the presented session. Missing or weak required configuration stops startup.
One-command, idempotent local bootstrap
node scripts/bootstrap-dev.mjs creates the development and isolation-test databases, writes aligned backend and frontend .env files without overwriting existing values, applies migrations, and seeds verified accounts. It runs on Ubuntu 24.04, native Windows 11 PowerShell, and WSL, prints random passwords once, and is safe to re-run.
Two independently buildable trees
One git repository holds a Next.js 16 App Router frontend and a Rust 1.97 Cargo workspace (Axum, sqlx/PostgreSQL). There is no root package manifest or monorepo orchestrator, no path dependency in either direction, and the frontend reaches product data only through the backend REST API. Each tree builds and tests on its own.
Hardened CI, supply chain, and container
CI runs rustfmt, clippy with -D warnings, workspace tests, a release build on Rust 1.97, cargo audit, cargo deny, frontend audit-ci at high severity, ESLint, Vitest, a production build, and Playwright end-to-end tests. The backend image is a non-root (uid 10001) build with a /health healthcheck, published only by a manual workflow that smoke-tests it first.