Rewrite as a Rust, Apache-2.0 workspace
Supersedes the Go + embed-Mox design. The Go tree is removed; its
architecture doc is preserved at docs/archive/ARCHITECTURE-go-embed-mox.md
because its competitive analysis and data model still hold.
Five decisions recorded as ADRs:
0001 Rust, not Go — accepting ~5,500 lines of protocol code that Mox
would have given us free, to get the first permissively licensed
Rust mail server. Costs stated plainly.
0002 Apache-2.0, not MIT or AGPL — patent grant, trademark, CLA-free
contribution. Public on GitHub; Gitea stays as the private fallback.
0003 Stalwart's primitive crates (Apache-2.0/MIT) yes; its AGPL server
crates never. DANE and MTA-STS sit on the AGPL side of that line,
which is why we write our own.
0004 Milestones, reordered: embedded inbound is required at launch.
0005 Oracle Cloud blocks outbound :25, so direct-to-MX is impossible on
the launch host. Split delivery is mandatory, not an on-ramp.
Twelve crates in three tiers. Tier 1 (mail-dane, mail-mta-sts, mail-dsn)
is standalone and publishable — no `dane` or `mta-sts` crate exists on
crates.io at all today.
openmail-relay ships the provider table as data, with SES and Oracle from
the start. Oracle's and Resend's SPF includes are deliberately None: a
guessed include turns the DNS check green against a mechanism the provider
does not honour, and mail still fails SPF silently.
cargo check/test/clippy/fmt all green; unsafe_code is forbidden workspace
wide; cargo-deny enforces the licence policy in CI.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkyvfNJGTshJNE9FtwPLk7
This commit is contained in:
co-authored by
Claude Opus 5
parent
428040d964
commit
36b15ddcaf
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "openmail-junk"
|
||||
description = "Per-inbox Bayesian spam classifier with trainable, persistable state."
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
thiserror.workspace = true
|
||||
serde.workspace = true
|
||||
sha2.workspace = true
|
||||
@@ -0,0 +1,26 @@
|
||||
//! Per-inbox Bayesian spam classification.
|
||||
//!
|
||||
//! Per-inbox, not global: an agent mailbox that only ever receives webhook
|
||||
//! receipts has a radically different prior than a human's. A shared corpus
|
||||
//! makes both worse.
|
||||
//!
|
||||
//! The classifier state must be persistable and versioned — a model that
|
||||
//! cannot be rolled back is a model that can silently start eating real mail.
|
||||
|
||||
/// A score in `[0.0, 1.0]`; higher is more likely junk.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Score(pub f32);
|
||||
|
||||
impl Score {
|
||||
/// Conventional threshold. Deliberately not a global constant used for
|
||||
/// filing decisions — the caller owns policy, this crate owns the number.
|
||||
pub const LIKELY_JUNK: f32 = 0.9;
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("corpus not trained")]
|
||||
Untrained,
|
||||
#[error("state version {found} is not readable by this build (expects {expected})")]
|
||||
VersionMismatch { found: u32, expected: u32 },
|
||||
}
|
||||
Reference in New Issue
Block a user