dodo

module
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 12, 2026 License: MIT

README

dodo

dodo

A self-hosted todo & reminders service. MIT licensed.

Tasks support one-off reminders at a specific date/time and recurring tasks (daily/weekly/monthly/yearly). Notifications repeat at the priority's interval until completed (low=2h, normal=1h, high=20m) and are delivered through each user's own Telegram bot (with a "Complete" button straight from the chat) and as desktop browser notifications while the web app is open. Tasks can be snoozed to silence reminders until later. The service is designed to run inside a private Tailscale tailnet and uses long polling (no webhooks).

All users are equal — there are no roles. User and token management is done with the dodo admin CLI (direct DB access); everything else is per-user and scoped to the authenticated user.

Binary Purpose
dodo HTTP API + web UI + scheduler + telegram pollers; admin CLI. Ships in the container.
dodo-cli AI-agent CLI client (JSON stdout, --pretty for humans).
dodo-tui Terminal UI client (API + token auth).

Quickstart (docker)

docker run -d --name dodo \
  -p 8080:8080 \
  -v dodo-data:/data \
  -e DODO_ENCRYPTION_KEY=$(openssl rand -base64 32) \
  ghcr.io/mtzanidakis/dodo

# create the first user (inside the container; prompts for the password)
docker exec -it dodo dodo admin user create --email you@example.com
TOK=$(docker exec -it dodo dodo admin token create --email you@example.com --name agent | jq -r .token)

dodo-cli init --url http://localhost:8080 --token "$TOK"
dodo-cli tasks create --title "Pay electric bill" --due 2026-07-11T17:00:00Z --priority high
dodo-cli tasks list

Quickstart (local, from source)

mise install
mise run build-all
export DODO_ENCRYPTION_KEY=$(openssl rand -base64 32)
export DODO_DATABASE_PATH=/tmp/dodo.sqlite
./dodo admin user create --email admin@example.com   # prompts for the password
./dodo serve &
TOK=$(./dodo admin token create --email admin@example.com --name agent | jq -r .token)
./dodo-cli --url http://localhost:8080 --token "$TOK" tasks create --title "Pay bill" --due 2026-07-11T17:00:00Z --priority high
./dodo-cli --url http://localhost:8080 --token "$TOK" tasks list

Open http://localhost:8080/login in a browser and sign in with those credentials to use the web UI: a list view (with today/this-week/ this-month and pending/completed/all filters), a month calendar, per-user profile, API token management, Telegram setup, dark/light/ system theme, and English/Greek locales.

Deploy with Docker Compose

The repo ships a compose.yaml. Configure the encryption key and start it:

cp .env.example .env && chmod 600 .env
# edit .env: set DODO_ENCRYPTION_KEY (openssl rand -base64 32)
docker compose up -d

# first user + a CLI token (user create prompts for the password)
docker compose exec dodo dodo admin user create --email you@example.com
docker compose exec dodo dodo admin token create --email you@example.com --name agent

The web UI is at http://localhost:8080.

Production (Tailscale, no exposed ports)

compose.override.yaml-prod fronts dodo with tsrp, a Tailscale reverse proxy, and removes the published port so the service is reachable only on your tailnet. Activate the override and set the Tailscale vars:

ln -s compose.override.yaml-prod compose.override.yaml   # compose auto-merges compose.override.yaml
# in .env: HOSTNAME=dodo  and  TS_AUTHKEY=tskey-auth-...
docker compose up -d

dodo is then served at https://dodo.<your-tailnet>.ts.net. The override also bind-mounts ./dumps and adds hourly dodo backup labels for ofelia — run an ofelia scheduler alongside the stack to execute them.

Server env vars

See AGENTS.md for the full table. Highlights:

Env Default Notes
DODO_DATABASE_PATH /data/dodo.sqlite SQLite path (serve + admin).
DODO_LISTEN :8080 HTTP bind.
DODO_ENCRYPTION_KEY (required) 32-byte base64; AES-256-GCM for telegram bot tokens.
DODO_SCHEDULER_INTERVAL 1m Reminder scan cadence (min 1m).

The CLI/TUI clients ignore env vars and read ~/.config/dodo/config.json (overridable with --url/--token/--config).

Client config (dodo-cli / dodo-tui)

{
  "url": "http://localhost:8080",
  "token": "dodo_xxxxxxxxxxxx",
  "log_level": "info",
  "timezone": "Europe/Athens"
}

dodo-cli init --url <api> --token <token> [--timezone <IANA>] writes the config for first-time setup.

Both clients render task times (and the CLI parses --due/--until input) in your timezone, matching the web UI. The zone is resolved in this order: the optional timezone config value (an IANA name like Europe/Athens, or UTC), then your profile timezone from the server, then the host's local zone. The CLI keeps its JSON output valid RFC3339 — same instant, just a local offset (e.g. 2026-07-11T20:00:00+03:00) instead of a Z suffix.

Telegram setup

  1. Create a bot with @BotFather and copy its token.
  2. In the web UI Account page (or POST /api/v1/me/telegram), save the bot token and your Telegram user id (comma-separated list of allowed ids).
  3. Send /start to your bot from Telegram; the chat gets linked.
  4. Reminders now arrive via your own bot with a Complete button.

Bot tokens are encrypted at rest with AES-256-GCM (DODO_ENCRYPTION_KEY). The server receives updates with long polling (no webhooks needed).

Browser notifications

On the web Account page, click Enable notifications to also get a desktop notification when a task is due. This uses the Web Notifications API, so it fires while a dodo tab is open and requires a secure context (localhost or https — not plain http to a LAN/tailnet IP).

Backup

The /data volume is the only state. Take a consistent online backup with the built-in command (safe while the server is running — it uses VACUUM INTO):

dodo backup -dump /data/backup-$(date +%F).sqlite

Inside the container:

docker exec dodo dodo backup -dump /data/backup-$(date +%F).sqlite

-dump refuses to overwrite an existing file, so use a fresh (e.g. dated) path. Equivalent with the sqlite3 CLI, if you have it:

sqlite3 /data/dodo.sqlite ".backup /data/backup.sqlite"

Upgrading the clients

dodo-cli and dodo-tui can update themselves in place from the latest GitHub release:

dodo-cli version    # print the installed version
dodo-cli upgrade    # download & replace with the latest release, if newer
dodo-tui upgrade

upgrade compares the installed version with the latest release, and when a newer one exists it downloads the matching archive for your OS/arch, verifies its checksum, and atomically replaces the running binary (so the install directory must be writable). Released binaries carry their version; a locally-built binary reports its git describe string and only upgrades to a strictly newer release.

Development

mise run lint          # golangci-lint
mise run test         # go test -race -cover ./...
mise run build-all    # all three binaries
mise run web:build    # frontend assets -> internal/web/dist

Commits follow Conventional Commits, enforced by CI (feat fix refactor test chore docs ci build perf style).

License

MIT - see LICENSE.

Directories

Path Synopsis
cmd
dodo command
dodo-cli command
dodo-tui command
internal
api
backup
Package backup implements the `dodo backup` command: an online snapshot of the SQLite database, equivalent to `sqlite3 <db> ".backup <dest>"`.
Package backup implements the `dodo backup` command: an online snapshot of the SQLite database, equivalent to `sqlite3 <db> ".backup <dest>"`.
cli
db
selfupdate
Package selfupdate lets the dodo-cli and dodo-tui binaries upgrade themselves in place from the project's GitHub releases: it compares the installed version against the latest release, and when a newer one exists it downloads the matching archive, verifies its checksum, and atomically replaces the running executable.
Package selfupdate lets the dodo-cli and dodo-tui binaries upgrade themselves in place from the project's GitHub releases: it compares the installed version against the latest release, and when a newer one exists it downloads the matching archive, verifies its checksum, and atomically replaces the running executable.
tui
web
ws

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL