Rename the identifiers that carried the upstream name
ci / fork-checks (pull_request) Successful in 16s
ci / build (pull_request) Successful in 7m53s

Everything clients, users and operators meet now carries the fork's name,
with no aliases (SPEC.md §2.4, changed here from "protocol identifiers
stay"):

- JMAP: upstream's registry capability is urn:inbuxa:jmap:registry, beside
  the fork's own urn:inbuxa:jmap.
- WebDAV lock and sync tokens are urn:inbuxa:dav*; clients resync once.
- Sieve: vnd.inbuxa.while and vnd.inbuxa.expressions. sieve-rs spells these
  into its compiler, so it's vendored (vendor/sieve-rs, 0.7.3) and patched in;
  a unit test fails if Cargo.lock ever moves past the vendored copy. The
  trusted runtime now names itself too, rather than answering sieve-rs's
  default.
- The web interface's OAuth client is inbuxa-webui. On every start the old
  stalwart-webui client is removed and any application naming it is moved
  over.
- The spam filter's blobs are INBUXA_SPAM_*; every start moves any left
  under the old keys, so a trained model survives.
- SQL stores and log files default to inbuxa, in the code and in the
  schema served to the admin (checksum regenerated).
- Settings are INBUXA_* only. A STALWART_* variable that's set where its
  INBUXA_* one isn't stops the server at startup, naming it.
- The version-upgrade messages link docs.inbuxa.org's migration page, and
  the OpenAPI description, smtp crate metadata and web-push test fixtures
  lose the name.

Kept on purpose, allowlisted with reasons: the OAuth key-derivation
contexts (renaming them would end every session and invalidate every
sealed client id) and the hashed application prefix.

Also fixes a latent start-up failure: ensure_client updated an existing
first-party client with a revision of 0, which the registry's assertion
never matches, so adding a redirect URI or changing the webmail secret
failed start-up. And the principal session test now expects
legacyProtocols (C-1, added 2026-09-21), which it had missed.

Tested: the server builds without warnings; common's 106 unit tests,
including the vendoring check; a new integration test for the two
start-up migrations; and the webdav, jmap, imap and SMTP Sieve suites.
This commit is contained in:
2026-09-22 19:33:02 -07:00
parent 4799d191a0
commit cc6f1eb298
129 changed files with 20504 additions and 168 deletions
+37 -19
View File
@@ -212,14 +212,29 @@ depends on `store` and can't be called from it (`features/scale-out-storage.md`)
- Factual statements are allowed and required: "a fork of Stalwart",
"compatible with Stalwart 0.16 data". Upstream copyright notices stay on
every file they cover.
- Protocol identifiers stay as upstream has them, for example the JMAP
capability `urn:stalwart:jmap` and the `x:` object names. They're
interoperability, not branding, and renaming them breaks every existing
client. Anything the fork adds uses its own namespace (open: which one).
- **Version and build metadata are exempt from the first bullet** (added
2026-09-19). `inbuxa --version`, the startup banner and events,
OpenTelemetry's `service.version`, the JMAP `implementation` string, release
notes and the strip report all name the Stalwart base, as §2.6 requires.
- **Identifiers people meet carry the fork's name** (changed 2026-09-22;
this bullet used to keep upstream's). Upstream's JMAP capability for the
registry (`x:`) objects is `urn:inbuxa:jmap:registry`, beside the fork's
own `urn:inbuxa:jmap` (contract C-1); WebDAV lock and sync tokens are
`urn:inbuxa:dav*`, the Sieve extensions are `vnd.inbuxa.while` and `vnd.inbuxa.expressions`, the
web interface's OAuth client is `inbuxa-webui`, the spam filter's blobs are
`INBUXA_SPAM_*`, and the SQL stores and log files default to `inbuxa`.
There are no aliases: the old names stop working, so front ends move with
the server, Sieve scripts that `require` the old extensions are edited,
DAV clients resync once, and anyone signed in to the web interface signs in
again. Pre-rename data is carried over where it would otherwise be lost
(the spam model, the web interface's client). The `x:` object names are
unchanged, having no name in them. `tools/fork/renames.py` holds the list,
and the strip applies it to every import (§2.2).
- **Identifiers nobody sees keep upstream's spelling:** the OAuth
key-derivation contexts, whose renaming would invalidate every issued token
and client id, and the application blob prefix, which is hashed before use.
`tools/fork/name-allowlist.txt` lists them with their reasons.
- **Version and build metadata give the base without the name** (added
2026-09-19, narrowed 2026-09-22). `inbuxa --version`, the startup banner and
events, OpenTelemetry's `service.version` and the JMAP `implementation`
string say `2026.9.23 (upstream 0.16.23)`, as §2.6 requires; release notes
and the strip report may name the Stalwart base.
That is a factual statement about what was compiled, not a name the product
calls itself, and the two bullets don't conflict: the first governs
identity, this one governs provenance. A reader who takes "no Stalwart in
@@ -243,15 +258,18 @@ Done 2026-09-18:
- The package and binary are `inbuxa` (`cargo build -p inbuxa`). The binary's
help, banner and every protocol greeting say INBUXA (the branding module,
`types::brand!()`).
- Settings come from `INBUXA_*` environment variables. Each still falls back
to its `STALWART_*` name, with a startup warning to rename it
(`types::branding::env_var`). That covers all nine the server reads:
`HOSTNAME`, `RECOVERY_MODE`, `RECOVERY_ADMIN`, `RECOVERY_MODE_PORT`,
`RECOVERY_MODE_LOG_LEVEL`, `ROLE`, `PUSH_SHARD`, `PUBLIC_URL`, `HTTPS_PORT`.
- **Not renamed, on purpose:** `STALWART_APP_` and the two `STALWART_SPAM_...`
names. They look like environment variables, but they're keys inside the
data store, so renaming them would orphan existing installed apps and
spam-classifier models.
- Settings come from `INBUXA_*` environment variables
(`types::branding::env_var`): `HOSTNAME`, `RECOVERY_MODE`, `RECOVERY_ADMIN`,
`RECOVERY_MODE_PORT`, `RECOVERY_MODE_LOG_LEVEL`, `ROLE`, `PUSH_SHARD`,
`PUBLIC_URL`, `HTTPS_PORT`, and the front-end variables of contract C-6.
Until 2026-09-22 each fell back to its `STALWART_*` name with a warning.
Now a `STALWART_*` name that's set where its `INBUXA_*` one isn't stops the
server at startup, naming the variable to rename, so an install moved over
from upstream never runs on settings it silently dropped.
- The spam filter's blobs moved from `STALWART_SPAM_*` to `INBUXA_SPAM_*` on
2026-09-22; every start moves any left under the old keys
(`migration::try_migrate`), so no trained model is orphaned.
`STALWART_APP_` stays: it's hashed into a blob key and never seen.
- New installs default to `/var/lib/inbuxa` for data and `/var/log/inbuxa` for
logs. Existing installs keep the paths their configuration names, so no data
moves.
@@ -421,8 +439,8 @@ Versioned, and advertised in the JMAP session so either side can check it.
address or network, so an admin credential is useless from anywhere else.
- **Push.** Unchanged: JMAP push with VAPID, as ihasmail uses today.
- **INBUXA Admin's client.** INBUXA Admin signs in by OAuth (authorization
code with PKCE) as upstream's `webui` does, as client `stalwart-webui` for
now. The fork registers a first-party `inbuxa-admin` client with the
code with PKCE) as upstream's `webui` does, as client `inbuxa-webui`
(upstream's `stalwart-webui` until 2026-09-22) when the server serves it. The fork registers a first-party `inbuxa-admin` client with the
admin's own redirect URIs, and the admin switches to it (its
`<meta name="oauth-client-id">`).
- **Cross-origin access.** Verified 2026-09-18 against a separate