Logging a set into a commercial fitness app takes 20–40 seconds of tapping. Between sets you have 60–90. IRON is designed so that logging takes 10 seconds, max, every time.
Four design principles hold the line:
- Data-first — every pixel serves the measurement. No decorative UI.
- Noir + Acid — deep black background, acid green (
#BFFF00) only where it matters (the last number you care about). - Mono for data — JetBrains Mono for every measurable value; Bebas Neue for titles; Outfit for body.
- Log in 10 seconds — tap-tap-tap, no modal nesting, no confirmation popups between sets.
| Component | Choice | Why |
|---|---|---|
| Mobile | React Native + Expo (Expo Router) | File-based nav, fast iteration |
| Backend | FastAPI + SQLAlchemy + PostgreSQL | Typed, fast, boring |
| Auth | Apple Sign-In + Google OAuth + JWT | Mobile-native, no email/password |
| Health data | Apple HealthKit bridge | Bodyweight syncs automatically |
| AI | OpenAI (exercise suggestions, muscle mapping) | Targeted, not chat |
| MCP server | Python · FastMCP |
Lets Claude read/write your training data |
| Hosting | Railway (API + Postgres) | git push to prod |
- 🏋️ Sessions & sets — log a workout set by set (weight · reps · RPE), with templates for repeat sessions
- 📈 Progress — per-exercise history, 1RM (Epley/Brzycki), trend over time
- 🫁 Body sync — bodyweight pulled from Apple HealthKit, no manual entry
- 🧠 Smart exercise suggestions — AI proposes next logical exercises from your split and recent volume
- 🎯 Muscle taxonomy — every exercise is tagged with primary and secondary muscles, drives visualizations
- 🔌 MCP integration — point Claude at your IRON API and ask "how's my bench trending?" from your editor
- 👥 Multi-user — full data isolation via user-scoped foreign keys
┌────────────────────────┐
│ React Native (Expo) │
│ • (tabs) Lift/History │
│ • Session detail │
│ • Healthkit bridge │
└───────────┬────────────┘
│ HTTPS + JWT
▼
┌────────────────────────┐ ┌──────────────────────┐
│ FastAPI API │ │ MCP server │
│ • /auth (Apple/Google)│ │ • read/write tools │
│ • /sessions · /sets │◀───────▶│ • same DB │
│ • /bodyweight │ │ • for Claude / │
│ • /ai (suggestions) │ │ AI assistants │
└───────────┬────────────┘ └──────────────────────┘
│
▼
┌────────────────────────┐
│ PostgreSQL │
│ users · exercises │
│ sessions · sets │
│ bodyweight · templates│
└────────────────────────┘
.
├── app/ React Native (Expo Router)
│ ├── app/ file-based routes (tabs, session/[id], exercise/[id])
│ ├── components/ StatCard, SessionCard, ExerciseLog, SetRow, ProgressChart, ...
│ ├── lib/ api client, theme, healthkit bridge, 1RM math
│ └── assets/ fonts, icons
├── api/ FastAPI backend
│ ├── main.py app entry, lightweight live migrations
│ ├── models.py SQLAlchemy (User, Exercise, Session, Set, Bodyweight, ...)
│ ├── schemas.py Pydantic
│ ├── auth.py JWT + Apple + Google verifiers
│ └── routes/ auth, sessions, bodyweight, ai
├── mcp/ MCP server (Python · FastMCP)
│ └── server.py tools that talk to the same API
├── data/ sample session data (weights in kg)
├── docs/plans/ design & implementation docs
├── Muscles-iron.svg anatomy sprite used by the muscle visualizer
└── railway.toml deploy config
cd api
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # set DATABASE_URL, JWT_SECRET_KEY, GOOGLE_CLIENT_IDS
uvicorn main:app --reloadcd app
npm install
npx expo startcd mcp
pip install -r requirements.txt
export IRON_API_URL=http://localhost:8000
python server.pyThen wire it into Claude Desktop or your MCP-capable client.
| Variable | Purpose |
|---|---|
DATABASE_URL |
Postgres connection |
JWT_SECRET_KEY |
32+ chars, openssl rand -base64 32 |
GOOGLE_CLIENT_IDS |
Comma-separated Google OAuth client IDs |
OPENAI_API_KEY |
Smart exercise suggestions (optional) |
Apple Sign-In is verified against Apple's public JWKS; no client secret needed.
Single-developer project, extracted from personal use. The backend has been multi-user since day ~30 — data is scoped by user_id across all resources. The MCP layer is the more unusual piece: it lets an AI assistant treat your training data as a first-class resource.
Built by @thomasetienne
