Tasks can now have a start/end time on their deadline date. The new Calendar view shows a day as a timeline (with a week strip and projected repeats of recurring tasks), and the task form lists what is already planned that day and flags overlaps. Existing databases are migrated to schema v2 automatically, with a backup copy taken first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WhatToDo
A small, self-hosted to-do app for one person. It works in a desktop browser and on a phone, and you can install it to your home screen.
- Tasks have an optional deadline with an optional time range (e.g. 10:00–12:00), priority (none / low / medium / high), category, tags, notes, and a checklist of steps.
- Recurring tasks (every N days/weeks/months/years): completing one creates the next occurrence.
- When you pick a date and time, the task form lists what else is planned that day and warns about overlaps.
- Views: Today (overdue, due today, high priority), Upcoming, Calendar (day timeline with week strip; tap an hour to add a task there), All open, Done, plus one view per category.
- You can rename, recolor, reorder, add, or delete categories in Settings. It starts with Work, Life, Personal, YouTube.
- Export / import of all data as JSON, from Settings.
- Login with a single password.
Stack: Python (FastAPI) + SQLite with a plain HTML/CSS/JS frontend and no build step. Everything runs in Docker.
Run locally
Requirements: Docker with the Compose plugin. Nothing else is installed on the host.
cp .env.example .env # then edit .env: set APP_PASSWORD and SECRET_KEY
docker compose up -d --build
Open http://localhost:8080 and log in with APP_PASSWORD.
The database lives in ./data/whattodo.db, which is git-ignored.
Useful commands:
docker compose logs -f # follow logs
docker compose down # stop
docker compose up -d --build # rebuild after pulling changes
docker compose run --rm test # run the test suite (in Docker)
Testing on your phone (same Wi-Fi)
Set BIND_ADDR=0.0.0.0 in .env, run docker compose up -d, then open http://<this-computer's-LAN-IP>:8080 on the phone.
Browsers only allow "Install app" / "Add to Home Screen" with offline support over HTTPS (or on localhost). Over plain LAN HTTP the app still works, but it won't install as a full app.
Deploy to production
-
On the server, clone the repo and create
.envfrom.env.example. Use a new strongAPP_PASSWORDandSECRET_KEY, and setCOOKIE_SECURE=true. -
docker compose up -d --build. The app listens on127.0.0.1:${PORT}. -
Put it behind a reverse proxy with HTTPS. For example, with Caddy:
todo.example.com { reverse_proxy 127.0.0.1:8080 }If the proxy runs in another container instead of on the host, set
BIND_ADDR/networking to match and setFORWARDED_ALLOW_IPSto the proxy's IP. The app then sees the real client IP, which login rate limiting uses.
Moving your data from this PC to production
- Local app → Settings → Export data. This downloads
whattodo-export-YYYYMMDD-HHMM.json. - Production app → Settings → Import, pick the file:
- Replace deletes everything on that install and loads the file. Use this for the first move to prod.
- Merge adds the file's tasks to what is already there. Categories with the same name are reused.
The export file contains your tasks. Keep it out of the repo (whattodo-export-*.json is git-ignored).
Backups
Export regularly from Settings, or copy ./data/whattodo.db while the app is stopped.
Security notes
.envanddata/are git-ignored. Never commit them; the repository is public.- Changing
APP_PASSWORDorSECRET_KEYlogs out all sessions. - Failed logins are rate limited: 10 failures per 15 minutes per IP.
- Write requests require an
X-WhatToDoheader (CSRF protection). The app also sends a strict Content-Security-Policy.
Project layout
app/main.py API: auth, tasks, categories, export/import
app/db.py SQLite schema and setup
app/static/ frontend (index.html, app.js, style.css, PWA files, icons)
tests/ API tests (run with: docker compose run --rm test)
tools/make_icons.py regenerates the PNG icons