Documentation
CalDAV behavior and storage reference
This page covers Everlock's CalDAV behavior and storage model.
What it supports
- served through
frontend-http - multi-instance and multi-vhost
- one
versioned-storagestore per instance - Everlock-user HTTP authentication
- Everlock access control on
http/calendar/<instance> - multiple calendars per instance
- writable CalDAV collections and event resources
- calendar names, descriptions, colours and time zones set by
MKCALENDAR, extendedMKCOLandPROPPATCH, and any other calendar property a client sets — Apple Calendar's calendar order among them — kept and given back - readable
.icsevent files in versioned storage - recurrence-aware
calendar-querywith the full CalDAV filter calendar-datawithexpandcalendar-multigetsync-collectionwith commit-id sync tokensfree-busy-query
URL shape
/.well-known/caldav
/caldav/
/caldav/principals/me/
/caldav/calendars/
/caldav/calendars/<calendar>/
/caldav/calendars/<calendar>/<event>.ics
Stores and vhosts
store=<name> picks the store explicitly and is used as given — an existing
store, a hosted git repository or a store another instance uses included —
as long as it is a valid name: lowercase letters, digits and -, not first or
last. Without store=, the store is the instance name, and /calendar create
refuses it when a store or repository of that name already exists, another
instance uses it, or Everlock reserves it (everlock-system, everlock-mail
and every backend's bootstrap store such as calendar and
everlock-contacts). The refusal says what the name collides with; pass
store=<name> to use it on purpose, or pick another name.
Instances that share a store serve the same data: deleting a calendar through one deletes it for all.
A vhost belongs to one calendar instance; /calendar create and /calendar set
refuse a vhost another instance maps, comparing without regard to case. The
creating user becomes Owner of the new instance. When two configured
instances list one vhost, the first by name serves it and a warning is logged.
A config file that is not TOML stops the backend from starting, while a single
malformed instance entry is skipped with a warning.
CalDAV lives under /caldav, with discovery at /.well-known/caldav.
An instance holds those two paths on each of its vhosts and nothing else, so
a site, a file share or the contacts backend serves the rest of the same
host; /calendar create and /calendar set refuse a vhost where another backend
already holds one of the two paths. Virtual hosts explains how
backends share a host.
.local vhosts are announced over mDNS as CalDAV services with
path=/caldav as soon as an instance takes them on, and withdrawn when it
gives them up.
A vhost inside a zone the DNS backend serves carries
the RFC 6764 records clients look up when given just the vhost name:
_caldavs._tcp.<vhost> SRV 0 1 <HTTPS port> <vhost>. with a TXT record
path=/caldav beside it, and _caldav._tcp.<vhost> SRV 0 0 0 ., as CalDAV
runs over HTTPS. The port is the one the HTTP frontend serves HTTPS on, and
the records follow a vhost from the moment /calendar create or
/calendar set saves it.
Storage shape
Each calendar instance uses the versioned-storage store it names:
default/
config.toml
team-meeting.ics
work/
config.toml
customer-review.ics
Each event is one .ics file named by the URL the client writes it to. The
file holds what the client sent, with CRLF line breaks and a UID added where
one is missing; when that changed the body, the answer to the write carries no
ETag, and the client fetches the stored version. Collection sync uses the
underlying store commit id as the sync token, and the token a sync hands out
stands for exactly the events it reports.
A conditional write (If-Match, If-None-Match) is checked again as it is
committed, so of two clients writing the same event against the same
version, the second to commit gets 412 and the first one's edit stays.
Calendar properties
A calendar's name, description and colour live in its config.toml. Every
other property a client sets on a calendar is kept there as sent and comes
back when a client asks for it: Apple Calendar's calendar-order keeps the
order of your calendars across devices. PROPPATCH with remove deletes
one. Properties the server works out itself — the ETag, the sync token, the
resource type — are refused with 403, and the request then changes
nothing.
calendar-timezone sets the zone floating times and all-day events are read
in when the TZID of its VTIMEZONE is an IANA name such as
Europe/Berlin. The value is kept as sent in any case, so a Windows zone
name is stored and returned too, and the calendar keeps its zone.
A PROPFIND gets the properties it names, and a 404 for each one the
calendar or event lacks; a request for everything gets the full set, with
calendar-timezone given when a client names it.
Event validation
- require one
VCALENDAR - require one master
VEVENT; related recurrence overrides may share the same resource andUID - require
VERSION:2.0 - require
DTSTART - preserve
UIDor inject one if missing; an update without one keeps the event's - refuse a
UIDanother event of the same calendar uses, and a write that changes theUIDof an existing event, with403no-uid-conflict
The UID check looks the UID up in a set of hashes Everlock keeps in
memory per calendar, built at the calendar's first new event; it reads the
calendar only to name the event that holds a UID. Bulk uploads stay fast,
and a change made to the store by other means is noticed at the next new
event.
That stays intentionally permissive because Android and Apple calendar clients do not always emit identical event shapes.
Query behavior
- recurrence fields such as
RRULEandEXDATEare preserved - queries apply the CalDAV filter as written: component, property and parameter filters, text matches with negation, and time ranges with a start, an end or both
- an event is in a time range when one of its instances overlaps it, cancelled ones included; free/busy counts cancelled and transparent time as free
- recurring events are expanded at query time for range-based views, and a
client asking for
expandgets each instance as an event of its own, in UTC - IANA timezones, floating times, daylight-saving transitions, recurrence exclusions, additional dates, and detached instance overrides are applied during expansion
- free/busy answers are derived from the stored events
- sync uses commit history instead of a second sync database; a sync token the calendar cannot sync from makes the client start over with a full sync