A single-user, self-hosted TODO web app for personal task management. It combines a local Node.js HTTP server with a React + TypeScript frontend, stores all runtime data on disk as JSON, and includes a PM2 release workflow for running a stable packaged snapshot while development continues in the working tree.
The product is designed as a private personal workspace, not a team SaaS app. It supports task planning, project organization, calendar-style review, reports, markdown notes, checklist subtasks, attachments, import/export, and PWA installation.
- Single-user password login with an HttpOnly session cookie
- Workspace views: Today, Inbox, Upcoming, Recent 7 Days, Calendar, Matrix, Reports, Projects, Tags, Smart Filters, Completed, Closed, and Settings
- Task create/edit/complete/close/delete flows with priority, urgent flag, tags, projects, sections, due date ranges, reminder ranges, recurrence, markdown notes, checklist subtasks, and attachments
- Project, tag, and smart filter management
- Browser notification permission and test notification flow
- JSON data export/import with preview
- Attachment upload/download/delete plus ZIP export/import
- PWA manifest and service worker with versioned cache busting
- Release snapshots under
.deploy/releases/for PM2 deployment
- Runtime: Node.js 20+
- Server: native Node.js HTTP server
- Frontend: React, TypeScript, Vite
- Storage: local JSON files and local upload files
- Process manager: PM2, optional for deployment
server.js HTTP server, API routes, auth cookie handling, static file serving
src/auth.js password hashing, password validation, session token helpers
src/storage.js JSON store, seed data, normalization, attachment paths
src/client/ React + TypeScript frontend source
public/ production shell, manifest, service worker, built frontend assets
assets/ legacy/prototype static assets
design/ static design prototypes
docs/plans/ implementation notes and product planning docs
scripts/package-release.mjs
tests/ Node test runner suites
ecosystem.config.cjs PM2 deployment config
Install dependencies:
npm installStart the app:
npm run devOpen:
http://localhost:38887
The first generated data file uses local bootstrap credentials:
Username: self-hosted-user
Password: todo123456
For any shared or internet-accessible deployment, set a real password before the first run:
TODO_PASSWORD="replace-with-a-strong-password" npm startYou can also change the password later from Settings -> Account and Security.
The backend serves the built frontend from public/dist. For normal local usage, npm run dev is enough.
When working on the React frontend with Vite, run the backend and Vite in separate terminals:
npm run devnpm run dev:clientThe Vite dev server proxies /api and /prototype to the Node server on port 38887.
Build the frontend assets:
npm run build:clientEnvironment variables:
| Variable | Default | Purpose |
|---|---|---|
PORT |
38887 |
HTTP server port |
TODO_USERNAME |
self-hosted-user |
Username written into the first generated data file |
TODO_PASSWORD |
todo123456 |
Password written into the first generated data file |
TODO_SESSION_SECRET |
derived from the password hash | Optional extra session signing secret |
TODO_DATA_DIR |
./data |
Runtime JSON data and upload directory |
TODO_LOG_DIR |
.deploy/shared/logs |
PM2 log directory |
TODO_COOKIE_SECURE |
unset | Set to 1 when serving over HTTPS |
TODO_MCP_TOKEN |
unset | Bearer token required by the optional MCP HTTP service |
TODO_MCP_HOST |
127.0.0.1 |
MCP HTTP bind host |
TODO_MCP_PORT |
38888 |
MCP HTTP port |
TODO_MCP_ALLOWED_ORIGINS |
unset | Comma-separated browser Origin allowlist for MCP requests |
TODO_MCP_LOG |
auto | Set 1 to force MCP request/tool logs, 0 to disable |
Important behavior:
TODO_USERNAMEandTODO_PASSWORDare only used when a data file is created for the first time.- Existing credentials live in the runtime data file as a salted password hash.
- If you need an isolated development dataset, set
TODO_DATA_DIRto another directory.
Example:
TODO_DATA_DIR=./data-dev npm run devThe app can run a separate MCP Streamable HTTP service for other AI agents. It uses the same TODO_DATA_DIR as the web app and exposes a narrow task-focused tool surface:
todo_list_taskstodo_get_tasktodo_create_tasktodo_update_tasktodo_complete_tasktodo_close_task
Start it locally:
printf 'TODO_MCP_TOKEN=%s\n' 'replace-with-a-long-random-token' >> .envnpm run mcp:devEndpoint:
POST http://127.0.0.1:38888/mcp
Authorization: Bearer <TODO_MCP_TOKEN>
Health check:
GET http://127.0.0.1:38888/health
The default host is 127.0.0.1 so the MCP service is local-only. If you expose it to another machine, bind TODO_MCP_HOST deliberately, put it behind HTTPS, set a strong TODO_MCP_TOKEN, and configure TODO_MCP_ALLOWED_ORIGINS for browser-based clients. The MCP service does not expose account settings, password changes, import/export, attachment downloads, project/tag management, or hard-delete tools.
npm run mcp:dev enables MCP request and tool-call logs by default. Set TODO_MCP_LOG=0 in .env to silence them, or TODO_MCP_LOG=1 to force the same logs in another startup mode.
Full agent integration guide: docs/mcp-http-service.md.
By default, the app stores runtime data under data/:
data/todo-data.json tasks, projects, tags, filters, settings, auth hash, attachment metadata
data/uploads/ uploaded files
data/tmp/ temporary import/export backups
These files are private user data and should not be committed. The repository .gitignore already excludes the default runtime data paths.
Create a packaged runtime snapshot:
npm run release:buildThis command builds the frontend and creates:
.deploy/releases/<release-name>/ immutable runtime snapshot
.deploy/current symlink pointing at the active snapshot
.deploy/shared/logs/ PM2 stdout/stderr logs
The release snapshot contains code and static assets. It continues to use the shared data directory, which defaults to ./data unless TODO_DATA_DIR is set.
First deployment:
npm run release:build
npm run pm2:start
npm run pm2:logsStart the MCP service under PM2:
npm run pm2:mcp:start
npm run pm2:mcp:logsUpdate an existing deployment:
npm run release:build
npm run pm2:restartRestart an existing MCP deployment after changing MCP environment variables:
npm run pm2:mcp:restartStop the process:
npm run pm2:stopThe included PM2 app name is personal-todo.
Run the test suite:
npm testUseful local checks:
pgrep -af personal-todo
lsof -i :38887Before pushing this repository to GitHub:
- Do not commit
data/; it may contain personal tasks, imported task history, settings, password hashes, and attachment metadata. - Do not commit
data/uploads/; it may contain private uploaded files. - Do not commit
.deploy/; it contains release snapshots, shared runtime data, and PM2 logs. - Do not commit
output/or.playwright-cli/; they contain local screenshots, browser snapshots, console logs, and smoke-test scripts. - Do not commit
node_modules/. - Review
.od-skills/if you want a cleaner public repository; it contains local prototype/agent helper templates rather than app runtime code. - Replace the bootstrap password for any real deployment with
TODO_PASSWORDbefore the first start, or change it immediately in Settings.
The default username and password in source are development bootstrap values, not production credentials.