Commit Graph
57 Commits
Author SHA1 Message Date
jcoffey-dev adfa22c817 Specs: the nine ignored suites re-run, and the STORE each one needs
Re-ran all nine on containers removed beforehand, one suite at a time.
All nine pass. mysql_replica_position_tests failed the first time on test
18's lag assertion, with the pair half a minute old and still catching up,
and passed on a second run against the same containers; it fails before
the point where it changes the replica's settings, so it leaves nothing to
restore. Noted both, with each suite's time.

The table's STORE column said "default" for five suites, which reads as
"leave it unset". The harness has no default: it panics with "Missing or
invalid store type" before the suite starts. They run on RocksDb, as the
regression does, so the column now names it.
2026-09-19 16:54:27 -07:00
jcoffey-dev 309835be1d ACME: post a challenge once, then poll the authorization
The renewal loop re-posted the challenge every time it polled and found the
authorization still pending. RFC 8555 section 7.5.1 has the client post a
challenge once to say it is ready and then poll; a server that has already
moved the challenge to "processing" refuses a second post, and pebble
answers 400 malformed, which failed the whole renewal.

Verified against pebble: the 400s are gone and the client polls. The suite
still can't finish on this machine, because pebble never reaches the test
server to validate the challenge; that path is the environment, and the
runbook now says so.
2026-09-19 16:28:15 -07:00
jcoffey-dev ce12659138 Specs: what a plain regression leaves failing, and why none of it is ours 2026-09-19 16:15:09 -07:00
jcoffey-dev e492b7875a Specs: how to run the suites a plain regression doesn't reach
`cargo test -p tests` runs none of the fork's own feature suites except
what's inside `system_tests`: SCIM, per-domain directories, the sharded
stores and the four replica suites are all ignored, each needing a
container, a STORE the harness only builds on request, or both. The
runbook lists what each one needs, and the container-reuse trap.
2026-09-19 16:14:44 -07:00
jcoffey-dev 5cc28784df SCIM: turning a domain's SCIM authority off takes effect at once (SCIM-60)
`allowScimProvisioning` is cached with the domain as DOMAIN_FLAG_SCIM, but
it wasn't among the fields whose change drops the cached entry, so flipping
the flag changed nothing until something else evicted the domain. SCIM-60
says the change takes effect without a restart.

Found by the two acceptance checks that were written but never called from
the driver, so neither had ever run: `authority` (SCIM-58 to SCIM-60,
through `synchronize_account` itself) and `rate_limits` (SCIM-14). Both are
wired in now, and `scim_tests` passes with them.
2026-09-19 16:14:38 -07:00
jcoffey-dev cee4fd6bc6 Specs: how to run the eight compat tests on a copy of INBUXA's data, and the statuses of tests 10, 11, 17 to 19
docs/spec/compat-tests.md lists each compat test, what it needs, what it
checks and what a failure means, and says plainly that they run against a
copy only, since the monitoring one purges the history it reads. The
scale-out and per-domain statuses record the acceptance tests that now
run.
2026-09-19 15:11:04 -07:00
jcoffey-dev 216bb5b711 Scale-out storage: implementation status of read replicas (ST-5 to ST-15)
Where the routing lives and what a read scope covers, which acceptance
tests the container suite runs, what isn't exercised (two nodes, the
primary stopped, and the MySQL paths, which are built but unrun), what
was settled from the code, and the known limits.
2026-09-19 14:21:54 -07:00
jcoffey-dev a635b490ec Per-domain directories: implementation status and the compat check (DIR-1 to DIR-32, test 20)
per_domain_directory_compat, ignored, checks a copy of INBUXA's data has
no directory, no server default and no domain with its own directory, as
observed. The status names where the rules live, which suite covers each
acceptance test and how far, what was settled from the code, and the
known limits. SCIM's status notes its test 5 now passes.
2026-09-19 10:51:41 -07:00
jcoffey-dev b080fa0736 Scale-out storage: implementation status of the sharded stores (ST-1 to ST-4, ST-16 to ST-30)
Where the code lives, which suite covers each acceptance test, what isn't
exercised, what was settled from the code, and that read-replica routing
(ST-5 to ST-15) waits for per-domain directories.
2026-09-19 10:21:30 -07:00
jcoffey-dev e7efa91cbc Scale-out storage decision: sharded stores first, read-replica routing after per-domain directories, with configured replicas reported at startup meanwhile (ST-2) 2026-09-19 10:01:34 -07:00
jcoffey-dev 00b2eb6f4b SCIM: implementation status (SCIM-1 to SCIM-61)
Where each part lives, which suite covers each acceptance test, test 5
deferred to per-domain directories and test 31 unrun until a copy of
INBUXA's data, what was settled from the code, and the known limits.
2026-09-19 09:59:53 -07:00
jcoffey-dev 2d3f8251c5 SCIM decision: a groups value equal to the current membership in a POST or PUT body is accepted and ignored, since Okta sends groups: [] on create (SCIM-28) 2026-09-19 09:04:25 -07:00
jcoffey-dev 4b389b6b4e SCIM decisions: build before per-domain directories, with the OIDC authority test deferred; build to the spec without the profile drafts or vendor docs (SCIM-61, open questions 12 and 13) 2026-09-19 08:56:53 -07:00
jcoffey-dev f107443b29 Monitoring: implementation status, and the compat test for INBUXA's data (test 26)
monitoring_compat, ignored, checks the observed settings against a copy
of INBUXA's data, reads the old history without an error, and purges it.
The status names where each part lives, which suite covers each
acceptance test, the tests not exercised, and the known limits.
2026-09-19 08:54:21 -07:00
jcoffey-dev 143a10fcf0 Monitoring decision: a live token may be reused within its 60 seconds, since INBUXA Admin reconnects with the same token (MON-23) 2026-09-19 08:34:03 -07:00
jcoffey-dev 715f219528 Monitoring: correct the storage decision; history uses the AGPL telemetry key classes with the registry's encoding, and INBUXA's old history can't be read 2026-09-19 08:12:11 -07:00
jcoffey-dev 3ea242f7fb Monitoring decisions before implementation: live streams and stored histograms match INBUXA Admin, and history is stored as x:Trace and x:Metric registry items (MON-4, MON-20, MON-22, MON-23) 2026-09-19 08:10:27 -07:00
jcoffey-dev 88090b3ea6 AI spam classification: calibration results for commercially usable models; Qwen3 4B Instruct 2507 recommended, 4 vCPU minimum 2026-09-19 07:42:12 -07:00
jcoffey-dev 554cc4fcd2 AI spam classification decisions from calibration: maxContentBytes 2048 and spamMaxAdded 2.0 by default, for low-end CPU-only instances 2026-09-19 06:19:38 -07:00
jcoffey-dev 9490fc4677 AI spam classification: the model's opinion as one bounded spam signal, and the llm_prompt Sieve function (AI-1 to AI-28)
The classifier sends only the subject and text, between unforgeable markers
after the operator's prompt, to an OpenAI-compatible endpoint the operator
configured; nothing is preset. Its answer maps to an LLM_ tag whose score is
clamped (+5.0, -1.0 by default) and can never discard or reject on its own;
X-Spam-LLM is sanitized, encoded and folded, and a planted one is removed.
Failures, timeouts past the ceiling, a full slot or a paused model leave
mail flowing untagged. llm_prompt answers trusted scripts, and accounts
holding interactAi within an hourly limit. Redirects aren't followed and no
content or secret is logged. The limits live in inbuxa:AiLimits.
Acceptance tests 1 and 3 to 21; test 2 as the re-enabled shared llm case,
whose setup no longer waits on a rules file from a developer's own path;
test 22 written as the ignored ai_compat.
2026-09-19 00:41:00 -07:00
jcoffey-dev cba48cf03b AI spam classification decision before implementation: the limits singleton is inbuxa:AiLimits under urn:inbuxa:jmap (open question 10) 2026-09-18 23:50:33 -07:00
jcoffey-dev 0bc6b03dcd Branding and templates: per-domain, tenant and server logos, /logo, operator calendar email templates and RSVP page (BT-1 to BT-26)
Logos resolve domain, then tenant, then server-wide, then the built-in, with
subdomains finding their domain. GET /logo serves a data-URL image, redirects
to a URL logo without fetching it, sandboxes SVG, and answers 404 when no
custom logo applies. Emails embed the first PNG, JPEG or GIF logo. Logo and
template writes are checked; stored templates are read at send time, always
escaped, and fall back to the built-in with a build warning when they don't
parse. The RSVP page is served byte for byte with a CSP and no-referrer. The
sign-in and RSVP pages load the logo through an image element. MT-22's
session logo follows the chain to the server-wide logo.
Acceptance tests 1 to 17; test 18 written as the ignored branding_compat.
2026-09-18 22:27:19 -07:00
jcoffey-dev ecbdfd533b Undelete: deleted accounts are kept for their period, hold their addresses, and are restored or destroyed through inbuxa:DeletedAccount (UD-15 to UD-17a)
With archiveDeletedAccountsFor set, a destroyed account's record is kept in
the fork subspace with its id, its DestroyAccount task is due at the end of
the period, and its shares are suspended both ways. Its addresses can't be
taken by new accounts, aliases, lists or masks. inbuxa:DeletedAccount/get
lists kept accounts to server and tenant administrators; /set restores one
with a new password (same id, task cancelled, shares reinstated) or destroys
it now. The destroy task also clears undelete's own records.
Acceptance test 14; test 16 written as the ignored undelete_compat.
2026-09-18 21:35:48 -07:00
jcoffey-dev 0a2c29e8fd Undelete decision before implementation: a kept account's shares are suspended both ways and reinstated on restore (UD-17a) 2026-09-18 21:19:09 -07:00
jcoffey-dev a1ce14b76b Undelete decisions before implementation: restore data, kept accounts, the restore API (UD-4, UD-15a, UD-17) 2026-09-18 19:49:34 -07:00
jcoffey-dev ac2232c98d Multi-tenancy decision before implementation: impersonate has no effect inside a tenant (MT-1, MT-15) 2026-09-18 18:49:10 -07:00
jcoffey-dev 4a9aa9c548 Masked email: rewrite to the owner at RCPT TO, create responses carry the address, admins query all masks (ME-4, ME-9, ME-13, ME-19)
Found by running system_tests, which masked email no longer stops:
- rcpt_resolve rewrites a live mask to its owner's address, so
  Delivered-To names the account; delivery recognizes the mask from the
  original recipient when it belongs to that account.
- x:MaskedEmail/set create responses carry the server-set email.
- x:MaskedEmail/query returns every mask to a server-level impersonate
  holder, and filters on accountId.
- The refusal for an unlinked emailDomain uses upstream's wording.
- The shared delivery test checks the fork's address format (ME-13).
- The masked email test's tenant domain uses manual DKIM, so its cleanup
  leaves nothing behind.
2026-09-18 18:29:19 -07:00
jcoffey-dev 31b8ca8ed5 Masked email built: implementation status, deferral and known limits (ME-1 to ME-19) 2026-09-18 16:40:14 -07:00
jcoffey-dev 7ef2cdc273 Masked email: the fork's subspace is _, not X, which SQL backends fold into upstream's x 2026-09-18 16:10:50 -07:00
jcoffey-dev edfb357cb1 Masked email decisions before implementation: storage, start state, rate config, who administers (ME-7a, ME-15, ME-19) 2026-09-18 16:09:57 -07:00
jcoffey-dev 2e43f5fc0d Multi-tenancy built: implementation status, deferrals and known limits (MT-1 to MT-23) 2026-09-18 15:38:29 -07:00
jcoffey-dev 2cbecac415 Multi-tenancy decision before implementation: generated DKIM keys count against maxDkimKeys (MT-17) 2026-09-18 15:28:24 -07:00
jcoffey-dev b6b345a129 Multi-tenancy decision before implementation: sharing grants across tenants (MT-3) 2026-09-18 15:17:03 -07:00
jcoffey-dev d0d4ac4317 Open decisions: the Enterprise License text stays until the headers are cleaned up 2026-09-18 14:52:45 -07:00
jcoffey-dev a1254cd3c4 Multi-tenancy decision before implementation: what moves with a domain (MT-8, MT-17) 2026-09-18 14:50:57 -07:00
jcoffey-dev edc6a8ccbd Multi-tenancy decisions before implementation: MT-3, MT-7, MT-12, MT-19a, MT-22
Test 3 expects invalidForeignKey, as MT-3 says. A tenant admin reads its own
tenant through sysTenantGet and sysTenantQuery, added to the default Tenant
Administrator role for new installs only. The server adds nothing for MT-7's
dashboard list. The MT-19a submission warning is deferred. MT-22's logo is
the urn:inbuxa:jmap account capability's logo field (contract C-1).
2026-09-18 14:48:22 -07:00
jcoffey-dev cf59d5183a Branding: INBUXA's last two logo values removed 2026-09-18 14:45:38 -07:00
jcoffey-dev 8c1879e853 Cross-origin requests only from the front ends' origins (contract C-14) 2026-09-18 13:44:55 -07:00
jcoffey-dev a45e0ef8b1 Feature specs 4-9: observations from INBUXA's live server 2026-09-18 13:42:00 -07:00
jcoffey-dev cd7c1a6d4c Feature specs 4-9: branding and templates, AI spam classification, monitoring, SCIM, scale-out storage, per-domain directories 2026-09-18 13:18:39 -07:00
jcoffey-dev 05d220ae4f No web interface on the mail host: first boot installs and downloads none (SPEC §5.3) 2026-09-18 13:10:32 -07:00
jcoffey-dev c2be7e2956 Multi-tenancy implementation hand-off brief 2026-09-18 13:06:47 -07:00
jcoffey-dev d6fc4600cd OAuth: registration required by default; first-party clients registered on every start (contract C-5, C-6) 2026-09-18 13:04:20 -07:00
jcoffey-dev f82f15d863 Contract: note the browser sign-in check after the OAuth fix 2026-09-18 12:53:59 -07:00
jcoffey-dev ad3322183a Contract: record the OAuth registration fix applied on production 2026-09-18 12:40:33 -07:00
jcoffey-dev 2e2eb76301 Contract: C-7 matches C-5 (third-party OAuth apps are admin-registered unless open registration is chosen) 2026-09-18 12:31:18 -07:00
jcoffey-dev 559bb3d1a6 Contract: INBUXA production OAuth findings; C-5 also turns anonymous client registration off 2026-09-18 12:31:07 -07:00
jcoffey-dev 0ed540f43a Contract spec: inbuxa-server, ihasmail-inbuxa and INBUXA Admin
Discovery and a contract version in the session; front ends configured once
(x:FrontEnds); OAuth with required registration, first-party clients,
server-hosted sign-in and consent for everything else; per-grant revocation;
cross-origin limited to the front ends; an admin lane by scope; push
unchanged. Records what upstream does today, including that it accepts any
client and redirect URI by default, and the phishing that allows.
2026-09-18 12:19:47 -07:00
jcoffey-dev b2de803680 Spec: public ihasmail stays Stalwart-facing; INBUXA work goes to an INBUXA fork of ihasmail 2026-09-18 12:09:12 -07:00
jcoffey-dev faedf7a1da Versioning: INBUXA's own dated version, with the Stalwart base shown
inbuxa --version, the banner, startup events, OpenTelemetry and the JMAP
implementation string read "2026.9.18 (Stalwart 0.16.22)". The base comes
from Cargo, which keeps following upstream so version bumps merge cleanly.
Received headers and IMAP ID carry the INBUXA version.
2026-09-18 12:08:41 -07:00