diff --git a/docs/spec/features/ai-spam-classification.md b/docs/spec/features/ai-spam-classification.md index 7b0e37f..abe5b53 100644 --- a/docs/spec/features/ai-spam-classification.md +++ b/docs/spec/features/ai-spam-classification.md @@ -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. diff --git a/docs/spec/features/branding-and-templates.md b/docs/spec/features/branding-and-templates.md index 60ed572..9cda2f3 100644 --- a/docs/spec/features/branding-and-templates.md +++ b/docs/spec/features/branding-and-templates.md @@ -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/` 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. diff --git a/docs/spec/features/monitoring.md b/docs/spec/features/monitoring.md index 3975b3e..3b69f87 100644 --- a/docs/spec/features/monitoring.md +++ b/docs/spec/features/monitoring.md @@ -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. diff --git a/docs/spec/features/per-domain-directories.md b/docs/spec/features/per-domain-directories.md index aa14ff7..5fde141 100644 --- a/docs/spec/features/per-domain-directories.md +++ b/docs/spec/features/per-domain-directories.md @@ -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. diff --git a/docs/spec/features/scale-out-storage.md b/docs/spec/features/scale-out-storage.md index e01f435..e5624b0 100644 --- a/docs/spec/features/scale-out-storage.md +++ b/docs/spec/features/scale-out-storage.md @@ -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. diff --git a/docs/spec/features/scim.md b/docs/spec/features/scim.md index d1b0e8a..91b162a 100644 --- a/docs/spec/features/scim.md +++ b/docs/spec/features/scim.md @@ -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.