| Today | Quick add |
|---|---|
![]() |
![]() |
Works with any spec-compliant CalDAV server. Lists, due dates, priorities
and notes are stored as ordinary VTODOs that other clients can read.
On top of that:
- Quick add.
Clean the gutters tomorrow at 3pm #chores p1becomes a todo with a due date, a time, a list and a priority. One line, one Enter. - Views your server doesn't have. Today, Tomorrow, Next 7 days, Summary and Search, gathered across every list.
- Recognised lists. Name one
Healthand its todos lead every view in a block of their own. Health is the one thing that shouldn't wait behind a chore.Groceriescollapses into a single row instead, because "did the shopping" is the useful fact, not twelve lines of shopping. No setting to find; the name is the whole configuration. - Works offline. Changes queue locally and replay when the network returns. The app is usable on a train.
- It won't break your data. Properties from other clients survive an edit untouched.
- Keyboard-first, and installable. Shortcuts for everything; add it to a phone's home screen as a PWA.
No notifications, no streaks, no nagging. There's no account with me. You sign in with your existing CalDAV credentials and your todos stay on your server.
I built Fold for myself. Nothing I tried quite fit, and I wanted my todos on hardware I control rather than in someone else's database. CalDAV already solved the storage problem; I wanted a client I'd actually enjoy using.
It's also a deliberate experiment in AI-assisted development end to end. This is what that looks like when the bar doesn't move: full type safety, tests at every layer against a real CalDAV server, every decision written down with its reasoning in docs/, and human review on all of it. I'd have built it the same way by hand; it would just have taken longer.
Use it freely. It's MIT licensed. Bug reports and fixes are genuinely welcome, and CONTRIBUTING.md covers how. Feature requests get weighed against one question: do I want it? So Fold may never do what you want, and that's the point rather than an oversight.
Found a security problem? Please report it privately rather than in an issue. SECURITY.md explains how.
Docker, and one required setting.
# compose.yml
services:
fold:
image: ghcr.io/jackcuthbert/fold:latest
restart: unless-stopped
ports:
- '127.0.0.1:3000:3000'
environment:
# openssl rand -base64 32
SESSION_SECRET: 'replace-me'docker compose up -dSESSION_SECRET encrypts the cookie holding your CalDAV credentials. Keep
it private; changing it signs everyone out. There's nothing else to
configure and nothing to back up, because Fold keeps no database.
Then put a reverse proxy in front of it and sign in with your CalDAV URL,
username and password. To upgrade: docker compose pull && docker compose up -d.
Optional settings and image detail: docs/specs/deployment.md.
Use it. The session cookie carries your CalDAV credentials, so the
connection needs to be private. Fold also marks the cookie Secure, so
browsers silently drop it over plain HTTP. Sign-in appears to work, then
bounces you back to the login screen.
Any reverse proxy will do. With Caddy it's two lines and certificates are automatic:
fold.example.com {
reverse_proxy 127.0.0.1:3000
}Only the browser↔proxy hop needs TLS; your CalDAV server doesn't.
On a trusted LAN with no TLS? Set ALLOW_INSECURE_COOKIE=true to drop
Secure so plain HTTP works. Anyone who can watch that network can then
copy the cookie, so only where you trust every device on it.
compose.yml in this repo runs a local
Radicale for trying Fold out. It's for local
testing only, not your real server. See
docs/development/local-caldav-server.md.
You hand Fold your CalDAV password, so it's fair to ask what happens to it. Fold never stores it. There is no user table and no session store.
sequenceDiagram
autonumber
participant B as Your browser
participant F as Fold server
participant C as Your CalDAV server
Note over F: No database.<br/>Nothing kept between requests.
B->>F: Sign in with CalDAV details
F->>C: Check them
C-->>F: OK
F-->>B: Sealed cookie (encrypted, HttpOnly)
Note over B: The cookie lives here.<br/>Page scripts cannot read it.
B->>F: Later request + cookie
F->>F: Unseal in memory
F->>C: Fetch / change todos
C-->>F: Todos
Note over F: Credentials discarded.
F-->>B: Todos
On sign-in your credentials are checked against your CalDAV server, then
encrypted (AES-256-GCM) into an HttpOnly cookie that only Fold can
open. JavaScript in the browser cannot read it. Each later request
carries that cookie; Fold unseals it in memory, talks to your server, and
keeps nothing. Restarting the container loses no state, because there is
none.
Detail: docs/architecture/sealed-cookie-sessions.md, and docs/specs/security.md for the rest.
By default Fold tries whatever server URL is typed into the login form, which is what makes it work for everyone's setup. If your login page is reachable by people other than you, name the servers it should accept:
CALDAV_ALLOWED_HOSTS: 'dav.example.com, *.example.org, 192.168.1.10:5232'Anything else is refused before a request goes anywhere. Left empty it restricts nothing, which is the default.
- No sub-tasks and no recurring todos. Existing
RRULEproperties are preserved untouched, but Fold won't create or edit them. - No background sync. Queued changes replay when you open the tab, not while it's closed.
- One account at a time.
bun install
SESSION_SECRET=$(openssl rand -hex 16) bun run --filter @fold/server dev
bun run --filter @fold/client dev # second terminalOpen the Vite URL and sign in with your CalDAV server URL + credentials.
| Command | What |
|---|---|
bun run lint / bun run fmt |
oxlint (type-aware, via tsgolint) / oxfmt |
bun run typecheck |
TS 7, strictest |
bun run test |
unit tests (vitest) |
bun run test:integration |
gateway vs a real Radicale (spawns a container) |
bun run test:e2e |
Playwright (needs chromium; Docker for one spec) |
bun run screenshot |
regenerate the README screenshots |
bun run favicons |
rebuild the favicon PNGs from favicon.svg |
Most e2e specs run against an in-memory fake CalDAV gateway inside the BFF, so only the one real-CalDAV spec needs Docker. See docs/specs/testing.md.
- Specifications: docs/specs
- Architecture decisions: docs/architecture
- User guide: https://jackcuthbert.github.io/fold/ (source in
apps/docs/guide) - Development notes: docs/development
- Agent rules: CLAUDE.md
The published site is built from apps/docs/guide. To edit it, run it
locally with live reload:
bun run docsThat serves it at http://localhost:5174/fold/. To check what would
actually ship: bun run docs:build, then bun run docs:preview. See
docs/specs/docs-site.md.

