Skip to content

avinal/memodav

Repository files navigation

MemoDAV

Bidirectional task sync between Memos and CalDAV.

Write tasks as Markdown checkboxes in Memos, and they appear as VTODO items in any CalDAV client — Tasks.org, Thunderbird, GNOME To Do, or Apple Reminders. Check them off in your task app, and the checkbox updates in Memos. MemoDAV sits in the middle, reconciling both directions.

Why MemoDAV? — the problem, the gap, and the design principles.

How It Works

┌──────────┐  webhook   ┌──────────┐  CalDAV PUT  ┌──────────┐  sync  ┌──────────┐
│          │ ────────── │          │ ──────────── │          │ ────── │          │
│  Memos   │            │ MemoDAV  │              │ Radicale │        │ Tasks.org│
│          │ ────────── │          │ ──────────── │          │ ────── │          │
└──────────┘  API PATCH └──────────┘  hook POST   └──────────┘  sync  └──────────┘
                         Memos→CalDAV               CalDAV→Memos

MemoDAV uses two event-driven triggers for near-instant sync:

  1. Memos webhook — Memos fires a POST when a memo changes. MemoDAV parses the tasks and PUTs them to CalDAV.
  2. Radicale storage hook — Radicale runs a shell script after every CalDAV write. The script POSTs to MemoDAV, which reads the CalDAV state and PATCHes Memos.

A periodic reconciliation loop (configurable, default 5 min) acts as a safety net for missed events.

Task Format

Tasks are Markdown checkboxes with optional inline metadata, inspired by Todoist Quick Add:

- [ ] Buy groceries 2024-03-15 p1 @shopping #errands
- [x] Renew domain
- [ ] Fix laptop p2 @work
Token Format CalDAV mapping Example
Due date YYYY-MM-DD DUE property 2024-03-15
Priority p1, p2, p3 PRIORITY (1, 5, 9) p1 = high
Labels @word CATEGORIES @work @urgent
List #word Target calendar #personal

A standalone #tag line at the top of a memo sets the default calendar for all tasks. Per-task #tags override it.

See docs/SYNTAX.md for the full mapping reference — priority mapping, date formats, escaping, calendar routing, and edge cases.

Quick Start

Docker Compose

git clone https://github.com/avinal/memodav.git
cd memodav
cp config.example.yaml config.yaml
# Edit config.yaml with your Memos URL, API key, and CalDAV credentials
docker compose up -d

From source

go build -o memodav ./cmd/memodav
./memodav -config config.yaml

Use -debug for verbose logging without editing config.

Requires: Go 1.21+ (uses log/slog). No CGO needed.

Configuration

See config.example.yaml for all options. Key settings:

Setting Description Default
log_level info or debug info
webhook.listen HTTP listen address :8887
caldav.url Radicale base URL
caldav.default_list Fallback calendar name tasks
memos.url Memos server URL
memos.apikey Memos API access token
sync.reconciliation_interval Polling interval (0 to disable) 5m

Architecture

Single Go binary. No CGO. Two runtime dependencies: a Memos instance and a CalDAV server (Radicale).

cmd/memodav/           Entry point, HTTP server, startup sequence
internal/
  config/              YAML config loading
  memos/               Memos REST API client
  caldav/              CalDAV HTTP client (raw net/http, no library)
  parser/              Markdown ↔ Task conversion
  sync/                Bidirectional sync coordinator
  db/                  SQLite persistence (task mappings, sync state)
  webhook/             Memos webhook HTTP handler
scripts/               Radicale storage hook script
docs/                  Extended documentation

CalDAV uses raw HTTP instead of a library — we only need PUT, DELETE, REPORT, and MKCALENDAR, and Radicale has XML namespace quirks that trip up generic parsers.

Each task gets a deterministic UID: sha256(memoID + "|" + lineIndex). Reordering lines generates new UIDs — a known limitation with title-hash recovery planned.

Deployment

See docs/DEPLOYMENT.md for complete setup instructions:

  • Docker Compose — MemoDAV + Radicale in one command
  • Standalone binary — build and run directly, with a systemd unit file
  • Nginx reverse proxy — host both services on a single domain (e.g. sync.example.com/memodav/ and sync.example.com/caldav/)
  • Radicale configuration — storage hooks, authentication

An example nginx config is provided at docs/nginx.conf.

Endpoints

Method Path Description
POST /webhook Receives Memos webhook payloads
POST /sync/caldav-changed Receives Radicale hook notifications
GET /health Liveness probe

Contributing

See CONTRIBUTING.md for development setup, code style, and areas where help is welcome.

AI Notice

The back sync and documentation is written heavily using AI, please report if any discrepencies are found.

License

MIT — Avinal Kumar

About

A Memos to CalDAV sync

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors