Local-first developer task / note / link tracker. Single SQLite file, FastAPI backend, vanilla-JS web UI, a native SwiftUI macOS app, and an MCP server so an LLM can drive everything.
- Tasks · notes · links scoped to projects, with cross-refs (
#42,[[Title]]), tags, full-text search, and a rich markdown editor. - Time tracking with single-doing invariant, editable sessions, end-of-workday auto-pause.
- Drawings via a vendored drawio webapp — fully offline; tokens like
![[drawing:N]]render inline. - Markdown via markdown-it with footnotes, task lists, anchors, a custom MkDocs-style admonition rule, highlight.js, and Mermaid.
- MCP server (
devlog-mcp) exposing 18 tools so Claude can create projects/tasks/notes/links/sessions and search.
GitHub Actions publishes a multi-arch image (linux/amd64 + linux/arm64) to ghcr.io/morapet/devlog on every push to main and on v*.*.* tags. drawio is baked in.
# one-shot run with a host volume for data
mkdir -p ~/devlog-data
docker run -d --name devlog \
-p 8765:8765 \
-v ~/devlog-data:/data \
--restart unless-stopped \
ghcr.io/morapet/devlog:latest
open http://localhost:8765Update later:
docker pull ghcr.io/morapet/devlog:latest
docker rm -f devlog
# …then re-run the docker run command aboveOr use docker compose with the published image (drop into a new dir as docker-compose.yml):
services:
devlog:
image: ghcr.io/morapet/devlog:latest
container_name: devlog
ports: ["8765:8765"]
volumes: ["./data:/data"]
restart: unless-stoppeddocker compose up -dAvailable tags:
| Tag | Source |
|---|---|
latest |
most recent push to main |
main |
most recent push to main |
v1.2.3, 1.2, 1 |
semver from a v*.*.* git tag |
sha-abc1234 |
a specific commit |
Note: GHCR packages start as private. After the first publish, go to https://github.com/users/morapet/packages/container/devlog → Package settings → "Change visibility" → Public, so others can
docker pullwithout auth.
git clone https://github.com/morapet/devlog.git
cd devlog
make docker-up # builds the image (includes drawio) and starts the container
open http://localhost:8765Data persists in ./data/ (a SQLite WAL file). Stop with make docker-down.
Option C — Local Python (with uv)
git clone https://github.com/morapet/devlog.git
cd devlog
make install # uv sync
make drawio # download + install the drawio webapp (~120 MB, one-time)
make dev # uv run devlog
open http://127.0.0.1:8765Data goes to ~/.local/share/devlog/devlog.db (or $XDG_DATA_HOME/devlog/ if set).
uv tool install git+https://github.com/morapet/devlog.git
devlog # starts the backend
bash $(uv tool dir)/devlog/scripts/install-drawio.sh # if you want drawings# From a repo checkout
make install-linux # backend service + desktop app, all-in-one
# Or piecewise
bash clients/linux-server/install.sh # backend via pipx/uv + systemd --user
bash clients/linux-app/install.sh # GTK desktop app + autostart
# Or from anywhere, no checkout
curl -sLf https://raw.githubusercontent.com/morapet/devlog/main/clients/linux-server/install.sh \
| bash -s -- --from-github --lingerThe server script:
- Installs the
devlogpackage (viauv tool→pipx→pip --user, whichever exists; bootstrapspipxvia apt if none). - Downloads the drawio webapp into the installed package (skip with
--no-drawio). - Writes
~/.config/systemd/user/devlog.serviceand runssystemctl --user enable --now devlog. - With
--linger, runssudo loginctl enable-linger $USERso the backend keeps running after logout.
See clients/linux-server/README.md for status / upgrade / uninstall.
The web UI is a mobile-friendly PWA — run the backend on any machine your phone can reach and it behaves like a native app. There is no iOS build to install; Safari is the client.
- Make the server reachable from the network:
- Docker already binds
0.0.0.0— nothing to do. - Local Python:
DEVLOG_HOST=0.0.0.0 make dev(the default bind is127.0.0.1, which the phone can't reach).
- Docker already binds
- Find the machine's LAN IP:
ipconfig getifaddr en0(macOS) orhostname -I(Linux). - On the iPhone, open
http://<that-ip>:8765in Safari. - Add to Home Screen: tap Share → Add to Home Screen. Devlog launches full-screen with its own icon, indistinguishable from a native app.
Install Tailscale on the server machine and the iPhone (App Store, free for personal use). The phone can then reach the server from anywhere:
http://<machine-name>:8765 # via MagicDNS
For HTTPS (nicer, and required for the offline service worker — plain http:// on a LAN IP is not a secure context):
tailscale serve --bg 8765
# → https://<machine-name>.<tailnet>.ts.netThe backend is small enough to run on the phone itself inside iSH (free, App Store); Safari talks to it over localhost. See clients/ios/README.md — install is four commands, nothing is compiled on-device.
Devlog ships built-in auth for remote access: your own machine (loopback) is trusted, but requests from other devices need a shared secret. The secret comes from DEVLOG_AUTH_TOKEN, or is auto-generated into <data_dir>/auth.token — print it with devlog --print-token. Set DEVLOG_AUTH=always to require the secret even on loopback, or DEVLOG_AUTH=off to disable auth entirely. The web UI prompts for the token on first remote access and keeps a 30-day session cookie; API clients send Authorization: Bearer <token>. With HTTPS in front, hosting publicly is reasonable. Two ready-made setups, both serving the PWA from anywhere with full offline-shell support:
- deploy/cloudflare/ — Cloudflare Tunnel, free, zero open ports: runs on any always-on machine at home; optional Cloudflare Access (Google login / email PIN) at the edge on top of the built-in shared secret.
- deploy/vps-caddy/ — VPS + Caddy: ~€4/mo box, automatic Let's Encrypt certificates, built-in shared secret for auth.
- deploy/pythonanywhere/ — PythonAnywhere: free tier works (HTTPS at
you.pythonanywhere.com), no server admin at all; deploys via a small WSGI bridge. - deploy/cloud-run/ — Google Cloud Run: free
*.run.appHTTPS URL, scale-to-zero; SQLite persists via Litestream streaming to a GCS bucket (single instance only).
Never expose port 8765 directly with
DEVLOG_AUTH=off— an open devlog is writable by anyone. Keep the shared secret (required for remote access by default) and TLS in front (the cookie and token travel in requests).
make mac
# or:
cd clients/mac-app && ./build.sh && open .build/Devlog.appRequires Swift / CommandLineTools. A native window hosts the full web UI in a WKWebView (with native save/open panels and Cmd+F find), and can manage its own backend. make mac-dmg builds a drag-to-Applications installer; make mac-install copies it into /Applications.
make app-linux
# or:
bash clients/linux-app/install.shA GTK desktop window wrapping the same web UI. The installer apt-installs the PyGObject / WebKit2GTK dependencies, drops a launcher, and enables autostart. See clients/linux-app/README.md.
Exposes the HTTP API as 18 MCP tools for use from Claude Desktop / Claude Code.
Add to your client config (e.g. ~/.claude.json or claude_desktop_config.json):
{
"mcpServers": {
"devlog": {
"command": "uv",
"args": ["run", "--directory", "/path/to/devlog", "devlog-mcp"]
}
}
}Override the backend URL with DEVLOG_BASE_URL. See src/devlog/mcp_server.py for the full tool list.
make help # list every target
make install # install python deps via uv
make drawio # download drawio webapp
make dev # run the backend
make mac # build and launch the macOS app
make mcp # run devlog-mcp (stdio)
make docker-build # docker compose build
make docker-up # docker compose up -d
make docker-down # docker compose down
make docker-logs # follow container logs
make clean # remove build artifacts (keeps data + db)
| Env var | Default | Meaning |
|---|---|---|
DEVLOG_HOST |
127.0.0.1 |
Bind address (0.0.0.0 in Docker) |
DEVLOG_PORT |
8765 |
Bind port |
DEVLOG_DATA_DIR |
$XDG_DATA_HOME/devlog or ~/.local/share/devlog |
Where the SQLite file lives |
DEVLOG_BASE_URL |
http://127.0.0.1:8765 |
Used by devlog-mcp to reach the backend |
DEVLOG_AUTH |
auto |
auto trusts loopback and requires the token remotely; always requires it everywhere; off disables auth |
DEVLOG_AUTH_TOKEN |
(auto-generated to auth.token) |
Shared secret for remote access; print it with devlog --print-token. Read by devlog-mcp too |
.
├── src/devlog/ # FastAPI app + web assets
│ ├── api/ # routers: projects, items, sessions, attachments, search, stats, settings
│ ├── web/ # index.html, app.js, style.css, vendor/drawio/ (ignored)
│ ├── db.py # schema + thread-local connections + migrations
│ ├── autostop.py # background loop pausing 'doing' tasks at end of workday
│ ├── stats.py # raw per-day session time math
│ └── mcp_server.py # FastMCP wrapper exposing 18 tools
├── clients/mac-app/ # SwiftUI native app (Swift Package Manager)
├── scripts/ # helpers (install-drawio.sh)
├── Dockerfile # python:3.13-slim base, uv, optional drawio install
├── docker-compose.yml # `make docker-up`
├── Makefile # convenience targets
└── pyproject.toml # uv-managed; entry points: devlog, devlog-mcp
- Backend: FastAPI · SQLite (WAL + FTS5) · httpx · selectolax
- Web UI: vanilla JS · Tailwind via CDN · markdown-it + custom plugins · highlight.js · Mermaid · drawio (vendored)
- macOS app: SwiftUI · WKWebView · async/await URLSession client
- MCP:
mcpPython SDK (FastMCP, stdio transport)
Three layers, each at a different scope:
- ARCHITECTURE.md — big picture: system diagram, process model, rendering pipeline, what each client does, what's new recently.
- AGENTS.md — operating guide for LLMs: how to connect via MCP, common workflows mapped to tool calls, the drawio recipe with a Python helper.
- SPECIFICATION.md — exhaustive contract suitable for full reimplementation. Domain model, every endpoint, every invariant.