Two P3 multi-user hardening fixes from the 2026-06-13 full-eval. Login timing oracle: both login paths (the UI server action and POST /api/auth) returned immediately on an unknown email but ran bcrypt.compare when the email matched a user, so response latency revealed which emails have accounts. New verifyPasswordOrDummy() in lib/auth runs bcrypt against a fixed dummy hash when there is no user, so every attempt spends exactly one bcrypt; the two error branches in each route collapse into one. exerciseId ownership: exercises are per-user, but the workout create / PATCH (set-replace) / add-sets and CSV import-save routes wrote SetLogs from a client-supplied exerciseId with no ownership check — letting a user attach another user's exercise to their own workout, which leaks that exercise's name/notes on fetch and wires up a cross-user onDelete: Cascade link. All four now reject unowned ids with 400 via the shared lib/exerciseOwnership helper; the pre-existing inline checks in both programs routes are refactored onto the same helper. App-code only — no schema, no API contract change, no data migration.
Workout Planner
A self-hosted workout planner and logger. Plan training cycles, log daily workouts, search your history, and get AI-powered suggestions over time.
Quick Start
# Install dependencies
npm install
# Set up the database
npx prisma db push
# Seed the InstanceSettings singleton
npm run db:seed
# Create the first admin (fresh installs ship with NO users — see below).
# Use a real-looking email; "admin@local" is rejected (no TLD).
npm run create-admin -- you@example.com yourpassword "Your Name"
# Start development server
npm run dev
Open http://localhost:3000 and log in with the email/password you just created.
No default account. Fresh installs ship with zero users on purpose, so there
are no default credentials to forget and leak. In production (StartOS) the
operator creates the first admin via the Actions → Set admin credentials
action; locally, npm run create-admin is the equivalent. Once an admin exists,
additional users sign up at /auth/signup (if sign-ups are enabled in Settings).
Access from Other Devices
To access from your phone or iPad on the same network:
npm run dev -- --hostname 0.0.0.0
Then open http://<your-computer-ip>:3000 on your device. You can install it as a PWA (Add to Home Screen) for an app-like experience.
Docker Deployment
docker compose up -d
Tech Stack
- Next.js 14 (App Router) — full-stack TypeScript
- SQLite + Prisma ORM — local database, no separate server
- Tailwind CSS — mobile-first responsive design
- PWA — installable on any device
Project Structure
app/
auth/login/ — Login page
main/
dashboard/ — Quick stats and recent workouts
workouts/ — Workout history, logger, detail views
exercises/ — Exercise library
settings/ — Preferences and AI config
api/ — REST API routes
components/ — Reusable UI components
lib/ — Database queries, auth, utilities
prisma/ — Schema and seed data