domain capa

domain-sender-reputation

Email sender identity normalization and trust/reputation scoring

Email sender identity normalization and trust/reputation scoring

Tierdomain
Roleunclassified (baselined)
Pathcrates/domain/sender-reputation
Edition2021
Targetsdomain_sender_reputation
Public items21 across 5 modules
Tests27

What it is for

# domain-sender-reputation

Email sender identity normalization and trust/bulk reputation scoring — used to decide send-safety (is this a sender worth replying to / a bounce-back safe recipient / a machine sender not worth alerting on).

Sprint 3.45 crate 9 (tools-mail-manager harvest). Lifted from rust-gmail-manager/src/sender/{normalize,scoring,model}.rs — see CHANGELOG.md for the full Gate 4.5 provenance and what was deliberately not lifted.

Two independent concerns

lowercased/trimmed address, display name, domain, root domain, and noreply/machine-sender detection. Pure, no I/O.

0-100 trust score (Bayesian-smoothed) and a 0-100 bulk score, and classify the pair into a scoring::ReputationClass. Pure, no I/O.

models::SenderProfile/models::SenderDomain assemble the two into a persistable record; repository::SenderReputationRepository is the trait a consumer implements against its own store (no concrete backend ships here — see that module's doc comment for why).

Composition, not duplication (Gate 0.5)

normalize::CanonicalSender::to_contact builds a domain_contact::Contact by calling Contact::email rather than inventing a parallel address type — this crate owns parsing raw sender headers into a canonical, scoreable identity, and hands off to domain-contact's existing shape once that identity is linked to a known party.

Example

use domain_sender_reputation::{
models::{InteractionCounts, SenderProfile},
scoring::SenderConfig,
};
use uuid::Uuid;

let config = SenderConfig::default();
let profile = SenderProfile::from_raw_sender(
Uuid::new_v4(),
"Newsletter <news@example.com>",
InteractionCounts {
message_count: 40,
inbox_count: 40,
spam_count: 0,
trash_count: 0,
starred_count: 0,
},
None,
None,
&config,
)
.expect("valid sender header");

assert!(profile.is_machine_sender);

Capabilities

SenderReputationError

Sender-reputation errors.

Item
pub enum SenderReputationError

InteractionCounts

Sender and sender-domain reputation records.

Item
pub struct InteractionCounts

SenderDomain

Sender and sender-domain reputation records.

Item
pub struct SenderDomain
SenderDomain :: fn from_aggregate(tenant_id : Uuid, domain : String, root_domain : String, sender_count : i32, counts : InteractionCounts, first_seen : Option <DateTime <Utc>>, last_seen : Option <DateTime <Utc>>, config : & SenderConfig,) -> Self

SenderProfile

Sender and sender-domain reputation records.

Item
pub struct SenderProfile
SenderProfile :: fn from_raw_sender(tenant_id : Uuid, raw_sender : & str, counts : InteractionCounts, first_seen : Option <DateTime <Utc>>, last_seen : Option <DateTime <Utc>>, config : & SenderConfig,) -> Option <Self>
SenderProfile :: fn from_canonical(tenant_id : Uuid, canonical : & CanonicalSender, counts : InteractionCounts, first_seen : Option <DateTime <Utc>>, last_seen : Option <DateTime <Utc>>, config : & SenderConfig,) -> Self

CanonicalSender

Sender identity normalization: canonicalize a raw From:-style header

Item
pub struct CanonicalSender
CanonicalSender :: fn to_contact(& self, tenant_id : Uuid, party_type : PartyType, party_id : Uuid) -> Contact
fn canonicalize_email(raw : & str) -> Option <CanonicalSender>

SenderReputationRepository

Sender-reputation persistence trait.

Item
pub trait SenderReputationRepository

SenderReputationResult

Sender-reputation persistence trait.

Item
pub type SenderReputationResult<T>: Result <T, SenderReputationError>

SenderSort

Sender-reputation persistence trait.

Item
pub enum SenderSort

scoring (other)

Trust/bulk reputation scoring: Bayesian-smoothed trust score, a

Item
fn compute_bulk_score(message_count : i32, is_noreply : bool, is_machine_sender : bool, starred_count : i32, inbox_count : i32,) -> f32

ReputationClass

Trust/bulk reputation scoring: Bayesian-smoothed trust score, a

Item
pub enum ReputationClass
ReputationClass :: fn as_str(& self) -> & 'static str
ReputationClass :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result
fn classify_reputation(trust_score : f32, bulk_score : f32) -> ReputationClass

SenderConfig

Trust/bulk reputation scoring: Bayesian-smoothed trust score, a

Item
pub struct SenderConfig
SenderConfig :: fn default() -> Self
fn compute_trust_score(inbox_count : i32, starred_count : i32, spam_count : i32, trash_count : i32, config : & SenderConfig,) -> f32

How to use it

From this crate's own rustdoc:

use domain_sender_reputation::{
    models::{InteractionCounts, SenderProfile},
    scoring::SenderConfig,
};
use uuid::Uuid;

let config = SenderConfig::default();
let profile = SenderProfile::from_raw_sender(
    Uuid::new_v4(),
    "Newsletter <news@example.com>",
    InteractionCounts {
        message_count: 40,
        inbox_count: 40,
        spam_count: 0,
        trash_count: 0,
        starred_count: 0,
    },
    None,
    None,
    &config,
)
.expect("valid sender header");

assert!(profile.is_machine_sender);

Module structure

domain_sender_reputation

flowchart TD
  n_domain_sender_reputation["domain_sender_reputation"]
  n_domain_sender_reputation --> n_errors["errors"]
  n_domain_sender_reputation --> n_models["models"]
  n_domain_sender_reputation --> n_normalize["normalize"]
  n_domain_sender_reputation --> n_repository["repository"]
  n_domain_sender_reputation --> n_scoring["scoring"]

Public surface

`errors`

ItemWhat it is
pub enum SenderReputationErrorErrors that can occur in the sender-reputation domain.

`models`

ItemWhat it is
pub struct InteractionCountsInteraction counts a reputation score is computed from.
pub struct SenderProfileA single sender's reputation record.
SenderProfile :: fn from_raw_sender(tenant_id : Uuid, raw_sender : & str, counts : InteractionCounts, first_seen : Option <DateTime <Utc>>, last_seen : Option <DateTime <Utc>>, config : & SenderConfig,) -> Option <Self>Build a SenderProfile by canonicalizing raw_sender and scoring it against counts, using config for the scoring thresholds/priors
SenderProfile :: fn from_canonical(tenant_id : Uuid, canonical : & CanonicalSender, counts : InteractionCounts, first_seen : Option <DateTime <Utc>>, last_seen : Option <DateTime <Utc>>, config : & SenderConfig,) -> SelfBuild a SenderProfile from an already-canonicalized sender.
pub struct SenderDomainA domain-level reputation record, aggregated across all senders at that domain.
SenderDomain :: fn from_aggregate(tenant_id : Uuid, domain : String, root_domain : String, sender_count : i32, counts : InteractionCounts, first_seen : Option <DateTime <Utc>>, last_seen : Option <DateTime <Utc>>, config : & SenderConfig,) -> SelfBuild a SenderDomain record from aggregated counts

`normalize`

ItemWhat it is
pub struct CanonicalSenderA sender identity that has been canonicalized from a raw header value
CanonicalSender :: fn to_contact(& self, tenant_id : Uuid, party_type : PartyType, party_id : Uuid) -> ContactBuild the domain_contact::Contact this sender would be, once linked to a known party
fn canonicalize_email(raw : & str) -> Option <CanonicalSender>Canonicalize a sender email address

`repository`

ItemWhat it is
pub type SenderReputationResult<T>: Result <T, SenderReputationError>Result type for sender-reputation persistence operations.
pub enum SenderSortSort order for listing sender/domain reputation records.
pub trait SenderReputationRepositoryRepository trait for sender/domain reputation persistence.

`scoring`

ItemWhat it is
pub struct SenderConfigConfiguration for sender reputation scoring.
SenderConfig :: fn default() -> Self
pub enum ReputationClassReputation classification derived from a sender's trust and bulk scores.
ReputationClass :: fn as_str(& self) -> & 'static strThe mm-compatible string form ("trusted", "likely_trusted", ...).
ReputationClass :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result
fn compute_trust_score(inbox_count : i32, starred_count : i32, spam_count : i32, trash_count : i32, config : & SenderConfig,) -> f32Compute a 0-100 trust score using Bayesian smoothing over inbox/starred (positive) and spam/trash (negative) interaction counts
fn compute_bulk_score(message_count : i32, is_noreply : bool, is_machine_sender : bool, starred_count : i32, inbox_count : i32,) -> f32Compute a 0-100 bulk score from message volume and machine-sender signals
fn classify_reputation(trust_score : f32, bulk_score : f32) -> ReputationClassClassify a sender into a reputation class from its trust and bulk scores.

Re-exports. Exported here, defined elsewhere.

ExportDefined in
SenderReputationErrorerrors::SenderReputationError
{InteractionCounts,SenderDomain,SenderProfile}models::{InteractionCounts,SenderDomain,SenderProfile}
{SenderReputationRepository,SenderReputationResult,SenderSort}repository::{SenderReputationRepository,SenderReputationResult,SenderSort}
{canonicalize_email,CanonicalSender}normalize::{canonicalize_email,CanonicalSender}
{classify_reputation,compute_bulk_score,compute_trust_score,ReputationClass,SenderConfig,}scoring::{classify_reputation,compute_bulk_score,compute_trust_score,ReputationClass,SenderConfig,}

Boundary

Reaches into foundation.

Shares tier domain with 41 other crates: domain-agreements, domain-ai-report, domain-billing, domain-catalog, domain-classify, domain-comments, domain-competitive-intel, domain-contact, … (41 total).

_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._

Where it sits

Tier (ontology)domain
Architectural role (taxonomy)unclassified (baselined)
Locationcrates/domain/sender-reputation
Vocabulary in force (lexicon)current

Tier flow. Which tiers this crate's own edges cross.

flowchart LR
  n_domain["domain"] --> n_foundation["foundation"]

Dependencies

Runtime, in this workspace.

CrateTierOptionalOnly on
`domain-contact`domainnoalways
`foundation-mail-message`foundationnoalways

Runtime, from outside the workspace.

CrateRequirementFeaturesOptionalOnly on
async-trait^0.1noalways
chrono^0.4serdenoalways
serde^1derivenoalways
serde_json^1noalways
thiserror^2noalways
uuid^1v4, v7, serde, jsnoalways

Development, from outside the workspace.

CrateRequirementFeaturesOptionalOnly on
tokio^1fullnoalways
tokio-test^0.4noalways

Build. None.

Depended on by. Nothing in this workspace.

Signal flow — what reaches this crate, and what it reaches.

flowchart LR
  SELF["domain-sender-reputation"]
  SELF -->|runtime| n_domain_contact["domain-contact"]
  SELF -->|runtime| n_foundation_mail_message["foundation-mail-message"]
  classDef self fill:#1f883d,stroke:#1f883d,color:#fff;
  class SELF self;

Feature flags

No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.

Targets

KindNameSource
libdomain_sender_reputation`src/lib.rs`

Error model

Error typeNamed by
SenderReputationErrorSenderReputationResult

Operational characteristics

PropertyEvidence
async public surfacenone detected
async runtimenone detected
database accessnone detected
network I/Onone detected
unsafe codenone detected
environment variablesnone detected

No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.

Configuration

No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.

No workspace crate depends on this one.

Verification

KindCount
Unit tests27
Integration tests0
Examples0
Doctests1

Evidence by module. How often each public module is named by something executable.

ModuleTestsExamplesConsumers
errors100
models300
normalize200
repository300
scoring500

What the tests establish, by name:

Documentation coverage

MeasureDocumentedTotal
Public items with rustdoc1921
Public modules with a //! block55
pie showData
    title Public items with rustdoc
    "Documented" : 19
    "No rustdoc detected" : 2

Metrics

MetricValue
Rust source files6
Source lines1067
Code lines725
Public API items21
Public modules5
Tests27
Examples0
Cargo features0
Direct runtime dependencies8
Workspace reverse dependencies0
pie showData
    title Public API by kind
    "enum" : 3
    "function" : 4
    "method" : 7
    "struct" : 5
    "trait" : 1
    "type alias" : 1
pie showData
    title Rust source composition
    "Code" : 725
    "Blank or comment" : 342

Generation

Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.

Todas las domain · Manual