Time Tracker
A self-hosted time-tracking app with projects, Kanban boards, risk management, and Craft.do sync.
Built with Next.js, TypeScript, Prisma, and SQLite/LibSQL.
Features · Quick start · Configuration · Architecture · Deployment
Time Tracker combines a focused personal timer with team-oriented project tooling. Track work against tasks, organize tasks into projects, plan work on per-project Kanban boards with assignments and deadlines, assess project risks on a 5×5 heatmap, and sync daily logs and risk reports to Craft.do — all from a single self-hosted Docker container with an embedded SQLite database or a hosted LibSQL backend.
Features
| Area | What is included |
|---|---|
| Time tracking | Start/pause/resume timers per task, accurate duration accounting across pauses, pause-on-switch between tasks, editable start/end times, and per-task notes with debounced autosave. |
| History | Browse completed tasks by date, filter by user, edit recorded times, and move time entries between tasks. |
| Projects | Group tasks under color-coded projects, archive and restore projects, or keep tasks standalone. |
| Kanban boards | Per-project boards with TODO/in-progress/done columns, drag-and-drop (dnd-kit), task assignments, deadlines, one-click time tracking from a card, and merging of duplicate cards. |
| Risk management | 5×5 likelihood/impact heatmap matrix, mitigation register with plans and status, risk-linked tasks for tracking mitigation effort, Craft.do risk-report sync, and executive-grade PDF export (jsPDF) with color-coded heatmap and register. |
| Craft.do integration | Per-user encrypted API credentials, sync of completed tasks and daily logs to Craft documents, folder selection, and connection testing from settings. |
| Team administration | First-run setup wizard, admin-managed user accounts, admin role toggles, user activation/deactivation, per-user timezones, and a team activity page. |
| Reader experience | Light/dark themes, responsive layout, and local-timezone-aware timestamps throughout. |
| Security | bcrypt password hashing, seven-day database-backed sessions, per-IP rate limiting (Cloudflare-aware), restrictive security headers with CSP, and AES-256-GCM encryption of stored Craft credentials. |
Technology stack
| Layer | Technology |
|---|---|
| Application | Next.js 16 App Router, React 19, TypeScript 5 |
| UI | Tailwind CSS 4, shadcn/ui (Radix primitives), next-themes, lucide icons, sonner toasts |
| Data | Prisma 6, SQLite by default, hosted LibSQL/Turso via @prisma/adapter-libsql |
| Authentication | Custom sessions with bcryptjs and random opaque tokens (database-backed) |
| Rate limiting | rate-limiter-flexible in middleware, honoring Cloudflare CF-Connecting-IP |
| Reporting | jsPDF + jspdf-autotable for risk-report PDFs |
| Testing | Bun test runner with happy-dom and Testing Library |
| Deployment | Multi-stage Dockerfile (standalone output), Docker Compose, Docker Hub image |
Quick start
Prerequisites
- Docker (recommended), or
- Node.js 20+ and npm for local development
- Bun (optional, for running the test suite)
Run with Docker
docker run -d \
-p 3000:3000 \
-e ENCRYPTION_KEY="replace-with-a-long-random-secret" \
-v ./data:/app/data \
--name time-tracker \
twlatx/timetracker:latest
The published image is available on Docker Hub as twlatx/timetracker.
Run with Docker Compose
git clone https://github.com/tunwinlat/TimeTracking.git
cd TimeTracking
cp .env.example .env # fill in ENCRYPTION_KEY
docker compose up -d
The compose file persists the SQLite database in ./data and runs database migrations automatically on container start.
Manual setup
git clone https://github.com/tunwinlat/TimeTracking.git
cd TimeTracking
npm install
cp .env.example .env # fill in ENCRYPTION_KEY
npx prisma migrate deploy
npm run dev
Open http://localhost:3000/setup. The setup wizard creates the first admin account; afterwards, sign in at /login. Admins can create additional users from the Admin page.
Configuration
Environment variables
| Variable | Requirement | Purpose |
|---|---|---|
ENCRYPTION_KEY |
Required for Craft.do integration; strongly recommended | Encrypts stored Craft.do API credentials with AES-256-GCM. A 64-character hex string is used directly as the key; any other value is SHA-256-derived into 32 bytes. Do not rotate without re-encrypting stored values. |
DATABASE_URL |
Optional (SQLite path) | Prisma connection URL for the local SQLite database. Defaults to file:./dev.db (or file:/app/data/dev.db in the container). |
TURSO_DATABASE_URL |
Optional (hosted LibSQL) | When set, the app uses this LibSQL/Turso database instead of local SQLite. Requires TURSO_AUTH_TOKEN. |
TURSO_AUTH_TOKEN |
Required with TURSO_DATABASE_URL |
Authentication token for the hosted LibSQL database. |
Generate a suitable ENCRYPTION_KEY with:
openssl rand -hex 32
Environment files are ignored by Git. Keep these values private and use different secrets per deployment.
Note: authentication is fully custom (bcrypt + database sessions) — the app does not use NextAuth, and no
NEXTAUTH_*variables are needed.
Database behavior
Time Tracker supports two database modes, selected automatically at startup:
- Local SQLite (default): Prisma runs against a
file:database. The container entrypoint applies pending migrations withprisma migrate deploy; bind-mount./datato persist the database file. - Hosted LibSQL/Turso: When
TURSO_DATABASE_URLis set, Prisma connects through the LibSQL adapter and the bundled migration runner (scripts/migrate-libsql.ts, compiled during the Docker build) applies any pending migrations fromprisma/migrationsidempotently on container start, tracked via the standard_prisma_migrationstable.
Migrations run automatically at container startup in both modes — no manual step is required after pulling a new image.
Craft.do integration
Each user configures their own Craft.do API credentials under Settings → Craft Integration. Credentials are stored encrypted in the database when ENCRYPTION_KEY is set. Once connected, completed tasks and daily logs sync to Craft documents, and project risk reports can be pushed to Craft directly from a project's Risks tab.
Architecture
flowchart LR
Users[Users] --> UI[App Router UI]
UI --> API[API route handlers]
API --> MW[Rate-limit middleware]
MW --> Services[Auth, tasks, projects, Kanban, risks]
Services --> Prisma[Prisma Client]
Prisma --> SQLite[(Local SQLite)]
Prisma -->|LibSQL adapter| Turso[(Hosted LibSQL / Turso)]
Services -->|encrypted credentials| Craft[Craft.do API]
Repository layout
TimeTracking/
├── app/ # Public app pages, admin, settings, and API routes
├── components/ # Shared UI (tasks, Kanban, risks, projects, dialogs)
├── hooks/ # React hooks
├── lib/ # Auth, DB, encryption, rate limiting, Craft client, PDF export
├── prisma/ # Schema and migrations
├── scripts/ # Docker entrypoint, LibSQL migration runner, build helpers
├── Dockerfile # Multi-stage standalone build
└── docker-compose.yml # Single-service deployment
Development
| Command | Purpose |
|---|---|
npm run dev |
Start the development server. |
npm run build |
Create a production build. |
npm run start |
Start the production server. |
npm run lint |
Run ESLint. |
bun test |
Run the Bun test suite (lib/*.test.ts, component tests). |
npx prisma migrate dev |
Create and apply a migration during local development. |
npx prisma generate |
Regenerate Prisma Client. |
Deployment
The recommended deployment is the published Docker image or the included Compose file:
- Run the container with a persistent volume for
/app/dataand a strongENCRYPTION_KEY. - Optionally set
TURSO_DATABASE_URL+TURSO_AUTH_TOKENto use a hosted LibSQL database instead of the embedded SQLite file. - Visit
/setupto create the first admin account. - Each user can connect their Craft.do account under Settings → Craft Integration.
SQLite is well suited to individual or small-team use. For a multi-user deployment behind Cloudflare, rate limiting automatically uses the CF-Connecting-IP header for accurate per-client limits.
Security
- Password hashing with bcrypt (12 rounds) and seven-day database-backed sessions
- Per-IP rate limiting on all API routes, with stricter limits on login, setup, user management, and password-change endpoints
- Restrictive response headers including a Content Security Policy,
X-Frame-Options: DENY, HSTS, and a locked-down Permissions Policy - AES-256-GCM encryption of stored Craft.do API credentials
- Server-side authorization checks, including protection against deactivated users
Review SECURITY_FIXES_SUMMARY.md and the reports under Security Reports/ for the audit history of this codebase. Please report suspected vulnerabilities privately rather than opening a public issue.
Known limitations
- History rendering: Very large datasets may impact browser rendering speed (pagination pending).
- SQLite concurrency: Best suited for individual or small-team use; use a hosted LibSQL database for larger teams.
- PDF fonts: Risk-report PDFs support standard characters; some emoji or complex Unicode symbols may fall back to standard glyphs.
License
Time Tracker is licensed under the Mozilla Public License 2.0. Modifications to MPL-covered files must remain available under the MPL when distributed.