← 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
Access
personal product, source public on GitHub
visit live site →read the source →

A Screens

Click any screen to open it in the zoom viewer. Wheel to zoom, drag to pan.

01Everything at a glance: clickable counters that deep-link into each panel, today's agenda, a consistency strip and the week's focus hours.
02The timer pops out into a small always-on-top window and keeps counting while you work in another app — the two clocks never drift.
03A session engine that walks preview → work → rest per set, with time-based and rep-based exercises and superset handling.
04Not just courses — books, projects and deliberate practice too, grouped by the skill area you choose.
05Rich text notes with inline images, backed by SQLite full-text search over title, body and tags.
06Income, expense, transfer and debt, with a spend-by-category chart. The currency sits after the amount, the way it reads locally.
07Daily, weekly or custom cadences with streaks. Any past day can be checked in, so training outside the app still counts.
08Eight built-in themes plus custom presets that inherit their base theme and change only the accent.

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.

  1. 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.

  2. 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

↑ Back to the surface