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.
This commit is contained in:
2026-09-19 00:41:00 -07:00
parent cba48cf03b
commit 9490fc4677
39 changed files with 2945 additions and 28 deletions
+1 -1
View File
@@ -279,7 +279,7 @@ is written.
| 2 | Masked email | Per-sender disposable addresses that deliver to the account | Existing addresses must keep delivering (§3.4). Built 2026-09-18 in `crates/features`; status in `features/masked-email.md`. |
| 3 | Undelete | Deleted mail held for a set period and restorable | Existing archived items must stay restorable. Built 2026-09-18 in `crates/features`; status in `features/undelete.md`. |
| 4 | Branding and templates | Operator logo, and the text of calendar alarm and invitation emails | INBUXA's branding is the default. Built 2026-09-18 in `crates/features`; status in `features/branding-and-templates.md`. |
| 5 | AI spam classification | An optional model's opinion as one spam signal, and a Sieve function that asks a model | Local and auditable model only: no hosted API by default. Spec: `features/ai-spam-classification.md`. |
| 5 | AI spam classification | An optional model's opinion as one spam signal, and a Sieve function that asks a model | Local and auditable model only: no hosted API by default. Built 2026-09-19 in `crates/features`; status in `features/ai-spam-classification.md`. |
| 6 | Monitoring history, live tracing, alerts | Stored metrics and traces, a live trace view, and threshold alerts | ihasmail's dashboard shows them. Spec: `features/monitoring.md`. |
| 7 | SCIM 2.0 provisioning | Accounts and groups managed by an identity provider | From RFC 7643 and RFC 7644. The largest piece. Spec: `features/scim.md`. |
| 8 | Scale-out storage | SQL read replicas; sharded blob and in-memory stores | For large installs only. Spec: `features/scale-out-storage.md`. |
@@ -470,6 +470,37 @@ Not for observation, but open:
10. ~~The name of the fork's limits singleton.~~ Settled 2026-09-18:
`inbuxa:AiLimits` (see "Added by inbuxa-server").
## Implementation status
Built 2026-09-19 from this spec, clean-room, under the multi-tenancy hand-off
brief's rules. The rules live in `crates/features` (`inbuxa-features`, module
`ai`); the model call in `crates/common/src/enterprise/llm.rs`, at the path
the shared tests name; the classifier step in
`crates/spam-filter/src/analysis/llm.rs`; `inbuxa:AiLimits` in
`crates/jmap/src/inbuxa/ai_limits.rs`; upstream files carry hooks marked
`inbuxa:`. Acceptance tests 1 and 3 to 21 pass as `tests/src/system/ai.rs`,
against a stub model on loopback. Test 2 passes as the shared `llm` case in
`tests/src/smtp/inbound/antispam.rs`, re-enabled with its mock reading the
last message.
- **AI-1 to AI-28:** built.
- **Test 22 (compat)** is written as `ai_compat`, ignored, and unrun until a
copy of INBUXA's data is provided. It checks the twelve `LLM_*` tags and
scores in observed 2.
- **No model is calibrated.** The code speaks the OpenAI-compatible API and
has only met stubs. How well a given local model follows the default
prompt, and how accurate it is, isn't measured yet.
- **Known limits, not requirements of this spec:**
- The call limits (AI-10, AI-11, AI-24) are per server node, as the spec
says; a cluster of n nodes can have n times `maxConcurrentCalls` in
flight.
- The shared antispam suite's setup reads spam rules from a developer's own
checkout path. It now carries on when that file is missing (a test-only
change marked `inbuxa:`), so its `llm` case runs; its other cases still
need the rules file.
- Test 21's warning count is checked only when `registry.build-warning` is
a metric of interest; the warning itself is always logged.
## Observed
Settled on 2026-09-18 against INBUXA's live Enterprise server (Stalwart