Phase 4a: Backend foundation for dashboard + billing
Phase 4a
Backend prerequisites for the Next.js dashboard (4b) and Stripe billing
(4c). No UI yet — everything in this release is HTTP API surface and the
DB schema that supports it.
Schema (migrations 002 + 003)
- New
api_keystable: one customer can hold many named keys
(lp_live_<32 hex>) withlast_used_at+is_active. Existing
single-key customers backfilled;customers.api_key_hashdropped. customersgainsplan(defaultfree) andstripe_customer_id
(nullable).- New
usersandsessionstables for dashboard auth (argon2id +
sliding sessions, 30d sliding / 90d absolute cap).
Auth
POST /auth/register | /auth/login | /auth/logout— email + password,
cookie-based sessions (lp_session,HttpOnly,SameSite=Strict,
Securein prod).require_user_sessionmiddleware extracts the session in a single
UPDATE ... FROMJOIN, injectsuser_id+CustomerId(Uuid)into
request extensions.- Login returns identical
Unauthorizedfor unknown-email and bad
password — no user enumeration.
Read endpoints (programmatic, API-key auth)
GET /v1/proofs?cursor=&limit=— cursor-paginated, newest first.
Opaquebase64url(unix_micros:uuid)cursor.GET /v1/usage— current month + 12-month history + plan + quota.
Dashboard endpoints (session-cookie auth)
GET /dashboard/proofs | /dashboard/usage— same DB helpers as/v1.POST /dashboard/keys | GET /dashboard/keys | DELETE /dashboard/keys/:id—
customer-scoped key management. Delete uses
WHERE id AND customer_idso cross-tenant probing returns 404.
Stripe linkage
- If
STRIPE_SECRET_KEYis set at boot,/auth/registercreates a
Stripe customer and persists the id. If unset,stripe_customer_id
stays NULL and registration still succeeds.
Plans (read by 4c)
free=100, starter=5000, growth=25000, enterprise=u32::MAX —
single source of truth in api/src/plan.rs.
Tests
api/tests/dashboard_e2e.rs covers register → mint key → use key on
/v1 → revoke → expect 401 → cross-tenant probe 404 → logout → stale
cookie 401, with FK-safe Postgres cleanup. Total: 30 tests pass
(cargo test --workspace).