/etc/ufw/user.rules is world-readable, so this needed no privilege after all. The default input policy is DROP and 8899 is not among the allowed ports, so pebble's connection to the suite's listener is dropped. That matches the live run, where twelve probes from a container completed no handshake while the same openssl reached pebble's own TLS port. The nc readings that pointed the other way — instant refusals on closed ports, where a DROP should hang — are still unexplained, and are left on the page as unexplained rather than quietly dropped, since they are what sent an earlier pass through this page in the wrong direction. Nobody has added the allow rule and re-run the suite, so the fix is written down as a prediction. The cutover draft says the same: the test failing here is not evidence that renewal works on the host.
5.7 KiB
Cutting INBUXA over to the fork
Status: draft, 2026-09-19. Nothing here has been rehearsed yet.
SPEC.md §7 step 4. Steps 1 to 3 are met: the fork builds and its suites pass
(container-tests.md), and all eight compat tests pass against a copy of
INBUXA's store (compat-tests.md). What is left is running it as the mail
server.
What the fork has never done
Everything proven so far was proven in a test harness. On the day, the fork has to do things nothing has yet asked of it:
- hold INBUXA's ports (25, 465, 587, 143, 993, 110, 995, 443) under load,
as an unprivileged user with
CAP_NET_BIND_SERVICE; - keep certificates renewing. ACME renewal was found broken and fixed
(
309835b), and the test that still fails here fails for a reason local to this machine:ufwdrops the port pebble validates against (container-tests.md). That is not evidence the renewal works on the host, only that the failure isn't the fork's. A certificate that fails to renew is a 90-day fuse, not an outage on the day, so it is the first thing to watch and the least likely to announce itself; - write to a store that the Enterprise build wrote until an hour earlier.
The rehearsal below exists to turn those from assumptions into observations.
The clock
The Enterprise subscription ends at 30 days' notice, and the server falls back to the Community Edition 15 days after that (SPEC.md §7). So the fork must be carrying INBUXA before anything triggers that notice, and the rehearsal has to happen before the cutover, not instead of it.
Anything that has to be recorded from the Enterprise server — the tenant
administrators' view, which tenant_compat still hasn't checked — has to be
captured before the cutover, because afterwards there is nothing to record
from (compat-tests.md).
Before the day
- Build the artifact.
cargo build --release -p inbuxa, on the same glibc as the host or in the container image.inbuxa --versionmust read2026.9.18 (Stalwart 0.16.22), naming the upstream base the data belongs to (SPEC.md §2.6). - Rehearse on a copy. Restore a snapshot of
/opt/stalwart/dataonto a spare host or container, point the fork at it, and put it through what a mail server does: receive over SMTP, read over IMAP and JMAP, send, sign in to both front ends, let a queued message deliver. Then throw the copy away. This is the step that turns "the data opens" into "the server runs on it". - Decide the rollback, and write down its cost. See below: rolling back is only clean up to the moment the fork accepts its first message.
- Read the boot warnings.
INBUXA_*settings fall back to theirSTALWART_*names with a warning to rename them (SPEC.md §2.5). The Enterprise license object in the registry has no meaning to the fork, which has no licence concept at all; the compat runs booted on a copy without complaining about it, but on the host, read the startup log rather than assume.
The sequence
The install is native: /opt/stalwart with its data in /opt/stalwart/data
and a stalwart.service unit. The fork ships inbuxa.service with the same
shape, including AmbientCapabilities=CAP_NET_BIND_SERVICE, which is what
lets an unprivileged process hold port 25.
- Announce the window. Mail will queue at the sending side while the server is down; nothing is lost, but delivery is delayed.
systemctl stop stalwart.- Snapshot
/opt/stalwart/data— the whole directory, from the stopped server. This is the rollback. - Install the fork's binary and unit. Keep the Enterprise binary where a rollback can reach it without a download.
- Start the fork. Watch the log before opening the ports to the world.
- Work through the checks below.
- Let mail flow.
Before letting mail flow
- The version string names the right upstream base.
- Every listener is up, and the certificate served on 443 and 993 is the current one, not a self-signed fallback.
- An account signs in over IMAP, over JMAP, and through both front ends.
- A message sent from outside arrives; a message sent from inside leaves.
- The tenant's administrator can still see its own accounts and domains, and nobody else's.
- The queue is draining, and the task queue has no new permanent failures.
Rollback
Restoring the snapshot from step 3 and starting the Enterprise build returns the server to exactly where it was, and loses every message accepted since. That is clean while the window is short and nothing has been accepted.
Rolling back after the fork has been serving is not a snapshot restore. The fork writes objects its own features own — SCIM's event ids and the rebuilt features' records — into a store the Enterprise build would then read. Whether it tolerates them has never been tested, and testing it is worth an hour before the day rather than an argument at 2am. Until it is tested, treat the cutover as forward-only once mail has been accepted, and make the decision to roll back early rather than late.
The first week
- Certificate renewal. The one thing that fails quietly and late. Watch for the first renewal rather than waiting for an expiry.
- The task queue, for work that retries forever rather than failing.
- The queue, for mail that leaves more slowly than it did.
- The logs, for the
STALWART_*fallback warnings, which name settings worth renaming while nobody is under pressure.
Open
- Whether the Enterprise build can read a store the fork has written, which decides whether rollback stays available after mail has been accepted.
- Whether ACME renewal works on the host, which the test suite has not been
able to settle (
container-tests.md). - Whether the front ends need anything at cutover, or follow separately (SPEC.md §5).