arnefandCopilot b4644bc590 Fix WebDAV/CalDAV/CardDAV bugs, drop username from URLs, harden concurrency
- Root handler now only serves the welcome page for GET/HEAD; all other
  methods (e.g. OPTIONS, PROPFIND) return 405 with an Allow header instead
  of always returning 200, fixing client capability probes and PROPFIND
  misbehavior.
- Mount /files/ properly and cache one xwebdav.Handler per authenticated
  user so its LockSystem persists across requests instead of being
  recreated per-request (which broke LOCK/UNLOCK).
- Remove the username segment from all DAV URLs (/cal/, /card/, /files/
  are now identical for every account; the acting user is always resolved
  via Basic Auth, never the path).
- Reintroduce a fixed literal "home" path segment (/cal/home/,
  /card/home/) to preserve the URL segment depth that go-webdav's
  caldav/carddav server relies on to classify resources (principal vs.
  home-set vs. collection vs. object). Removing the username had
  collapsed this depth, silently misclassifying requests and returning
  empty <multistatus> responses (DAVx5 "no resources found").
- Replace the store's single global mutex with per-user sharded locks so
  different users' requests no longer serialize against each other.
- Add auth.NewContext test helper, WebDAV handler tests
  (per-user isolation, lock persistence across requests), and a
  concurrent multi-user store test.
- Update README and copilot-instructions to document the new URL scheme
  and the go-webdav path-depth classification quirk.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-18 12:49:57 +02:00
wip
2026-04-23 21:56:59 +02:00
wip
2026-04-23 21:56:59 +02:00
wip
2026-04-23 21:56:59 +02:00
wip
2026-04-23 21:56:59 +02:00
wip
2026-04-23 21:56:59 +02:00
wip
2026-04-23 21:56:59 +02:00
wip
2026-04-23 21:56:59 +02:00
wip
2026-04-23 21:56:59 +02:00

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
  • 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

go mod tidy

2. Generate password hashes

go run ./tools/hashpwd mysecretpassword
# Outputs: $2b$12$...

3. Edit config.yaml

Replace the placeholder hashes with your real bcrypt hashes:

users:
  alice:
    password: "$2b$12$<hash generated above>"
    display_name: "Alice Smith"
    email: "alice@example.com"
    calendars:
      - personal
      - work
    address_books:
      - contacts

4. Run the server

make run
# or
go run ./cmd/server -config config.yaml

The server starts at http://localhost:8080.


Docker

# 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>/ 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/<book>/ 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.


TLS / Reverse proxy

Self-signed certificate (development)

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

Update config.yaml:

tls:
  enabled: true
  cert_file: cert.pem
  key_file:  key.pem
dav.example.com {
    reverse_proxy localhost:8080
}

Configuration reference

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:
  <username>:
    password: "<bcrypt hash>"
    display_name: "Full Name"
    email: "user@example.com"
    calendars:       # pre-created calendar names
      - personal
    address_books:   # pre-created address book names
      - contacts

Project layout

caldav-server/
├── cmd/server/          # main entrypoint
├── internal/
│   ├── auth/            # HTTP Basic Auth middleware
│   ├── caldav/          # CalDAV backend
│   ├── carddav/         # CardDAV backend
│   ├── config/          # YAML config loader
│   ├── store/           # filesystem storage layer
│   └── webdav/          # WebDAV file handler
├── tools/hashpwd/       # bcrypt password hasher CLI
├── config.yaml          # sample configuration
├── Dockerfile
├── docker-compose.yaml
└── Makefile

Running tests

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
S
Description
No description provided
Readme AGPL-3.0
826 KiB
v2026.9.2
Latest
2026-09-08 20:21:01 +00:00
Languages
Go 82.1%
templ 12.8%
JavaScript 2.3%
TypeScript 2.1%
Makefile 0.4%
Other 0.3%