An OS-agnostic, self-hosted execution system: 90-day Seasons, a hard 168-hour budget, calendar-bound time blocks, passive activity auditing, and coaching that runs on a local model.
Built from the analysis in Superfocus Architecture and Replication.pdf, with
the macOS-specific parts of that design replaced by portable equivalents. See
docs/ARCHITECTURE.md for the reasoning.
Runs the same on Windows, macOS and Linux. Nothing in the core knows which one it is on.
packages/core domain model, Plan Strength scoring, prompts, PORTS (no I/O, no OS)
packages/adapters the only code allowed to touch a machine or a network
packages/db Drizzle schema + TimescaleDB migration
apps/web Next.js UI and API
apps/desktop Tauri v2 shell (tray, hotkeys, notifications)
infra Docker Compose, Dockerfile, Caddyfile
No database server, no Docker, no install:
npm install
npm run db:migrate # creates an embedded Postgres under ./data
npm run db:seed # optional demo season
npm run dev # http://localhost:3210
npm test # 88 tests, no services required
npm run build:check # production build that will not disturb `npm run dev`DATABASE_URL defaults to pglite://./data/superfocus-db — PGlite,
real Postgres compiled to WASM, running in-process. The schema, the SQL and the
query layer are identical to a server; point DATABASE_URL at a
postgresql:// URL whenever you want one.
Optional Docker stack (Postgres + Timescale, Redis, the app, and Ollama behind
--profile gpu):
cp .env.example .env # set DB_PASSWORD
npm run stack:upEverything except enforcement and coaching. SoftBlocker and ManualTracker
are always available on every platform, so a session always starts — the UI just
tells you plainly that nothing is protecting it.
All of it goes in the .env at the repository root, and the app must be
restarted to pick it up.
Add capability incrementally:
| Want | Set | Notes |
|---|---|---|
| Block distracting domains | PIHOLE_BASE_URL + PIHOLE_API_TOKEN |
Recommended. No admin rights, covers phones too |
| Block on this machine only | ENABLE_HOSTS_BLOCKER=true |
Needs Administrator / root |
| Passive activity audit | SCREENPIPE_URL |
Install Screenpipe on the host, not in Docker |
| Slack DND + status | SLACK_USER_TOKEN |
Scopes: users.profile:write, dnd:write |
| AI coaching | INFERENCE_BASE_URL |
Any OpenAI-compatible endpoint. Without it, coaching still runs rule-based |
| Google Calendar | GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET |
Then click Connect in Settings; the refresh token is stored for you |
| Any other calendar | CALDAV_URL + CALDAV_USERNAME + CALDAV_PASSWORD |
Nextcloud, Radicale, Fastmail, iCloud, Synology |
GET /api/adapters reports what is actually usable right now.
- Screenshots are never stored. Screenpipe extracts text on the host; only text reaches the database, capped at 2000 characters per sample.
reality_logshas a 90-day retention policy when the Timescale migration is applied.INFERENCE_ALLOW_REMOTE=false(the default) refuses any inference endpoint that is not loopback or RFC1918, so telemetry cannot leave the machine by accident. Every coaching audit recordswas_remotepermanently.
The whole loop runs — plan the day, protect it, do the work, learn from it:
| Screen | What it does |
|---|---|
Compass / |
Plan Strength and its four components, today's schedule, live shield status |
Plan /plan |
The day drawn to scale; add and delete blocks, edit Major Moves and confidence |
Focus /focus |
Engages the shield for a block, counts up, always releases on exit |
Reflect /reflect |
Plan Strength against adherence, observed divergence, coaching |
Seasons /seasons |
Create and edit seasons, goals, why-statements, anti-goals |
Settings /settings |
Timezone, adapter status, Google/CalDAV calendar sync |
Focus sessions record what the shield actually managed on this machine, so a retro can tell "I got distracted" apart from "nothing was blocking me".
Coaching runs at 07:00 and 21:00 in your timezone, plus a weekly review. With no model configured it still runs, from a deterministic rule-based composer, and says so rather than pretending a model wrote it. Every audit records whether the request left your machine.
With no SUPERFOCUS_PASSWORD set the app is open — right on your own
machine, wrong anywhere else, and the Settings screen says so rather than
leaving it ambiguous. Set it (plus SUPERFOCUS_SESSION_SECRET) and every page
sits behind a login, with a 30-day signed cookie verified in middleware.
This is single-user by design: one password, no account creation, no reset.
getPrimaryUserId takes the oldest account. If you need real multi-user
accounts, this is the piece to replace.
Note: PGlite is single-process. Stop
npm run devbefore runningdb:seedor any script against the samepglite://directory, or the dev server will keep serving stale rows.