CalDAV (Calendar Sync)
bext can serve as a CalDAV server (RFC 4791), enabling calendar synchronization with any standards-compliant client — Apple Calendar, Thunderbird, GNOME Calendar, DAVx5, and others.
Enabling CalDAV
[caldav]
enabled = true
storage = "memory" # "memory" | "pg"
# For PostgreSQL storage
# storage = "pg"
# pg_url = "postgresql://user:pass@localhost/bext_dav"
Compile with the caldav feature flag:
bext build --features caldav
WebDAV Foundation
CalDAV is built on WebDAV. bext implements the required HTTP methods:
| Method | Purpose |
|---|---|
PROPFIND |
Read properties of calendars and events. |
PROPPATCH |
Modify calendar properties. |
REPORT |
Run queries (time-range, multiget, sync). |
MKCALENDAR |
Create a new calendar collection. |
PUT |
Create or update a calendar event. |
GET |
Retrieve a single event (.ics). |
DELETE |
Remove an event or calendar. |
URL Structure
Calendar collections follow a predictable hierarchy:
/calendars/{user}/ # User's calendar home
/calendars/{user}/{calendar}/ # A specific calendar
/calendars/{user}/{calendar}/{uid}.ics # A single event
Example:
/calendars/alice/work/
/calendars/alice/work/meeting-2026-04.ics
/calendars/alice/personal/
iCalendar Format
Events are stored as iCalendar (RFC 5545) .ics files. bext parses and
validates them on PUT:
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//bext//CalDAV//EN
BEGIN:VEVENT
UID:meeting-2026-04@example.com
DTSTART:20260415T090000Z
DTEND:20260415T100000Z
SUMMARY:Team Standup
END:VEVENT
END:VCALENDAR
Supported component types:
| Component | Description |
|---|---|
VEVENT |
Calendar events. |
VTODO |
Tasks / to-do items. |
VJOURNAL |
Journal entries / notes. |
CalDAV Reports
Clients query calendars using REPORT requests with XML bodies. bext supports three report types:
calendar-query
Filter events by property or time range:
<c:calendar-query xmlns:c="urn:ietf:params:xml:ns:caldav">
<d:prop xmlns:d="DAV:">
<d:getetag/>
<c:calendar-data/>
</d:prop>
<c:filter>
<c:comp-filter name="VCALENDAR">
<c:comp-filter name="VEVENT">
<c:time-range start="20260401T000000Z"
end="20260430T235959Z"/>
</c:comp-filter>
</c:comp-filter>
</c:filter>
</c:calendar-query>
calendar-multiget
Fetch specific events by URL in a single request:
<c:calendar-multiget xmlns:c="urn:ietf:params:xml:ns:caldav">
<d:prop xmlns:d="DAV:">
<d:getetag/>
<c:calendar-data/>
</d:prop>
<d:href>/calendars/alice/work/meeting-2026-04.ics</d:href>
<d:href>/calendars/alice/work/review-2026-04.ics</d:href>
</c:calendar-multiget>
sync-collection
Efficient incremental sync (RFC 6578). The client sends a sync token from the previous sync, and bext returns only changes since then:
<d:sync-collection xmlns:d="DAV:">
<d:sync-token>data:,12345</d:sync-token>
<d:prop>
<d:getetag/>
</d:prop>
</d:sync-collection>
bext responds with new/modified/deleted resources and a fresh sync token. This avoids full re-fetch on every sync cycle.
Discovery
Clients discover CalDAV using the .well-known redirect (RFC 6764):
GET /.well-known/caldav
→ 301 /calendars/
bext registers this redirect automatically when CalDAV is enabled. Client apps only need the server hostname to find the calendar home.
Storage Backends
| Backend | Description |
|---|---|
memory |
In-memory store. Fast, lost on restart. |
pg |
PostgreSQL via @bext/infra-pg. Persistent. |
The PostgreSQL backend shares its schema with CardDAV (contacts), so both features use the same database.
Authentication
CalDAV endpoints require authentication. bext uses the same auth middleware as HTTP routes — JWT, API keys, or OAuth:
[[routes]]
path = "/calendars/*"
auth = "required"
The {user} in the URL path is matched against the authenticated
identity. Users can only access their own calendars unless granted access
via sharing.
Configuration Reference
[caldav]
enabled = true
storage = "memory"
max_event_size_bytes = 65536
max_events_per_calendar = 50000
| Field | Default | Description |
|---|---|---|
storage |
memory |
Storage backend. |
max_event_size_bytes |
65536 | Max size of a single .ics upload. |
max_events_per_calendar |
50000 | Safety limit per calendar. |
Feature Flag
CalDAV requires the caldav feature flag:
bext build --features caldav
The memory storage backend loses all calendar data on restart. Use
storage = "pg" for any deployment where data persistence matters. The
PostgreSQL backend shares its schema with CardDAV, so both features use
the same pg_url.
Client apps only need the server hostname — the .well-known/caldav
redirect handles discovery automatically. Pointing clients at
/calendars/ directly bypasses this and may break auto-configuration in
some apps (Apple Calendar, DAVx5).
Related
- CardDAV (Contacts Sync) — companion protocol that shares the same WebDAV stack and storage
- Auth capability — authentication middleware applied to
/calendars/*routes - Storage capability — object and key-value storage; PostgreSQL backend for CalDAV is separate
- Build flags — compile-time feature flags including
caldav - Configuration reference — full
bext.config.tomlfield reference
Links
- RFC 4791 — CalDAV: Calendaring Extensions to WebDAV — the CalDAV protocol specification
- RFC 5545 — iCalendar — the iCalendar format used for
.icsevent files