Say up front what an operator has to fix before migrating

Both blockers found on the production run were changes to the directory, not
flags, and both were discovered by reading a preflight failure rather than
the documentation. They now lead the README.

Multi-tenancy had no section at all: v0.16 requires a tenant-scoped account
to sit on a domain owned by that same tenant, v0.15 did not, and an install
that is valid today can be unrepresentable tomorrow. Where one tenant's
accounts use a tenant-less domain the conversion repairs it; where two
tenants share a domain nothing can, and it has to be resolved in v0.15.

The admin section predated the preflight check that now refuses a
fallback-admin, and it stopped one step short: the account has to keep its
rights *after* the migration, not merely exist. An account whose admin came
only from `tenant-admin` authenticates afterwards and is still refused every
management operation, and preflight cannot predict that - it cannot know
which roles the converter carries across.
This commit is contained in:
2026-08-24 17:17:04 -07:00
parent 3342662b33
commit 7ec1e70109
+40 -3
View File
@@ -77,6 +77,33 @@ Lines are implementation only; each package carries its tests alongside.
| `internal/plan` | 130 | yes |
| `internal/config` | stub | — |
## Before you start: two things you must fix on the server
Neither is something this tool can do for you, and both stop a migration
dead. `preflight` refuses on both, while the mail server is still running —
but they are worth knowing before you book a maintenance window, because
fixing them is a change to your directory, not a flag.
1. **Remove or collapse multi-tenancy.** v0.16 requires a tenant-scoped
account to sit on a domain owned by that same tenant, for its primary
domain and every alias. v0.15 imposed no such rule, so an install that is
perfectly valid today can be unrepresentable in v0.16. Run
`stalwart-migrate tenants` to see who owns what. Where a domain has no
tenant of its own and only one tenant's accounts use it, the conversion
repairs it for you; where two tenants genuinely share a domain, nothing
can, and you must resolve it in v0.15 first — give each tenant its own
domains, move the accounts into one tenant, or remove the tenants
entirely.
2. **Migrate as a directory account, not the built-in admin.** A
`[authentication.fallback-admin]` from `config.toml` authenticates
perfectly well right up to the moment the migration finishes, and then
stops existing — v0.16 keeps its configuration in the store, so the block
defining it is never read again. The migration itself still succeeds; what
you lose is the ability to verify it, recalculate quotas, or administer
the server afterwards. See [You need a named admin
account](#you-need-a-named-admin-account-before-you-migrate).
## Why not a shell script
Stalwart's 0.15 → 0.16 boundary is not a drop-in binary swap: settings move,
@@ -249,9 +276,19 @@ against a real 0.15.5 → 0.16.14 migration:
role rather than risk granting administrator rights to the wrong one.
It says so rather than guessing; you then grant it by hand.
The practical check: before you migrate, make sure you can authenticate to
the admin API as a directory account — not as the fallback admin — and that
its local part is unique across your domains.
4. **The role has to survive, not just the account.** `tenant-admin` has no
v0.16 equivalent and is not restored, so an account whose rights came
only from it authenticates afterwards and is still refused management
operations. Preflight cannot check this — it cannot know which roles the
converter will carry across — so confirm on a clone, or immediately
afterwards, that the account can still administer the server.
`preflight` refuses to proceed if the account you authenticate with is not
in the directory, so this is caught before anything is touched rather than
after the migration completes. The practical check: make sure you can
authenticate to the admin API as a directory account — not as the fallback
admin — that its local part is unique across your domains, and that it holds
admin rights through a role other than `tenant-admin`.
## Recovery is your job