oximail migrate imports existing data into a provisioned account. The setup wizard offers it as its final step, and it can be run standalone any number of times — the import is resumable and keeps its own progress state, so a re-run continues instead of duplicating.
Sources
What the import gets right
- Folder roles are detected from names. A localized source folder with no role metadata (“Brouillons”, “Papierkorb”, “Sent Items”, …) merges into the matching provisioned system folder through a multilingual name→role table, instead of arriving as a duplicate custom folder next to an empty system one. This applies in every mode, including JMAP-mode imports.
- Recurring events become real recurrence rules. Common client
RRULEs are converted into typed JMAPRecurrenceRulesthrough the same conversion authority the live server uses. An unconvertible residual is kept verbatim (and the event is flaggedhasUnmodeledRecurrenceon read) with a loud warning — never a half-converted rule, never a silent drop. An event carries one rule: aVEVENTwith twoRRULElines is refused by the store and counted as an error, rather than stored with only its first rule honoured. - A calendar file lands at the right hour. A
DTSTARTin UTC (theZform) is stored as a local time inEtc/UTC, aTZIDas its normalised IANA zone, a floating time or a date unchanged, the rule CalDAV ingestion applies; it used to arrive floating, two hours early in Zurich.DTENDbecomes the event’s duration with its ownTZIDread, aDATEwithout an end lasts one day, and an invalidDTENDcounts the event as an error (duration and end). A file whose lines end in\r\r\n(a CRLF passed through a text-mode writer on Windows) no longer leaves a carriage return in titles and UIDs. Google Takeout goes through the same conversion. - Google Takeout writes what it counts. Its tasks go to the account’s default task list through the same conversion and write as
--from-ics(they used to be counted “ok” and never written; a re-run imports them), and its events land in the account’s default calendar. - Directory identities are minted fresh. Imported accounts get new, distinct principal ids (the account↔principal binding is an internal column, not id equality), matching what the runtime does for natively created accounts.
- The source’s certificate is validated, and no setting turns that off. The client that talks to the source server carries its credentials; it used to disable certificate validation unconditionally while an
accept_invalid_certssetting sat next to it, read by nobody. Validation is PKIX-complete now and the flag is gone rather than wired: the project’s posture is that a production build contains no certificate bypass. The one remaining exception is the post-install check that queries the machine just installed, whose certificate is still self-signed and where no secret travels; what matters is not “internal or external client” but “does this connection carry a secret”. - A source renumbering neither skips nor duplicates. The resume identity of each message includes the
UIDVALIDITYthe source announces; a renumbering forces re-enumeration, and content-addressed dedup absorbs the re-presentation. A source that announces no validity fails the import rather than resuming on ambiguous keys (RFC 3501 §6.3.1). A recycled UID used to skip a message silently, the worse of the two defects. - Everything lands through the normal ingestion path. Imported mail is parsed, indexed, threaded, and stored exactly like delivered mail; there is no separate “imported” storage class.
Idempotence and the state sidecar
The migration records per-account progress in a state sidecar, so an interrupted run resumes and a completed account is skipped on re-run. For a file import (--from-ics, --from-vcf, --from-google-takeout), an event, task or contact already imported is skipped only while the object the state names is still live in the account: one trashed or destroyed since is imported again, and the summary counts it apart (Re-imported (earlier copy deleted or trashed)), where it used to be skipped forever until the state file was deleted. The Stalwart sources and mail keep their state-only resume. Deleting an account — by CLI or by the admin API — purges its migration state, so a delete followed by a re-migration imports it again rather than silently skipping it as “already migrated”.
After the import
Run the two dry-run-by-default repair commands and the DNS check:dedupe commands print their plan without touching anything; add --apply to execute. See Groupware for what exactly they repair, and the CLI reference for all flags.
Accounts must exist before mail can be imported into them — create them with
oximail account create (or the wizard) first. The import authenticates against the source; it never needs the users’ OxiMail passwords.