The plan said the private ops/runbooks still needed retiring. It did not: the steward's Gitea token carries write:organization and can archive repos, which I had wrongly believed it could not (that belief came from a different operation, an org-user rename, needing a different scope). ops/runbooks is now archived — it held the same five files as this repository and was behind it, so nothing was lost, and archived rather than deleted keeps it inspectable and reversible.
The cutover finished 2026-11-02 and this repo is public and read by members, so leaving a plan that reads like outstanding work is actively misleading. A dated banner now says it is complete and retained as the record; the STATE OF PLAY section is re-labelled as pre-cutover and marked superseded, with the one part known to be wrong (Listmonk's mail path, which moved to Scaleway TEM) named rather than silently corrected; and both files point at where live knowledge now lives — the cloudron-ops skill for procedure and co-op/docs/infrastructure.md for the member-facing description.
Written for a fresh session with none of the session history, because the person starting the migration asked whether the CLI had this context. It does not: memory and skills carry over, and the reasoning does not. This records what changed on 2026-11-01/02 that is not obvious from the rest of the plan: inbound 25 confirmed open so outbound 25 needs no ticket; Scaleway TEM proven by real headers (spf, dkim under the UUID selector, dmarc pass at p=reject); and the trap that Listmonk still sends through the Cloudron mail app, so a newsletter must not be assumed to route through the relay. Plus the DNS wildcard shape, the credential confirmations, what remains undone, the duplicate-Synapse caution, and the steward's own fallback route.
The pre-migration baseline was captured 2026-10-30 and the post-restore
checker diffs against it, so drift since then (app image bumps, newer
backup, more portal rows) made a healthy box look wrong.
Regenerated from the live box with the new capture-migration-baseline.py
(same schema/keys, idempotent). Reflects the box as of 2026-11-02.
The previous sheet's record counts could not be reproduced and at least two listed records did not exist, so it was replaced rather than corrected. Rebuilt from a screenshot of the domains.coop panel: 11 records total, of which two are wildcards carrying both A and AAAA. That is the item the old sheet omitted, and it is the one that matters most — every application hostname (chat, portal, panel, dashboard, newsletter, gateway, vault, git, registry, files, forum, email, docs, my, status) resolves ONLY via the wildcard and is not an individual record. A cutover that moves the apex but not the wildcards leaves all of them behind.
Also recorded: the second DKIM selector (UUID-named, 2048-bit) that is almost certainly Scaleway TEM's, and which explains why probing plausible selector names never found it; the absence of any CNAME, CAA, SRV or ACME records; an unexplained server.inference.coop A record that must be identified before it is moved; and the pre-window TTL reduction.
Known gap: row 11's full name and value are truncated in the panel screenshot and must be read in the panel before that row is edited.
Two corrections and one closure. (1) The S3 secretAccessKey is confirmed held externally by the operator, which was the last credential item a restore depends on. (2) The Vault export question is closed: the operator has the passwords outside the server, and the full export stays optional since the vault database rides in the Cloudron backup. (3) The mail ports were listed as "still to confirm" after I reported a timeout as closure. That was wrong for the same reason the earlier port-25 note was: the box has no mail app yet, and Cloudron drops rather than refuses, so a timeout is uninformative. Recorded what is actually known, and the 2026-10-30 IPv6 refusal as the better evidence that those ports are permitted.
Nathan confirmed in the Infomaniak portal that inbound 25 is open, which settles the half that matters most: receiving cannot be replaced by a relay, whereas sending can and will be. So the mail architecture is now: receive on the box, send through Scaleway Transactional Email. That makes an outbound-25 request unnecessary — the plan previously left both halves open, which would have cost a support ticket for nothing. Remaining ports listed as still to confirm rather than assumed.
Nathan confirmed the new box is on Infomaniak VPS Cloud, not Lite. The distinction decides three things this plan depends on: Cloud carries a 99.99% SLA, resizes both ways, and includes one snapshot, whereas Lite has no snapshot and closes outbound 25 outright. Cloud opens port 25 on justified request, which is what to cite in the support thread. Recorded alongside it: no snapshot has been taken on the new box yet, which is worth doing before the wizard is submitted.
They were published to co-op/runbooks for member review, so the README's own 'Private: these contain...' line was both wrong and the first thing a reader saw. Also records the DNS sheet's wildcard caveat up front, and says explicitly that this repository is the source (no private copy).
docs.inference.coop does not exist — it resolved only because *.inference.coop answers, which is how it got into this sheet (and into the digest footer, since fixed to point at git.inference.coop/co-op/docs). Adds the consequence that matters for cutover: a resolving name proves nothing here, so reconcile against the registrar's record list rather than by resolution.
Answered the subscription question with our own SpendLogs rather than guessing: 452 requests, $4.38, essentially all in the last 30 days, against $426 lifetime gateway spend. A plan must beat ~$4.38/month; Regolo prices are not published. Their 30-day trial (1M tokens/day) would cover our ~120k/day. Also puts the 2026-11-01 change in money terms: ~$1/month, so accuracy rather than urgency.
Records the announced table, our current applied costs, and the two models we serve. apertus-70b input rises 152% and qwen3.5-122b input falls 40%; until the gateway costs are updated we under-bill apertus by ~70c/M and over-bill qwen by ~46c/M. Also documents that regolo/qwen-image prices EUR as USD with no conversion.
Measured from the agent host: setup page live on both IPs, 80/443 open on v4 (rules gap fixed), cert is a valid Let's Encrypt IP certificate (not self-signed) so no ACME exposure for the domain, no cloudron CLI on a server, and 25/465/587/993/995/4190 still closed there.
Current Cloudron serves the setup page on the server IP with a self-signed cert and takes the domain in the wizard, so cloudron-setup needs no DNS and, with the domain left unset, makes zero certificate attempts: the rate-limit risk only appears if the domain is entered while DNS still points at Hostinger. Adds the post-install checks (version >= 10.1.5, pristine, no apps) and a VPS snapshot to roll back to.
Passphrase + Vaultwarden master password confirmed off-box in the operator's KeePass vault. S3 secretAccessKey still unconfirmed - the one credential not in the co-op's own JSON. Notes that the Vault is Vaultwarden (nothing to sync with Bitwarden) and that all break-glass copies sit with a single custodian.
Backup passphrase confirmed off-box by the operator 2026-10-30 (was the blocker). Outstanding: S3 secretAccessKey (absent from the off-box JSON) and the Vault export. Announcement drafted for #updates with placeholders for the window.
Diffs the restored server against migration-before-state.json: app inventory, env var names, image tags, backup target, portal row counts, DNS, federation, mail ports, site responses. Dry run 2026-10-30: 50 pass / 1 warn / 13 fail, every failure explained by pre-cutover DNS or port 25.
Snapshot records apps/images/env-names/backup config and data fingerprints for a
post-restore diff. Also resolves an open unknown: the off-box JSON carries only
accessKeyId, so the S3 secretAccessKey must be retrieved before the window. The
restic passphrase is fine - it is on the agent host, which is not migrating.
Verified from outside: rules applied on IPv6, nothing but SSH on IPv4.
Blocked=timeout, permitted-but-idle=refused, so the state is readable
before Cloudron is installed. Inbound 25 required (Cloudron docs);
outbound 25 unnecessary with a relay, which this box has.
Mail is enabled inbound for inference.coop (platform mail server, not the
Mail app container, which is only a UI on :3000). Ports verified against
the old box. Whether mailbox CONTENTS are in the Cloudron backup is
unverified and would be silent data loss - now the highest-priority open
question.
Ubuntu 26.04 LTS is supported (docs) and the real version floor is the
PLATFORM version 10.1.5, not the CLI's 9.0.4. Provider firewall is not
Cloudron's job (80/443 dropped, 22 open), rDNS belongs to Infomaniak, and
the setup wizard is where the subscription/restore happen - so install now,
stop before the wizard.
New box: 179.237.86.116 / 2001:1600:18:206::89, 4CPU/12GB/250GB,
Ubuntu 26.04 LTS, one free snapshot slot, reverse DNS unset on both
addresses. IPv6 exists, so the 18 AAAA records are repointed rather
than deleted.
Live zone inventory: 19 A + 18 AAAA records, all TTL 3600 (the lower values
seen on some answers are cache countdowns). The AAAA scope was understated
in the runbook - it is 18 hostnames, not 3. No CAA records, so no CA
restriction to update. The /var/lib/nextcloud host mount resolves to 3 MB of
regenerable thumbnails, so it is no longer an open unknown.
Verified against live DNS, federation and backup state (no privileged commands):
- IPv6: the zone carries AAAA records (2a02:4780:6e:83fd::1, Hostinger) that the plan
never mentioned. Switching A records only leaves IPv6 clients - including other
homeservers and mail senders - on the old box. Repoint or delete; decide pre-flight.
- TTL: the runbook said 760-1642s, which was a decrementing cache countdown, not the
zone TTL (3600s). Propagation is up to an hour, which is why the next item matters.
- "Leave the old box running read-only" was undefined and dangerous: during the TTL
window a still-serving old box silently collects Matrix messages, Gitea pushes, API
spend and mail into a database that gets thrown away. Now step 3: stop the apps after
the final backup; keep the machine powered for rollback only.
- Port 25 inbound was missing from the port list (mail delivery); 8448 noted as
intentionally closed - federation is delegated to 443 via the apex .well-known,
verified live.
- DMARC is p=reject pct=100: post-restore mail misalignment means discarded mail, not
junk. DKIM TXT must be verified against the restored key; test delivery to an external
mailbox, not just that the app accepts the send.
- Post-restore federation check now includes the apex .well-known (served by the
website app), without which federation breaks while matrix.inference.coop looks fine.
- Stale text removed: the once-daily backup schedule IS applied (00 00 23 * * *,
verified), and the backup space was enlarged - newest run healthy at 4.6 GB.
Pre-flight, ordered cutover, post-restore verification, rollback, and the list of what Cloudron's backup does not cover. Written from a live inventory of the server on 2026-10-29; open unknowns are marked rather than guessed.