feat(mail): Z-Push Exchange ActiveSync gateway for mobile clients

Wraps Stalwart in EAS so iOS/Android native Mail/Calendar 'Exchange'
accounts get two-way mail+calendar+contacts sync (BackendCombined:
IMAP + CalDAV /dav/cal/%l/ + CardDAV, credentials pass through).

- services/zpush: Z-Push 2.6.4 (AGPLv3, see LICENSE-NOTES.md) on
  php:8.2-apache-bookworm (trixie dropped libc-client); PHP 8 sysv
  sprintf fatal sed-patched; autodiscover dispatcher answers
  mobilesync schema, proxies outlook schema to Stalwart unchanged
- prod: zpush Deployment (replicas:1, Recreate — file sync state),
  /Microsoft-Server-ActiveSync Ingress on mail.dezky.eu (no redirect,
  POST-heavy), autodiscover.dezky.eu repointed to the dispatcher,
  selectorless stalwart-imaps/-smtps Services (host-Stalwart is
  implicit-TLS only: 993/465, no plain 143/587 — verified on node1)
- CI: build+deploy zpush like the other apps

EAS tops out at 14.1: covers native mobile clients, NOT the Outlook
mobile app (needs 16.1) and not new Outlook for Windows (no EAS).
This commit is contained in:
Ronni Baslund
2026-06-12 11:12:11 +02:00
parent 2e3c0f9188
commit 58a2c8077d
16 changed files with 658 additions and 13 deletions
@@ -29,6 +29,7 @@ volumes:
ocis_config:
ocis_data:
ocis_audit_log:
zpush_state:
portal_node_modules:
platform_api_node_modules:
operator_node_modules:
@@ -67,6 +68,7 @@ services:
- api.dezky.local
- files.dezky.local
- mail.dezky.local
- autodiscover.dezky.local
- office.dezky.local
- collaboration.dezky.local
labels:
@@ -277,7 +279,8 @@ services:
condition: service_healthy
# ─────────────────────────────────────────────────────────────────
# Stalwart Mail — Mail server (SMTP/IMAP/JMAP/CalDAV/CardDAV/ActiveSync)
# Stalwart Mail — Mail server (SMTP/IMAP/JMAP/CalDAV/CardDAV;
# ActiveSync comes from the zpush gateway above)
# ─────────────────────────────────────────────────────────────────
stalwart:
image: stalwartlabs/stalwart:v0.16
@@ -309,6 +312,43 @@ services:
- traefik.http.routers.stalwart.tls=true
- traefik.http.services.stalwart.loadbalancer.server.port=8080
# ─────────────────────────────────────────────────────────────────
# Z-Push — Exchange ActiveSync gateway in front of Stalwart. Gives
# "Exchange" accounts on iOS/Android native Mail/Calendar two-way
# mail+calendar+contacts sync (EAS 14.1 — NOT the Outlook mobile app,
# which requires EAS 16.1, and not new Outlook for Windows). Stateless
# wrt credentials: passes the client's Basic auth straight to Stalwart.
# ─────────────────────────────────────────────────────────────────
zpush:
build: ../../services/zpush
container_name: dezky-zpush
restart: unless-stopped
environment:
CALDAV_SERVER: stalwart
IMAP_SERVER: stalwart
# Hostname devices get pointed at by EAS autodiscover responses.
ZPUSH_HOST: mail.dezky.local
# Where outlook-schema (mail) autodiscover POSTs are proxied to.
MAIL_AUTODISCOVER_UPSTREAM: http://stalwart:8080
volumes:
- zpush_state:/var/lib/z-push
networks: [dezky]
depends_on:
- stalwart
labels:
- traefik.enable=true
# More-specific rule outranks the plain Host(`mail.dezky.local`)
# stalwart router; priority pins it explicitly.
- traefik.http.routers.zpush.rule=Host(`mail.dezky.local`) && PathPrefix(`/Microsoft-Server-ActiveSync`)
- traefik.http.routers.zpush.priority=100
- traefik.http.routers.zpush.tls=true
- traefik.http.services.zpush.loadbalancer.server.port=80
# EAS autodiscover for dev. The same dispatcher proxies outlook-schema
# mail autodiscover through to Stalwart.
- traefik.http.routers.zpush-autodiscover.rule=Host(`autodiscover.dezky.local`)
- traefik.http.routers.zpush-autodiscover.tls=true
- traefik.http.routers.zpush-autodiscover.service=zpush
# ─────────────────────────────────────────────────────────────────
# OCIS — File storage with S3-compatible backend
# ─────────────────────────────────────────────────────────────────