Repository navigation
feat(email): async per-credential mail transport (providers::mail) - #48
Conversation
The mail provider module was restructured to properly expose types and functions through the module hierarchy. The `types.rs` file was added to define shared data structures, and the test module was updated to import from the correct paths. This resolves compilation errors caused by missing type definitions and incorrect module references in the test suite. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add the `tokio1-rustls-tls` feature to the lettre dependency and introduce the `smtp` module behind the `email-send` feature flag. This allows the mail provider to use lettre's async SMTP transport with TLS, enabling non-blocking email sending in async contexts. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
When the SMTP password is not configured, the provider now returns a clear configuration error instead of panicking with an unwrap failure. This improves user experience by providing a helpful diagnostic message during startup. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test module was missing explicit imports for `MailSecret` and `SmtpCredentials`, which are now brought into scope to resolve compilation errors in the smtp test suite. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
When an IMAP FETCH response lacks a UID, the parser now returns an error instead of silently proceeding with an undefined value. This prevents potential data corruption and ensures that only well-formed responses are processed. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Updated the test expectation to match the actual delivery status returned by the email provider, ensuring the test validates the correct behavior. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add `smtp_credentials` and `imap_credentials` stub methods to `EmailChannel` for future credential retrieval. Also fix a test that incorrectly constructed a `SendMessage` by moving the `in_thread` call into the struct literal. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformat several long method chains and assertion calls to comply with the project's line-length convention, and replace the stub module documentation in `mod.rs` with a comprehensive description of the mail transport architecture, including the separation of concerns between outbound/inbound email and credentials, feature-gated transport implementations, and the origin of the ported code. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Tiny Sweeper reviewTiny Sweeper reviewed this change across 6 lane(s) and found 0 active actionable finding(s). Detailed lane evidence and any incomplete work are listed below. State: Incomplete Review snapshot
Completeness: Incomplete What changedThe review could not produce a supported behavioral summary; inspect the cited changed surface and lane details below. FeaturesNone identified with supported citations. TestsNo supported feature-to-test mapping was produced. Test execution is not inferred. FindingsNo active actionable findings. Could not review: Cargo.toml, README.md, src/providers/email_channel.rs, src/providers/email_channel_tests.rs, src/providers/mail/README.md, src/providers/mail/imap.rs, src/providers/mail/imap_tests.rs, src/providers/mail/mod.rs, src/providers/mail/mod_tests.rs, src/providers/mail/smtp.rs, src/providers/mail/smtp_tests.rs, src/providers/mail/types.rs, src/providers/mod.rs Before merge
How this fits togetherflowchart LR
n0["EmailChannel<br/>changed"]:::changed
n1["EmailConfig"]:::impacted
n2["channel_with_allowlist"]:::impacted
n3["Result"]:::impacted
n4["run_idle_session"]:::impacted
n5["fetch_unseen"]:::impacted
n2 -->|uses| n0
n2 -->|uses| n1
n4 -->|uses| n3
n5 -->|uses| n3
classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Agent review detailscritique
security
tests
commits
description
e2e
Evidence and run details
|
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 🧰 Additional context used📚 Code guidelines (1)No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
⛔ Files ignored due to path filters (1)
📒 Files selected for processing (13)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThis change adds shared mail types and asynchronous SMTP and IMAP providers. It updates ChangesMail provider
Priority: ➖ Normal Estimated code review effort: 4 (Complex) | ~45 minutes Change: Feature Sequence Diagram(s)sequenceDiagram
participant EmailChannel
participant LettreMailSender
participant SMTPServer
EmailChannel->>LettreMailSender: Send credentials and outbound email
LettreMailSender->>SMTPServer: Deliver message
SMTPServer-->>LettreMailSender: Return delivery result
LettreMailSender-->>EmailChannel: Return result or error
Merge Risk: ⚪ Minimal · up to This adds a mail transport module and moves email sending to an async SMTP sender with a 30-second bound. No concrete merge-blocking issue was found. The TLS paths are only covered by an external live test. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to The new receiver acknowledges messages without binding them to a mailbox generation and fetches unread messages without count or byte limits. These choices can undermine recovery or allow hostile mail to exhaust a shared process. Existing SMTP security behavior is preserved; deployment exposure depends on how applications adopt the receiver. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 66.15% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 65 functions across 10 files. (3 skipped: 3 unsupported.)
A rabbit packed a note to send, Comment |
There was a problem hiding this comment.
tinysweeper found nothing blocking, but could not review everything, so this is not an approval: Cargo.toml, README.md, src/providers/email_channel.rs, src/providers/email_channel_tests.rs, src/providers/mail/README.md, src/providers/mail/imap.rs, src/providers/mail/imap_tests.rs, src/providers/mail/mod.rs and 5 more.
$0.0015 · 64,983 in / 2,421 out · 21,760 cached (33%) · deepseek/deepseek-v4-flash
tests: $0.0006 · 21,329 in / 76 out · 0 cached (0%) · deepseek/deepseek-v4-flash
description: $0.0006 · 21,847 in / 72 out · 0 cached (0%) · deepseek/deepseek-v4-flash
Summary
Adds
providers::mail, an async mail transport that takes its credentials per call. It is ported from OpenCompany'sserver/ops/mailer.rs,smtp.rsandimap.rsso OpenCompany can delete its host-side copies.EmailChannel's asyncChannel::sendnow goes through it instead of driving the blockingSmtpTransporton a runtime worker.New public API (
tinychannels::providers::mail)MailCredentials::Smtp(SmtpCredentials)provider: "smtp". Also providesprovider(),from_email()andfrom_name().MailProviderDisplayandFromStr(case-insensitive).SmtpSecurityNone,Starttls(the default) orSsl.SmtpCredentialshost,port,security,username,password,from_name,from_email.ImapCredentialshost,port,username,password.MailSecretexpose(). Deserializes plaintext; bothDebugandSerializerender"[redacted]". This mirrors OpenCompany'sSecretValueguard from #1770.OutboundEmail,InboundEmail,FetchedEmail { uid, email }MailSender::send(&creds, &email),MailReceiver::{fetch_new, mark_seen}MailErrorConfig,InvalidAddress,Transport,TimedOut. Marked#[non_exhaustive].LettreMailSender::{new, default, with_timeout},DEFAULT_TIMEOUT(30s)email-sendlettre(tokio1-rustls-tls). An empty username means noAUTHis attempted.AsyncImapReceiver::{new, default, with_mailbox, with_timeout, mailbox},DEFAULT_TIMEOUT(30s)emailringprovider. Fetches withUID SEARCH UNSEEN+UID FETCH (UID BODY.PEEK[]);mark_seensendsUID STORE +FLAGS.SILENT (\Seen), and an empty UID list never dials.parse_message(&[u8]) -> InboundEmailemailEmailChannel::smtp_credentials(),EmailChannel::imap_credentials()email-sendsmtp_tlsmaps toSsl, andfalsemaps toNone.The vocabulary links no crates, so it compiles in every build. A host can accept a
dyn MailSenderor write test doubles withoutlettre, and the heavy dependencies still follow the existingemail-send/emailsplit.Behaviour changes
EmailChannelas aChannel(featureemail) now sends throughLettreMailSender:usernamenow skipsAUTH.send_message/build_*_messagesurface used by send-only hosts is unchanged.Differences from the OpenCompany port
MailErrorrather thanOpenCompanyError.ring(this crate's provider) instead ofaws_lc_rs.MailConfig/TenantMailboxConfig(OpenCompany-specificOPENCOMPANY_MAIL_*env parsing);RecordingMailSender/RecordingMailReceiver(host test doubles).Verification
SMTP is tested against a scripted plaintext SMTP fake: envelope, headers and body, per-call
AUTH PLAINcredentials, an invalid address failing without dialing, a refused recipient, and a stalled server timing out. The IMAP protocol exchange (login, select, PEEK fetch, then store) is tested against a scripted plaintext IMAP fake through the stream-generic session functions.The TLS connect leg of
AsyncImapReceiverand theStarttls/SslSMTP legs are not exercised offline, because they need a certificate-bearing server. OpenCompany's live Stalwart smoke test covers that path.Summary by CodeRabbit