From fc621cdf81527400bf5ad1eec33baed7a5fe573e Mon Sep 17 00:00:00 2001
From: Ronni Baslund
Date: Fri, 12 Jun 2026 15:31:27 +0200
Subject: [PATCH] =?UTF-8?q?fix(mail):=20drop=20contacts=20from=20the=20EAS?=
=?UTF-8?q?=20bundle=20=E2=80=94=20Stalwart=20404s=20empty=20addressbook?=
=?UTF-8?q?=20homes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
BackendCombined login is all-or-nothing, and Stalwart returns 404 for
/dav/card// when the account's default address book was never
created (it doesn't auto-create on the gateway's PROPFIND the way the
calendar home worked) — so CardDAV killed every otherwise-successful
EAS login. Exchange accounts now bundle mail + calendar; contacts stay
on the Apple-profile CardDAV path. Re-enable BackendCardDAV once
platform-api provisions a default address book at mailbox creation.
Copy/docs aligned: portal hint, SERVICES.md, website FAQ (da+en).
---
apps/portal/pages/admin/users.vue | 2 +-
apps/website/utils/landingCopy.ts | 4 ++--
docs/SERVICES.md | 7 +++++--
services/zpush/config/combined.config.php | 25 ++++++++++++++---------
4 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/apps/portal/pages/admin/users.vue b/apps/portal/pages/admin/users.vue
index 79d1705..36ef3e6 100644
--- a/apps/portal/pages/admin/users.vue
+++ b/apps/portal/pages/admin/users.vue
@@ -769,7 +769,7 @@ async function submitCreateMailbox() {
This member signs in with an external address and has no inbox. Create a mailbox on your domain to give them one — and to enable aliases.
- On iPhone and Android the mailbox can also be added as an Exchange account — mail, calendar and contacts in one go. Server {{ mailHost }}, username is the mailbox address. Works in the built-in mail apps (not the Outlook app); on Windows, Outlook syncs the calendar via the free CalDAV Synchronizer add-in.
+ On iPhone and Android the mailbox can also be added as an Exchange account — mail and calendar in one go. Server {{ mailHost }}, username is the mailbox address. Works in the built-in mail apps (not the Outlook app); on Windows, Outlook syncs the calendar via the free CalDAV Synchronizer add-in.
diff --git a/apps/website/utils/landingCopy.ts b/apps/website/utils/landingCopy.ts
index 837361d..11c945f 100644
--- a/apps/website/utils/landingCopy.ts
+++ b/apps/website/utils/landingCopy.ts
@@ -123,7 +123,7 @@ export const COPY = {
heading: 'Det vi bliver spurgt om.',
items: [
['Hvordan virker migration fra Microsoft 365?', 'Vi flytter mail, kalender, kontakter og OneDrive-filer i baggrunden, mens jeres team arbejder videre. Skifte-dagen er en DNS-opdatering. Typisk forløb er 2–4 uger for 50 brugere.'],
- ['Kan jeg stadig bruge Outlook og Office?', 'Ja. Mail virker i Outlook og alle IMAP-klienter. Kalender og kontakter synkroniserer via CalDAV/CardDAV — og som Exchange-konto i mobilens indbyggede apps. Outlook på Windows synkroniserer kalenderen via det gratis CalDAV Synchronizer-tilføjelsesprogram. Drev-filer åbnes med Office desktop via WebDAV. Vi anbefaler vores web- og mobil-apps som primært valg, men kravet er ikke at I skifter vaner.'],
+ ['Kan jeg stadig bruge Outlook og Office?', 'Ja. Mail virker i Outlook og alle IMAP-klienter. Kalender og kontakter synkroniserer via CalDAV/CardDAV — og mail + kalender som Exchange-konto i mobilens indbyggede apps. Outlook på Windows synkroniserer kalenderen via det gratis CalDAV Synchronizer-tilføjelsesprogram. Drev-filer åbnes med Office desktop via WebDAV. Vi anbefaler vores web- og mobil-apps som primært valg, men kravet er ikke at I skifter vaner.'],
['Hvor er data hosted?', 'Hos Hetzner i Tyskland. Tier III-certificerede datacentre, redundant strøm og netværk, ISO 27001-certificeret operatør. Ingen data forlader EU på noget tidspunkt — ikke for analytics, logs eller support.'],
['Hvad sker der hvis dezky lukker?', 'Hele stakken er open source. I kan eksportere alt og flytte til en anden dezky-partner. Vores forretningsmodel er drift, ikke gidseltagning.'],
['Hvad er jeres SLA?', '99,9 % uptime garanteret på alle planer. 99,95 % på Enterprise. Status-side med real-time data offentligt tilgængelig på status.dezky.eu.'],
@@ -633,7 +633,7 @@ export const COPY = {
heading: 'What we get asked.',
items: [
['How does migration from Microsoft 365 work?', 'We move mail, calendar, contacts and OneDrive files in the background while your team keeps working. Cutover day is a DNS update. Typical timeline is 2–4 weeks for 50 users.'],
- ['Can I still use Outlook and Office?', 'Yes. Mail works in Outlook and any IMAP client. Calendar and contacts sync via CalDAV/CardDAV — and as an Exchange account in the phone\'s built-in apps. Outlook on Windows syncs the calendar via the free CalDAV Synchronizer add-in. Drive files open with Office desktop via WebDAV. We recommend our web and mobile apps, but we don\'t require you to change habits.'],
+ ['Can I still use Outlook and Office?', 'Yes. Mail works in Outlook and any IMAP client. Calendar and contacts sync via CalDAV/CardDAV — and mail + calendar as an Exchange account in the phone\'s built-in apps. Outlook on Windows syncs the calendar via the free CalDAV Synchronizer add-in. Drive files open with Office desktop via WebDAV. We recommend our web and mobile apps, but we don\'t require you to change habits.'],
['Where is data hosted?', 'With Hetzner in Germany. Tier III certified data centers, redundant power and network, ISO 27001 certified operator. No data leaves the EU at any time — not for analytics, logs or support.'],
['What happens if dezky shuts down?', 'The whole stack is open source. You can export everything and move to another dezky partner. Our business model is operations — not hostage-taking.'],
['What\'s your SLA?', '99.9% uptime guaranteed on all plans. 99.95% on Enterprise. Public real-time status page at status.dezky.eu.'],
diff --git a/docs/SERVICES.md b/docs/SERVICES.md
index a40fbfd..8e96b34 100644
--- a/docs/SERVICES.md
+++ b/docs/SERVICES.md
@@ -158,8 +158,11 @@ docker compose port stalwart 25
**URL:** https://mail.dezky.local/Microsoft-Server-ActiveSync (+ EAS
autodiscover on https://autodiscover.dezky.local)
**Purpose:** Exchange ActiveSync gateway in front of Stalwart — "Exchange"
-accounts on iOS/Android native Mail/Calendar get two-way mail + calendar +
-contacts sync (IMAP + CalDAV + CardDAV fan-out via BackendCombined).
+accounts on iOS/Android native Mail/Calendar get two-way mail + calendar
+sync (IMAP + CalDAV fan-out via BackendCombined). Contacts are NOT bundled
+yet: the combined login is all-or-nothing and Stalwart 404s addressbook
+homes that were never created — re-enable CardDAV once platform-api
+provisions a default address book at mailbox creation.
**Protocol reality check:** EAS 14.1. Covers native mobile clients; NOT the
Outlook mobile app (requires EAS 16.1) and not new Outlook for Windows (no
diff --git a/services/zpush/config/combined.config.php b/services/zpush/config/combined.config.php
index 643fce8..14f9a00 100644
--- a/services/zpush/config/combined.config.php
+++ b/services/zpush/config/combined.config.php
@@ -1,10 +1,16 @@
BackendCardDAV once platform-api
+// provisions a default address book at mailbox creation. Apple devices get
+// contacts via the .mobileconfig CardDAV payload meanwhile.
class BackendCombinedConfig {
@@ -13,7 +19,6 @@ class BackendCombinedConfig {
'backends' => array(
'i' => array('name' => 'BackendIMAP'),
'c' => array('name' => 'BackendCalDAV'),
- 'd' => array('name' => 'BackendCardDAV'),
),
'delimiter' => '/',
'folderbackend' => array(
@@ -28,10 +33,10 @@ class BackendCombinedConfig {
SYNC_FOLDER_TYPE_USER_APPOINTMENT => 'c',
SYNC_FOLDER_TYPE_TASK => 'c',
SYNC_FOLDER_TYPE_USER_TASK => 'c',
- SYNC_FOLDER_TYPE_CONTACT => 'd',
- SYNC_FOLDER_TYPE_USER_CONTACT => 'd',
- // No notes/journal store in Stalwart — let mail own the rest
- // so folder creation never lands on a DAV backend by surprise.
+ // No contacts/notes/journal on EAS for now — let mail own
+ // them so folder creation never lands somewhere surprising.
+ SYNC_FOLDER_TYPE_CONTACT => 'i',
+ SYNC_FOLDER_TYPE_USER_CONTACT => 'i',
SYNC_FOLDER_TYPE_NOTE => 'i',
SYNC_FOLDER_TYPE_USER_NOTE => 'i',
SYNC_FOLDER_TYPE_JOURNAL => 'i',