A minimalist, single-user task manager where a single Markdown file is your database.
No cloud. No accounts. No vendor lock-in. Your tasks live in a plain backlog.md file you can read, edit, and version-control with any tool you already use.
| Concern | Todoist / TickTick / Notion | This App |
|---|---|---|
| Where's my data? | Their servers | A single .md file on your disk |
| Online required? | Yes (cloud sync) | Never — even the server is localhost-only |
| Vendor lock-in? | You need their app to read your data | It's a .md file — read it in VS Code, Vim, cat |
Can I git commit my tasks? |
No | Yes — it's a text file |
| Can I sync across devices? | Built-in cloud | Any file sync — Dropbox, rsync, git, USB, anything |
| Price | $5–6/mo | Free, forever |
If you've ever wanted your task list to be just a file — this is it.
Works with Chrome, Firefox, Safari — any browser. The server is a single Python file with zero dependencies.
cd personal-backlog
python3 server/server.py --port 8080Then open http://localhost:8080 in your browser.
The server creates backlog.md, backups/, and stats.jsonl in its own directory by default. To store data elsewhere:
python3 server/server.py --port 8080 --dir ~/my-backlogNo Python needed. Just open the HTML file:
open webapp/index-style-v2.htmlOr serve it with any static file server:
cd webapp && python3 -m http.server 3000
# Then open http://localhost:3000/index-style-v2.htmlOn first launch, the browser will prompt you to select a folder. This is required because browsers can't access your filesystem without explicit permission. Create a folder (or pick an existing one) and the app will create backlog.md, backups/, and stats.jsonl inside it.
Firefox and Safari don't support the File System Access API, so the app can't read/write files on your disk directly. Instead it automatically falls back to IndexedDB (browser-local storage) — fully read-write, with backups and history preserved across reloads. Use the Import/Export dialog to move data between IndexedDB and a portable .md file at any time.
All your tasks live in backlog.md, structured as:
# Backlog
<!-- SECTION: ENTRIES -->
- [ ] [P0] Ship landing page *(due: 2025-06-01, priority: P0, progress: 50)*
- [x] Design mockups *(priority: P0, progress: 100)*
- [/] Implement frontend *(priority: P1, progress: 30)*
- [!] [P1] API integration *(priority: P1, reason: waiting for keys)*
- [>] [P2] Blog post *(priority: P2, due: 2025-07-15)*
<!-- SECTION: HISTORY -->
| Timestamp | Item ID | Action | Details |
|-----------|---------|--------|---------|
| 2025-05-10T14:32:00Z | i-m1 | status_changed | open → done |
<!-- SECTION: INTEGRITY -->
<!-- saved: 2025-05-10T14:35:12Z | checksum: sha256:abc123... | entries: 3 | history: 1 -->You can edit this file in any text editor. The app detects external changes and reloads automatically.
- 4-level nesting — Area → Project → Task → Sub-task
- 6 statuses — open, in-progress (
/), blocked (!), postponed (>), done (x), cancelled (-) - Priorities — P0 (burning) through P3, with drag-and-drop reordering within each priority
- Progress tracking — 0–100% per task; done auto-sets to 100%
- Due dates — with overdue highlighting
- Tags — free-form labels with autocomplete
- Quick search — instant text search with hierarchical parent visibility
- Integrity checks — SHA-256 checksum on every save; warning-only on mismatch
- Automatic backups — timestamped on every save, rotating retention
- Stats & metrics — items created/completed, avg time in-progress, most active project — all from real history data
- Import/Export — Markdown or JSON, with checksum validation
- Admin page — health monitoring, backup browser, stats overview, manual actions
- Dark mode — system / light / dark, configurable in Admin → Appearance; persisted across reloads
- Icon sets — 4 styles (Color, Flat, Emoji, ASCII), configurable in Admin → Appearance; persisted across reloads
- Chrome / Edge direct file access — reads and writes
backlog.mdon disk directly via the File System Access API; no server needed - Firefox / Safari support — full read-write via IndexedDB when File System Access API is unavailable; export to
.md/.jsonanytime to get a portable file
The app detects which mode to use automatically:
| API Server | Direct File Access | IndexedDB fallback | |
|---|---|---|---|
| How to start | Run python3 server/server.py |
Open webapp/index-style-v2.html in Chrome/Edge |
Open in Firefox / Safari |
| Works in | Any browser | Chrome, Edge only | Firefox, Safari |
| Storage via | HTTP REST API | File System Access API | IndexedDB (browser-local) |
| Folder picker | Not needed | Required on first launch | Not needed |
| Saves to disk | Yes — plain .md file |
Yes — plain .md file |
No — browser storage only |
| LAN access | Yes (phone, tablet) | No (local browser only) | No |
| Admin shows paths | Yes (full path) | Folder name only | — |
When you open the HTML file directly, the browser has no access to your filesystem. The File System Access API (showDirectoryPicker()) is the only way to read and write local files from a web page. You grant permission once; the handle is stored in IndexedDB so it persists across reloads.
To reset the folder (pick a different one or clear saved permissions):
- Open DevTools → Application → IndexedDB → delete the
pb-storage-v2database - Reload the page — you'll be prompted to pick a folder again
Both versions read and write the exact same backlog.md format. The Parser and Serializer are identical in logic. This means you can:
- Start with the Python server — add tasks, create structure
- Shut down the server — open the same
backlog.mdlocation via the standalone HTML - Switch freely — edits in one mode are visible in the other
From API server to standalone:
- Stop the server (
Ctrl+C) - Open
webapp/index-style-v2.htmlin Chrome/Edge - When prompted, select the folder that contains your
backlog.md(e.g. theserver/directory or wherever--dirpointed)
From standalone to API server:
- Note which folder your
backlog.mdlives in - Start the server pointing to that folder:
python3 server/server.py --dir /path/to/your/folder
- Open
http://localhost:8080
- Don't run both modes simultaneously against the same
backlog.md— the last writer wins and you may lose edits. - External edits (Vim, VS Code, etc.) are detected automatically via checksum polling every 5 seconds. If you have unsaved changes in the app, you'll get a conflict resolution dialog.
- The checksum is a save indicator, not a gate. If it mismatches (e.g. you edited the file by hand), the app still loads it — just with a yellow warning banner. The next save recalculates a correct checksum.
personal-backlog/
├── README.md
├── doc/
│ ├── requirements/requirements.md # Functional & non-functional requirements
│ └── architecture/
│ ├── architecture.md # High-level architecture
│ └── tdd.md # Technical Design Document
├── server/
│ ├── server.py # Python REST API server (~420 LoC, stdlib only)
│ ├── backlog.md # Master data file (created on first run)
│ ├── backups/ # Automatic timestamped backups
│ └── stats.jsonl # Append-only analytics log
├── web/ # V2 source code (React 18 + JSX) + build tooling
│ ├── index.html # Dev entry point (React + Babel from CDN)
│ ├── styles.css # All CSS
│ ├── storage.jsx # Parser, ApiBackend, DirectBackend, SyncPoller
│ ├── helpers.jsx # Utility functions, event handling
│ ├── app.jsx # Root App component, state, save/load lifecycle
│ ├── tree.jsx # Task tree rendering
│ ├── dialogs.jsx # Modal dialogs
│ ├── admin.jsx # Admin dashboard
│ ├── filter-panel.jsx # Filter sidebar
│ ├── tweaks-panel.jsx # Settings panel
│ ├── data.jsx # Seed data (test-only, not loaded by default)
│ ├── bundle.js # Build tool: multi-file JSX → single HTML
│ ├── package.json # @babel/core + @babel/preset-react
│ └── node_modules/
├── webapp/ # Built output — ready to open or deploy
│ └── index-style-v2.html # Self-contained single-file SPA (~361 KB)
cd web
python3 -m http.server 9000
# Open http://localhost:9000 — React + Babel load from CDN, no build step neededcd web
npm install # once, to install @babel/core + @babel/preset-react
node bundle.js index.html ../webapp/index-style-v2.htmlThe bundler pre-compiles all JSX, swaps React dev CDN builds for production minified builds, removes Babel entirely, and writes a single self-contained HTML file to webapp/.
See doc/architecture/tdd.md for full technical details.
- Runtime: Python 3.8+ (server), any modern browser
- Development: Node.js 18+ (for the bundler only)
- Bundler deps:
cd web && npm install
Personal use. Do whatever you want with it.

