Back to work

// local-first · time tracking · knowledge graph · AI

Koku

刻 Koku is a local-first time tracker, notes workspace, and AI assistant. Track your work, connect your notes into a knowledge graph, and generate stand-ups and monthly reports with the LLM provider of your choice, all with your data stored in your own browser. Time entries, notes and settings live in IndexedDB. Nothing leaves the device except the AI requests you explicitly trigger, sent directly to the provider with a key you supply.

Role

Solo design & build

Stack

Next.js 16 · React 19 · TypeScript · Dexie

Status

v0.2.1 · active

Year

2026

GitHub ↗ Back to work

The problem

Time trackers are usually one of two things: a cloud silo that owns your data, or a dumb stopwatch with no memory of what the time was actually spent on. Notes live somewhere else entirely, a separate app, a separate login, so the record of what you did and the record of what you learned never meet each other.

I wanted one workspace that tracks time, holds notes, and can reason about both, without asking me to trust it with my data by default. Mostly because I didn't trust myself to remember which cloud service I'd signed up for last.

What I built

Koku is a Next.js app that runs entirely against the browser's IndexedDB by default, with an optional cloud mode (sign-in, sync, and a small admin workspace) built on the same codebase:

32.8Klines of TypeScript
250source files
12local tables, schema v6
247test cases

Two modes, one codebase

The same build ships as a zero-config local app and as a multi-tenant cloud app, gated by a single LOCAL_MODE flag.

Local-first

Your data never leaves the device

Every table lives in IndexedDB via Dexie, read through live queries so every view stays current with no polling. Export everything as one JSON file at any time. No lock-in, no "contact support to get your data back."

  • Dexie 4 · 12 tables · schema v6 with migrations
  • Zero config, works offline as an installable PWA
  • One-click setup.sh / .command / .ps1 self-host
  • No account, no server, no telemetry by default
Optional cloud

Same app, hosted for a team

Flip the flag and the app gains Zoho Catalyst hosted auth, an outbox-pattern sync engine per table, presence tracking, and an admin workspace for user and usage management.

  • Catalyst Datastore mirrors every local table
  • Revision-based conflict rejection on push
  • Admin: user/group management, per-user usage detail
  • Personal notes stay excluded even in cloud mode

Design decisions

A few calls I'm happy with:

The awkward bit: the Picture-in-Picture mini player. A PiP window starts as an empty document that inherits no stylesheets at all, so it opens unstyled, in Times New Roman, like it's 1998. adoptedStyleSheets couldn't carry the @import "tailwindcss" that globals.css opens with, so the styles get transplanted manually instead. The window's lifecycle also has to live outside React: StrictMode double-invokes effects in dev, and the transient user activation the PiP API requires gets spent on the first call. Open it from inside an effect and it'll open, tear itself down, and then simply refuse to reopen. Ask me how long that one took to track down.

Highlights

Links