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.
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).
## 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
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.
## 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
domains' traces is wanted (MON-31).
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
they did and doesn't need to. The rules they may have enforced (quota,
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
outside this feature. Check a stock build to see whether a sentinel lookup
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
tests imply `scim.example.com` and port 8899 (from addresses and URLs in
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.