Under the hood
A technical tour of how FocusNexus works: the motivation, the design goals it led to, and the key choices those goals forced — a single-file local-first application, your data as plain JSON in your own Google Drive, and team collaboration with no server at all.
FocusNexus began as a working scientist's answer to a familiar failure mode: the week is a mix of long-horizon goals (grants, papers, a lab's trajectory) and a relentless stream of small obligations, and the tools on offer either fragment that mix across several apps or centralize it on someone else's server, behind someone else's account, under someone else's terms. Every mainstream organizer asks for the same trade — convenience in exchange for custody of your working memory.
The founding question was whether that trade is actually necessary. A modern browser is a capable runtime; Google Drive (which most academics already have) is a durable, user-owned file store with a permission model narrow enough to be trustworthy; and cryptography in the browser is now standard. Could a serious organizer — daily digest, prioritization views, time blocking, recurring work, even small-team collaboration — be built so that no server exists anywhere in the design, and the user's data never leaves accounts the user owns? FocusNexus is the running answer, refined in daily use, and doubles as a modest experiment in how far the serverless idea can be pushed.
Six goals govern the design. Each one forced a concrete choice, and most of the architecture falls out of taking them seriously at the same time.
No server, no account, no database, no analytics. Choice: the entire application is one static HTML file served from GitHub Pages. There is no backend because there is nothing for a backend to do.
Data must live where the user can see, copy, and delete it — as readable files, not opaque rows. Choice: everything is plain JSON in a FocusNexus folder in the user's own Drive, under Google's narrowest OAuth scope (drive.file): the app can touch only files it created itself and is structurally blind to the rest of the Drive.
Networks drop, laptops sleep, and mistakes happen; none of it should cost data. Choice: local-first operation (every edit lands in the browser first, Drive follows), an offline mode that keeps you editing on the device and, when the connection returns, uploads on its own if nothing else changed — or asks which version to keep, never guessing — and one snapshot file per calendar day — the 30 most recent kept — with a one-click revert to yesterday.
An app you can audit in a single read, save locally, and reopen in a decade. Choice: a single self-contained HTML file — no build step, no bundler, React loaded as two pinned script tags. The price is paid in discipline instead: releases are made by scripted, checksum-verified surgical edits, and every release must clear a regression gate that now runs eighty test suites.
Small teams should share a board without anyone standing up a service. Choice: a team is nothing but a shared Drive folder plus cryptography — each member writes only their own file there, every member's app merges all the files deterministically, and access is controlled by an invite string and a passphrase rather than by a server checking identities.
People are email addresses, not storage accounts. Choice: the userID (the email shown on cards and to teammates) is deliberately separate from the googleID (the Google account whose Drive holds the data). The app pins the storage account it first syncs with and refuses to mix data across Google accounts — a guard against the classic multiple-sign-in accident. Moving to another Google account is therefore explicit: the account dialog switches accounts, and a forwarding note left in the old account (a movedTo field in its newest file) makes every other device pause and offer the switch instead of syncing to the old account.
At runtime, FocusNexus is a React application holding one document in memory: the state document described below. Every view — the Daily Digest, the Eisenhower matrix, the time-blocking board, the projected-workload chart, the calendar — is a pure function of that document; there are no per-view databases to drift out of sync. Every edit updates the document, which is written to the browser's local storage immediately and to Drive on a debounced schedule. Reads on startup go the other way: local storage boots the app instantly, and Drive reconciles behind it.
The single-file constraint looks like an aesthetic and is actually load-bearing. It makes the app auditable (the entire program is one view-source away, matching the privacy claims to something checkable); it makes it portable (a saved copy is a working app — the header's icon even hides itself gracefully when the icon assets aren't beside it); and it removes the build pipeline as a failure surface and a dependency treadmill. The engineering cost is real — you cannot casually refactor a 450 KB file — and it is paid with process: every release is produced by a small Python script of exact-count, checksum-asserted edits against the previous version, and must pass the full regression gate (currently 80 automated suites, from pure-function merge tests to two-client end-to-end simulations over a faked Drive) with zero failures before it ships.
Four object shapes carry the whole system. The first is the heart; the others are wrappers and envelopes around it.
roles[] roles — the hats you wear (role menu ▾)
└ sections[] areas within a role
└ items[] goal | project | task
(a subtask is a task whose
parent is a task — parentId)
sibling maps, keyed by item id:
status · deadlines · notes · links
whens · recurs · costs
personal-only: focus[] · timeBlocks
focusSessions · settings · scratch note
One document, many views: Digest, Eisenhower, time-blocking, workload and calendar are all pure functions of this object. Item facts live in flat maps beside the tree, so moving an item never rewrites its metadata.
FOCUSNEXUS-YYYYMMDD-HHMMSS.json = the state document, verbatim one file per calendar day same-day saves overwrite in place 30 most recent kept · older pruned
Yesterday's file is the backup. The app's revert button loads it; anything older is a normal file in your Drive, openable by hand.
= a state document, plus: author who wrote this file _ts write stamp (ms) tstamps per-item edit stamps (ms) tombstones per-item deletion stamps people roster the author has seen meta team name · owner + team maps: assignee · assigner
Millisecond per-item stamps exist because "newest wins" needs finer granularity than the personal day-level dates. Tombstones make deletion a fact that can win a merge, not an absence that loses one.
FNTEAM1:base64{
n team name
f hub folder id
b credential blob —
PBKDF2-SHA256 ×600,000
→ AES-256-GCM
c OAuth client id }
The invite is safe to send over ordinary channels: without the passphrase (told separately) the blob is ciphertext. The passphrase is never stored anywhere — the decrypted credential lives only in memory, and locking the team clears it.
The usual answer to "several people, one board" is a server that owns the truth and referees concurrent edits. FocusNexus refuses the server, so the truth has to be a function instead: every member's app computes the shared view by merging all member files with a deterministic, order-independent procedure. If two clients hold the same set of files, they render the identical board — there is nothing else to consult.
The merge is per-item, newest-wins. Every edit stamps the touched item with a millisecond timestamp in the author's file; the merged view takes, for each item independently, the version with the newest stamp (ties broken deterministically by file write-stamp, then author, so document order never matters). A deletion writes a tombstone stamp, and the tombstone kills the item exactly when it is at least as new as the item's last edit — which means a genuinely newer edit resurrects it, and a stale file carried forward by an absent teammate can never overrule either outcome. The procedure is idempotent and permutation-stable, and a dedicated unit suite proves both properties on every release.
Everything else about teams follows from the merge. Assignments (By:/For: on every card) are just more per-item facts riding the winning item. A member who edits offline simply has a stale file until they reconnect — and stale files, by construction, cannot clobber newer work. And because the hub is a plain folder, "rotating" a compromised or expired credential swaps an encrypted blob and issues a fresh invite while the folder and every file in it stay put.
Honest limits matter more in a serverless design, because there is no operator to compensate for them. The cryptography does three jobs: the invite's credential blob is unreadable without the passphrase (PBKDF2-SHA256 at 600,000 iterations feeding AES-256-GCM); the decrypted credential exists only in memory and is discarded on lock; and a wrong-account credential is rejected before anything is saved, because under drive.file only the account that created the hub folder can even see it.
What the design deliberately does not do: there are no per-item permissions inside a team — the trust boundary is team membership itself, which is the honest boundary for a small lab group sharing one board. Item timestamps come from members' own clocks, so "newest wins" inherits ordinary clock accuracy; the deterministic tie-breaks keep the outcome consistent everywhere, and at human editing timescales skew is a non-issue, but this is a small-team design, not a distributed database. And once someone can read the board, nothing technical prevents them copying it — as with any shared document, revocation (a credential rotation) protects the future, not the past.
Recognizing the possibilities inherent in new AI coding engines, and my long running dissatisfaction with other methods to address planning and time management challenges, I started developing FocusNexus in feb/march 2026. The current version represents approximately 200 iterative updates to the code base. I use FocusNexus constantly. The application is written and maintained in Claude cowork sessions: an AI coding agent does the implementation work while I direct it, setting the goals, judging behavior against daily use, and deciding what ships to github. The division of labor has stayed constant across every release: design intent, feature decisions, and acceptance criteria, are human (JOD); the mechanics of implementing, testing, documenting, as well as security analyses of each change are delegated to Claude.
The single-file constraint shapes the process as much as the product. A ~450 KB HTML file is hostile to hand edits, so no release is edited by hand: each version is produced by a short Python build script that applies an exact list of transformations to the previous version, with every transformation required to match the file an exact number of times — an unexpected count aborts the build instead of corrupting it. The scripts live in the repository beside the app, so each release is reproducible from the one before it.
Nothing ships on inspection alone. A regression gate — unit tests plus end-to-end suites exercised against the built file, eighty suites as of v1.25 — must pass on the exact bytes being released. And every release carries a handoff document recording what changed, why, and what was learned, so the next working session — human or AI — starts from the design's actual state rather than re-deriving it.
Version control is deliberately ordinary: a local git repository on the author's machine, mirrored to GitHub, one tag per release. The same repository is the deployment mechanism — a GitHub Actions workflow publishes the app and these pages to GitHub Pages on every push, and a verification script closes the loop by confirming that the live site matches the repository before a release is called done. Like the app itself, the process runs on nothing but files, folders and history — no build server, no extra infrastructure, nothing to maintain but the work.
A deeper design document — the full schema, the merge algorithm's edge cases, and the reasoning behind individual trade-offs — is planned; this page is the map, not the survey. For the practical tour, start with Getting started; for what the app does day to day, the homepage; for data handling in plain language, the privacy policy.