Calendar scheduling (iTIP)
When users invite each other, the server exchanges the scheduling messages (REQUEST / REPLY / CANCEL) by email, per RFC 5546. Two rules keep this from misfiring:- Organizer-side only. OxiMail sends scheduling mail only for events whose ORGANIZER resolves to a local account (RFC 5546 §3.2: only the organizer’s server sends invitations). An event that arrived as an inbound invitation never re-emits a REQUEST when the attendee’s client writes it back, and deleting a received copy never broadcasts an unauthorized CANCEL — on both the JMAP and CalDAV paths, fail-closed.
- Emitted after the commit. iTIP messages and calendar events are emitted only once the transaction that changed the event has actually committed. A concurrent-edit conflict (
stateMismatch) or any commit failure emits nothing — invitations can no longer escape for a change that was rolled back, and an update REQUEST always carries the post-change content with the bumpedSEQUENCE.
text/calendar parts) are processed on ingestion, including Outlook’s parameterised property style, and update the recipient’s calendar.
Free-busy and availability
Three read surfaces answer “when is this person free”, all through one occurrence-enumeration authority (a recurring event expands identically everywhere, DST included):Principal/getAvailability(JMAP) composes availability from subscribed calendars whoseincludeInAvailabilityisallorattending; aprivacy: "secret"event hides its busy time entirely; event details are only populated for callers who hold real read access — free-busy access never implies detail access. A caller with free-busy rights on none of the subject’s calendars gets an explicitforbidden, not a misleading empty answer.- CalDAV
free-busy-queryreturns aVFREEBUSYof merged busy periods (tentative counts as busy; free/transparent, trashed, and cancelled events are excluded). - The scheduling UI in a client builds on the same data.
Time zones
Floating events (no explicit zone) resolve through a fallback chain: event zone → user zone → organization default → UTC. The wizard captures the organization default at install (first boot); change it later with:VTIMEZONE so strict clients compute correct local times.
Notifications
Calendar changes produceCalendarEventNotification objects for the people concerned: a grantee’s write on a shared calendar notifies the owner, and an inbound scheduling message notifies the affected user (REQUEST/CANCEL → the recipient, REPLY → the organizer; self-authored changes are suppressed). Notifications are change-logged and pushed, so clients see them without polling. Alerts (reminders) support both offset and absolute triggers and fire once per occurrence through a deduplication table.
Tasks
Task lists mirror calendars: JMAP methods, CalDAV VTODO bridge, shared lists. Each account has exactly one default task list, enforced by a database invariant — aTask/set create without taskListIds goes there. Alerts on tasks support absolute triggers without requiring a due date.
Contacts
Contacts are JMAP ContactCards (RFC 9610) with a CardDAV bridge. The server owns theuid (a client-supplied one on create is rejected), and a CardDAV PUT that would create a cross-source duplicate of an existing UID is refused rather than silently forked. Name and address components round-trip completely between vCard and the JMAP form.
Sharing
Calendars, task lists, address books, and drive folders share one grant model (shareWith maps keyed by principal, RFC 9670 shapes). Two operator-relevant guarantees:
- Per-viewer state stays per-viewer. A grantee hiding a shared calendar (
isVisible) or starring a shared file affects their own view only, never the owner’s object. - Grantees must be human-backed. A grant to a non-loginnable technical identity is rejected at write time on every domain — a grant that could never be exercised is a lie in the ACL. Shared accounts’ drive custody has its own surface:
oximail account custodian(see multi-tenancy).
Repair one-shots
Migration-era artifacts have dedicated, dry-run-by-default repair commands:
Both record JMAP changes so clients resync cleanly. See the CLI reference.