← All projectspersonal product2026Under the hood ↓
PraxisOS
Ten tools' worth of daily tracking in one desktop app — and it never phones home.
A local-first command centre for a life: tasks, habits, training, food, focus, money, journal and notes, in one SQLite file with no account and no server.
- Role
- Personal product · solo, ongoing
- Stack
- ElectronReactTypeScriptSQLiteDrizzle ORMTailwindshadcn/uiTanStack QueryTiptapRechartselectron-updater
- Result
- 0 serversno account, no telemetry — the whole app is one local SQLite file on your machine
A Screens
Click any screen to open it in the zoom viewer. Wheel to zoom, drag to pan.
Under the hood
PraxisOS
Under the hood
PraxisOS
B Architecture
One write, end to end
There are no user roles — it's a single-person tool — so the flow that matters is the technical one: how a click in a panel becomes a durable row, and how every open window learns about it without polling.
One process owns the data
A three-surface desktop app over a single local database. Every window talks to the main process through a typed, context-isolated bridge; the main process is the only place SQL runs, and it broadcasts each change so the windows never disagree.
- What people use
- Code I wrote
- Where it is kept
- Someone else's service
A deliberately flat schema
Most panels own one or two tables no other panel reads, which is what keeps a feature change from rippling across the app. Only three real foreign keys exist — the places where a child record has no meaning without its parent.
C Decisions
The calls that made the difference, and what each one bought.
A focus timer that survives closing the window: the app parks in the system tray, and a frameless always-on-top mini widget keeps the running clock in view. Both windows are kept in exact sync by a broadcast from the one process that owns the data, so pausing in either is the same pause.
Every schema change ships a generated SQL migration applied at launch, with foreign keys toggled off on the raw handle around the migrator — Drizzle ignores that pragma inside its own transaction, and without it a table rebuild silently wipes child rows. So an install upgrades in place instead of losing data, and backups export and re-import through a versioned transformer.
D What it runs on
- Electron
- React
- TypeScript
- SQLite
- Drizzle ORM
- Tailwind
- shadcn/ui
- TanStack Query
- Tiptap
- Recharts
- electron-updater
personal product, source public on GitHub