# DAV Server A self-hosted **CalDAV**, **CardDAV**, and **WebDAV** server written in Go. ## Features | Protocol | Use case | |----------|----------| | CalDAV | Calendars — sync with Apple Calendar, Thunderbird, GNOME Calendar, … | | CardDAV | Contacts — sync with Apple Contacts, GNOME Contacts, … | | WebDAV | General file access via Finder, Windows Explorer, Nautilus, … | - HTTP Basic Auth with **bcrypt** password storage - Per-user isolated collections - **Calendar/address book sharing** — grant other users read or write access to your calendars/address books - **Web UI** — a small dashboard (login, manage shares) at `/web/`, built with templ + Tailwind + htmx - Auto-discovery via `/.well-known/caldav` and `/.well-known/carddav` - Optional **TLS** (or use a reverse proxy) - Structured logging (text or JSON) - Graceful shutdown - Docker & Docker Compose support ## Quick start ### 1. Install dependencies ```bash go mod tidy ``` ### 2. Create your `config.yaml` Copy the example config and edit it — `config.yaml` is git-ignored so your real settings never get committed: ```bash cp config.example.yaml config.yaml ``` Users, calendars, and address books are **no longer configured in `config.yaml`** — they live in the SQLite database and are managed with `nidusctl` (see below). ### 3. Run the server ```bash make run # or go run ./cmd/server -config config.yaml ``` The server starts at **http://localhost:8080**. ### 4. Create a user and their resources ```bash go run ./tools/nidusctl -config config.yaml user create alice \ --display-name "Alice Smith" --email alice@example.com # (prompts for a password; use --password to skip the prompt, e.g. in scripts) go run ./tools/nidusctl -config config.yaml calendar create alice personal go run ./tools/nidusctl -config config.yaml addressbook create alice contacts ``` Users can also be created/removed via the web UI (`/web/`) once logged in as an existing user — see **Web UI** below. --- ## Docker ```bash # Build and start docker compose up --build # Or build manually docker build -t davserver . docker run -p 8080:8080 \ -v ./config.yaml:/app/config.yaml:ro \ -v dav-data:/app/data \ davserver ``` --- ## Client configuration ### Apple Calendar / Contacts (macOS / iOS) 1. Go to **Settings → Calendar → Accounts → Add Account → Other → Add CalDAV Account** 2. Enter: - **Server**: `http://yourserver:8080` - **Username**: `alice` - **Password**: your plaintext password 3. The app will auto-discover calendars at `/cal/`. Same flow for **CardDAV** with Contacts app. ### Thunderbird 1. Install the **TbSync** add-on + **CalDAV & CardDAV** provider 2. Add a new account and point it at `http://yourserver:8080/.well-known/caldav` ### GNOME Calendar / Evolution Use the GNOME Online Accounts panel: - **Server**: `http://yourserver:8080` - Check *CalDAV* / *CardDAV* as appropriate --- ## API endpoints | Path | Description | |------|-------------| | `/.well-known/caldav` | Redirects to `/cal/` | | `/.well-known/carddav` | Redirects to `/card/` | | `/cal/` | CalDAV principal (same URL for every user; resolved via Basic Auth) | | `/cal/home/` | Calendar home-set (lists the user's calendars) | | `/cal/home//` | Calendar collection | | `/card/` | CardDAV principal (same URL for every user; resolved via Basic Auth) | | `/card/home/` | Address book home-set (lists the user's address books) | | `/card/home//` | Address book collection | | `/files/` | WebDAV file storage (same URL for every user; resolved via Basic Auth) | | `/healthz` | Health check (unauthenticated) | > **Note:** the `home` segment is a fixed literal (not a username or real > resource) — it exists only to give the calendar/address-book home-set the > path depth that the underlying CalDAV/CardDAV library expects when > classifying resources by URL. Clients should never need to construct > these URLs by hand; they're discovered automatically via > `.well-known` + `current-user-principal` + `calendar-home-set` / > `addressbook-home-set` properties. --- ## Sharing calendars and address books A user can grant another user read or write access to one of their own calendars or address books. Shared resources show up automatically in the grantee's own home-set alongside their own calendars — no separate account or extra client configuration needed. Sharing grants are stored in a small SQLite database at `/nidus.db` (not in `config.yaml`) and can be managed either via the `nidusctl` CLI or the web UI's dashboard (see below): ```bash # Give bob write access to alice's "work" calendar go run ./tools/nidusctl -config config.yaml calendar share alice work bob write # List everyone alice's "work" calendar is shared with go run ./tools/nidusctl -config config.yaml calendar shares alice work # Revoke access go run ./tools/nidusctl -config config.yaml calendar unshare alice work bob # Address books work the same way, using "addressbook" instead of "calendar" go run ./tools/nidusctl -config config.yaml addressbook share alice contacts bob read ``` Or via `make`: `make nidusctl ARGS="calendar share alice work bob write"`. A calendar that `alice` shares with `bob` appears in bob's calendar home-set as `/cal/home/alice~work/` (i.e. `~`) — the data itself still physically lives under alice's own storage; nothing is copied. The same scheme applies to address books under `/card/home/`. Read-only shares reject any write (PUT/DELETE) with `403 Forbidden`. --- ## Web UI A small server-rendered dashboard is served at `/web/` (separate from the DAV endpoints, which stay on HTTP Basic Auth): - **Login** (`/web/login`) — cookie-based session, stored server-side in `nidus.db` (`web_sessions` table), independent of DAV Basic Auth. Includes a "Show/Hide" password toggle to rule out typos before submitting. - **Dashboard** (`/web/`) — lists your own calendars/address books, who they're shared with, and any resources other users have shared with you. - **Share management** — add/remove shares directly from the dashboard (same effect as `nidusctl`); updates happen in place via [htmx](https://htmx.org/) without a full page reload. - **Logout** (`/web/logout`). Implementation: [templ](https://templ.guide/) for type-safe Go HTML templates, [Tailwind CSS v4](https://tailwindcss.com/) for styling, htmx for the sprinkles of dynamic behavior (form submission via POST/DELETE, partial page swaps), and TypeScript (compiled to plain JS, `web/ts/`) for the few bits of client-side-only logic (e.g. the password-visibility toggle) — no separate JS framework needed. The compiled CSS, compiled JS, and the htmx bundle are all embedded into the Go binary (`web/staticassets.go`), so no Node.js is required at runtime, only when you change styles, templates, or TypeScript during development: ```bash make web-deps # once, installs the Tailwind CLI + TypeScript compiler (needs Node.js/npm) make web-assets # regenerate templ code + rebuild web/static/app.css and web/static/*.js ``` --- ## TLS / Reverse proxy ### Self-signed certificate (development) ```bash openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes ``` Update `config.yaml`: ```yaml tls: enabled: true cert_file: cert.pem key_file: key.pem ``` ### Caddy reverse proxy (recommended for production) ``` dav.example.com { reverse_proxy localhost:8080 } ``` --- ## Configuration reference ```yaml server: host: "0.0.0.0" port: 8080 base_url: "https://dav.example.com" # used in DAV responses auth: realm: "My DAV Server" storage: data_dir: "./data" # all user data lives here logging: level: "info" # debug | info | warn | error format: "text" # text | json tls: enabled: false cert_file: "" key_file: "" ``` Users, calendars, and address books are managed via `nidusctl`, not `config.yaml` — see **Managing users** below. ## Managing users All user/calendar/address-book management is done with `nidusctl` (or the web UI). Nothing is stored in `config.yaml` anymore. ```bash # Users nidusctl user create [--display-name NAME] [--email EMAIL] [--password PW] nidusctl user delete nidusctl user list nidusctl user passwd [--password PW] # Calendars nidusctl calendar create nidusctl calendar delete nidusctl calendar list nidusctl calendar share nidusctl calendar unshare nidusctl calendar shares # Address books nidusctl addressbook create nidusctl addressbook delete nidusctl addressbook list nidusctl addressbook share nidusctl addressbook unshare nidusctl addressbook shares ``` Passwords are prompted for interactively (masked, double-entry) when `--password` is omitted. The web UI (`/web/`) also lets a logged-in user create/delete their own calendars and address books from the dashboard. > **Upgrading from an older version?** The `users:` section in > `config.yaml` is no longer read. Recreate your users with > `nidusctl user create` (and their calendars/address books) — there is no > automatic migration from the old config format. --- ## Project layout ``` caldav-server/ ├── cmd/server/ # main entrypoint ├── internal/ │ ├── auth/ # HTTP Basic Auth middleware (DAV endpoints) │ ├── caldav/ # CalDAV backend │ ├── carddav/ # CardDAV backend │ ├── config/ # YAML config loader │ ├── db/ # SQLite store (shares, web UI sessions) │ ├── store/ # filesystem storage layer │ ├── web/ # web UI (cookie sessions, dashboard, share mgmt) │ │ └── templates/ # templ templates (+ generated *_templ.go) │ └── webdav/ # WebDAV file handler ├── tools/hashpwd/ # bcrypt password hasher CLI ├── tools/nidusctl/ # sharing-grant admin CLI ├── web/ # front-end assets: Tailwind input/config, static/ │ └── static/ # compiled app.css + htmx.min.js (embedded into the binary) ├── config.example.yaml # sample configuration (copy to config.yaml) ├── Dockerfile ├── docker-compose.yaml └── Makefile ``` --- ## Running tests ```bash make test # or go test ./... -race ``` --- ## Dependencies | Package | Purpose | |---------|---------| | `github.com/emersion/go-webdav` | WebDAV/CalDAV/CardDAV protocol layer | | `github.com/emersion/go-ical` | iCalendar parsing/serialisation | | `github.com/emersion/go-vcard` | vCard parsing/serialisation | | `golang.org/x/crypto` | bcrypt | | `golang.org/x/net` | `golang.org/x/net/webdav` | | `gopkg.in/yaml.v3` | YAML config parsing | | `modernc.org/sqlite` | Pure-Go SQLite driver (shares, web UI sessions) | | `github.com/a-h/templ` | Type-safe Go HTML templates (web UI) | Front-end (dev-only, not required at runtime — see [Web UI](#web-ui)): | Tool | Purpose | |------|---------| | Tailwind CSS v4 (`web/package.json`) | Utility-first CSS, compiled to `web/static/app.css` | | [htmx](https://htmx.org/) (`web/static/htmx.min.js`, vendored) | Partial page updates without a JS framework |