← 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
Ten tools, one window
Counters deep-link into each panel, beside today's agenda and a weekly focus chart. All of it local, with no server to reach.
A timer that outlives its window
The focus timer pops into a small always-on-top panel and keeps counting while you work in something else entirely.
A session engine, not a checklist
Preview, work and rest per set, handling time-based and rep-based exercises without branching into two interfaces.
Search across everything written
Rich notes with inline images, indexed by SQLite full-text search over title, body and tags. Local, and instant.
Money tracked without an account
Income, expense, transfer and debt, with spend by category. Nothing leaves the machine, because nothing needs to.
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