Feature specs 4-9: observations from INBUXA's live server

This commit is contained in:
2026-09-18 13:42:00 -07:00
parent cd7c1a6d4c
commit a45e0ef8b1
6 changed files with 163 additions and 0 deletions
@@ -460,3 +460,25 @@ Not for observation, but open:
schema. A later addition in the fork's namespace if asked for. schema. A later addition in the fork's namespace if asked for.
9. A "test this model" action for INBUXA Admin. Useful, not required. 9. A "test this model" action for INBUXA Admin. Useful, not required.
10. The name of the fork's limits singleton, with the namespace (SPEC.md §8). 10. The name of the fork's limits singleton, with the namespace (SPEC.md §8).
## Observed
Settled on 2026-09-18 against INBUXA's live Enterprise server (Stalwart
0.16.22), read-only, as a server-level administrator and the throwaway test
account. No upstream code was read.
1. **Configured models** (open question 1). No `x:AiModel` exists and
`x:SpamLlm` is `Disable`. Compat test 22 carries only the tags below.
2. **`LLM_*` tags** (open question 2). Twelve entries, all `Score`, for every
category and confidence: `HIGH` 3.0, `MEDIUM` 2.0, `LOW` 0.5 for
`UNSOLICITED`, `COMMERCIAL` and `HARMFUL`, and 3.0, 2.0, 0.5 for
`LEGITIMATE`. AI-13's clamp (+5.0, 1.0) cuts only the `LEGITIMATE`
`HIGH` and `MEDIUM` scores.
3. **`X-Spam-LLM`** (open question 3). Not observable: the classifier has
never run on INBUXA.
4. **`interactAi`** (open question 5). Ordinary accounts hold it: the test
account's effective permissions include it, through the default `User`
role. This repository's defaults match what upstream ships. The public
permissions table is out of date.
Questions 4, 6 and 7 need a model configured, so they stay open.
@@ -378,3 +378,41 @@ the operator's approval first, as with the other features' probes.
8. **INBUXA Admin's logo.** Upstream documents that its web interface picks its 8. **INBUXA Admin's logo.** Upstream documents that its web interface picks its
logo by request hostname. Check in `inbuxa-admin` (an ordinary AGPL fork, logo by request hostname. Check in `inbuxa-admin` (an ordinary AGPL fork,
SPEC.md §5) whether it calls `/logo`, so it keeps working against BT-5. SPEC.md §5) whether it calls `/logo`, so it keeps working against BT-5.
## Observed
Settled on 2026-09-18 against INBUXA's live Enterprise server (Stalwart
0.16.22), read-only, as a server-level administrator and the throwaway test
account. No upstream code was read.
1. **What INBUXA holds** (open question 1). `x:Enterprise.logoUrl`, all three
templates and every tenant logo are null. Six of nine domains had a logo:
four held the same `https:` URL, pointing at a retired website that now
answers with an HTML page, and two held the literal string `admin`. With
the operator's approval, the four URL logos were removed the same day (a
write). The two `admin` values remain. BT-4 skips them as unusable, and
upstream already answers `404` for those domains.
2. **Upstream fetches URL logos itself** (open questions 2 and 3). While the
URL logos were set, `GET /logo` returned `200 text/html`: the retired
site's page, served from the mail server's own origin with no
`X-Content-Type-Options` and no CSP. So upstream fetches the URL on the
server side and passes on whatever comes back, content type included.
BT-7 (never fetch) and BT-8 (sandbox what is served) are deliberate
differences, and this is why.
3. **`/logo` shape.** `/logo` with no parameter used the request's host, and
`?domain=` selected a domain. `/logo/<domain>` answered the same as
`/logo`, so the path segment is ignored. An unknown domain, a domain with
an unusable value, and (after the removal) every domain answered `404`
with `application/problem+json`. No caching headers were sent. BT-5 keeps
`/logo?domain=` and `404`, and adds its own headers.
4. **Reload** (open question 4). Removing the logos took effect on the very
next `/logo` request, with no settings reload.
5. **RSVP page headers** (open question 6). `GET /calendar/rsvp` sends only
`Content-Type: text/html; charset=utf-8` and
`Cache-Control: no-store, no-cache, must-revalidate`: no CSP and no
`Referrer-Policy`. BT-21's headers are a difference.
6. **The stored `fromName`** (open question 7). INBUXA stores
`INBUXA Calendar` as a value already. Nothing to change at cutover.
Not settled: open question 5 (invalid templates on write) needs a write, and
question 8 is a check in `inbuxa-admin`, not on the server.
+52
View File
@@ -508,3 +508,55 @@ these needs a write.
`sysMetric*` or live permission, and whether a tenant view of its own `sysMetric*` or live permission, and whether a tenant view of its own
domains' traces is wanted (MON-31). domains' traces is wanted (MON-31).
12. Size of INBUXA's `o` and `x` subspaces, to check MON-19's estimate. 12. Size of INBUXA's `o` and `x` subspaces, to check MON-19's estimate.
## Observed
Settled on 2026-09-18 against INBUXA's live Enterprise server (Stalwart
0.16.22), read-only, as a server-level administrator and the throwaway test
account. No upstream code was read.
1. **Settings** (open question 6). `holdTracesFor` 30 days, `holdMetricsFor`
90 days, `metricsCollectionInterval` hourly at minute 0, `indexTelemetry`
on with `eventType`, `queueId` and `keywords` indexed. Tracing and metrics
stores are both `Default`. No `x:Alert` exists (open question 10).
2. **Traces** (open questions 1 to 3). 395 stored, the oldest exactly one day
old, although retention is 30 days. Why is unexplained: the history may
have been cleared a day earlier. Worth checking before cutover, since
metrics go back 24 days. Each trace is one message's journey: 4 to 44
events (10 on average), mostly delivery, queue, SMTP and authentication
results. Every stored inbound SMTP session reached `MAIL FROM` (82 of 82),
so connection-only sessions aren't stored, which matches MON-11. Events
carry `event`, `timestamp` and typed `keyValues`. There is no level field
and no raw I/O (MON-12). `to` is a string. No stored trace had more than
one recipient, so MON-14's multi-recipient case wasn't seen.
3. **Trace queries** (open question 8). `timestampAfter` and `after` are
refused with `unsupportedFilter`. The accepted filters weren't listed
further.
4. **Metrics** (open questions 4 and 5). 10,755 samples over 597 hourly ticks
since 2026-08-25, with no gaps: 5,598 `Counter`, 4,025 `Histogram` and
1,132 `Gauge`. Histograms are stored for eight metrics: delivery attempt
and total time, DNS lookup time, ingest time, and blob and data store
read and write times. Gauges are `queue.count` and `server.memory`. Only
four metrics appear in every tick, so a counter with nothing to report
isn't stored for that tick. Counter values read as per-interval
increases, not running totals. `queue.count` moved between 20 and 30 over
the last eight hours, so it tracks the queue. Whether it's right straight
after a restart wasn't tested.
5. **Live telemetry** (open question 9).
- `GET /api/token/tracing` and `/api/token/metrics` answer `200` with the
token as a bare 90-byte body and no content type.
- The streams are `GET /api/live/tracing` and `/api/live/metrics`, with
the token in the `token` query parameter. The same token in an
`Authorization: Bearer` header is refused (`401`).
- Both answer `text/event-stream`. Tracing sends
`event: ping` with `data: {"interval": 30000}` while idle. Metrics sends
`event: metrics` with a JSON array of `{metric, @type, count}` holding
totals since the server started.
- `/api/telemetry/traces/live` and `/api/telemetry/metrics/live` don't
exist upstream (`404`). The aliases in "Interfaces" are the fork's own.
6. **Tenant administrators** (open question 11). The default
`Tenant Administrator` role holds 50 permissions and none for telemetry.
The `System Administrator` role holds all of them, live ones included.
Not settled: open question 7 (null retention) needs a write, and question
12 (subspace sizes) needs access to the store itself.
@@ -534,3 +534,19 @@ data.
`auth/mod.rs` and 2 from `cache/principals.rs`. This spec doesn't say what `auth/mod.rs` and 2 from `cache/principals.rs`. This spec doesn't say what
they did and doesn't need to. The rules they may have enforced (quota, they did and doesn't need to. The rules they may have enforced (quota,
SCIM authority, tenant checks) are specified here from other sources. SCIM authority, tenant checks) are specified here from other sources.
## Observed
Settled on 2026-09-18 against INBUXA's live Enterprise server (Stalwart
0.16.22), read-only, as a server-level administrator and the throwaway test
account. No upstream code was read.
1. **The cutover check passes.** INBUXA has no external directory
(`x:Directory` is empty), `x:Authentication.directoryId` is null, and all
nine domains have `directoryId` null. The fork ignoring `directoryId` today
changes nothing for INBUXA.
2. **Tenant domains** (open question 7). INBUXA has no tenants, so no tenant
domain relies on a default. DIR-23 stands as written.
Open question 2 (the `requireAudience` default) needs a directory to be
created, so it stays open.
+10
View File
@@ -521,3 +521,13 @@ and PostgreSQL), to show members of different kinds work together (ST-16).
- **`LookupStore::RedisSentinel` has no build arm** in `build/lookup.rs`. It's - **`LookupStore::RedisSentinel` has no build arm** in `build/lookup.rs`. It's
outside this feature. Check a stock build to see whether a sentinel lookup outside this feature. Check a stock build to see whether a sentinel lookup
store works at all. store works at all.
## Observed
Settled on 2026-09-18 against INBUXA's live Enterprise server (Stalwart
0.16.22), read-only, as a server-level administrator and the throwaway test
account. No upstream code was read.
INBUXA runs a single RocksDB data store, with the default blob, in-memory
and search stores. No replicas, and no sharded stores. Nothing to carry
over, and nothing here affects its cutover.
+25
View File
@@ -839,3 +839,28 @@ removed suites (`auth`, `bulk`, `discovery`, `groups`, `limits`, `query`,
14. **`SCIM_DOMAIN` and `HTTP_PORT` in the test helpers.** The surviving 14. **`SCIM_DOMAIN` and `HTTP_PORT` in the test helpers.** The surviving
tests imply `scim.example.com` and port 8899 (from addresses and URLs in tests imply `scim.example.com` and port 8899 (from addresses and URLs in
them). Confirm when rebuilding `mod.rs`. them). Confirm when rebuilding `mod.rs`.
## Observed
Settled on 2026-09-18 against INBUXA's live Enterprise server (Stalwart
0.16.22), read-only, as a server-level administrator and the throwaway test
account. No upstream code was read.
1. **SCIM isn't used** (open question 2). No domain has
`allowScimProvisioning` on, and none of the 8 accounts has an
`externalId`. Cutover has nothing to carry.
2. **Discovery is anonymous.** `/scim/v2/ServiceProviderConfig`, `/Schemas`
and `/ResourceTypes` answer `200` with no credentials, as
`application/scim+json`. `/scim/v2/Users` without credentials answers
`401` with `WWW-Authenticate: Bearer realm="Stalwart SCIM"`.
3. **What upstream advertises:** PATCH, Bulk (1,000 operations, 1 MiB),
filtering (`maxResults` 200), sorting and ETags supported; password change
not supported; pagination by index and by cursor, index by default, page
size 100, at most 200. One authentication scheme, `oauthbearertoken`,
described as an API key presented as a bearer token. `interopProfileConformant`
is `false`. `/ResourceTypes` lists `User` and `Group` with no schema
extensions, which matches this spec's decision. `documentationUri` points at
upstream's documentation. The fork points it at its own.
The per-account behaviors (open questions 3 and 5 to 9) need an API key and
a SCIM-enabled domain, so they stay open.