Tun's Random Thoughts
BlogProfilePhoto Gallery
All Projects
TimeTracking

In Progress

TimeTracking

View on GitHub
Tech Stack
TypeScript

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.

Next.js 16 License: MPL-2.0 React 19 TypeScript Prisma 6 Self-hosted

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 with prisma migrate deploy; bind-mount ./data to persist the database file.
  • Hosted LibSQL/Turso: When TURSO_DATABASE_URL is 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 from prisma/migrations idempotently on container start, tracked via the standard _prisma_migrations table.

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:

  1. Run the container with a persistent volume for /app/data and a strong ENCRYPTION_KEY.
  2. Optionally set TURSO_DATABASE_URL + TURSO_AUTH_TOKEN to use a hosted LibSQL database instead of the embedded SQLite file.
  3. Visit /setup to create the first admin account.
  4. 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.

© 2026 Tun's Random Thoughts. All rights reserved.