calternal-plugin-mail
Mail Plugin: encrypted provider accounts and a rebuildable mailbox projection.
The provider remains the source of truth. Account credentials are Security
state in the Index; synced messages and cursors are derived from the provider
and can be rebuilt. imap owns safe transport and protocol handling,
sync owns resumable generation policy, and cache::store commits message
windows and their cursor together. #726 prepares HTML at cache time through
html; remote owns bounded image prefetch and derived-content jobs.
proxy supplies owner-bound virtual UIDs and a
separate exact MIME cache to the shared IMAP stack. proxy_mutations delivers
accepted flags and selective deletion through durable Jobs. proxy_transfers
journals COPY/MOVE receipts and preserves virtual UIDs while offline (#486, DESIGN §53).
Source: crates/plugins/mail/src/lib.rs
Structs
Section titled “Structs”Credential
Section titled “Credential”pub struct CredentialOne provider app password. Debug output never reveals either field.
Implements: Clone, Serialize, Deserialize, fmt::Debug
Credential::new
Section titled “Credential::new”pub fn new(username: String, app_password: String) -> SelfNo doc comment.
Credential::with_smtp
Section titled “Credential::with_smtp”pub fn with_smtp( username: String, app_password: String, smtp_username: String, smtp_app_password: String, ) -> SelfNo doc comment.
Credential::username
Section titled “Credential::username”pub fn username(&self) -> &strNo doc comment.
Credential::app_password
Section titled “Credential::app_password”pub fn app_password(&self) -> &strNo doc comment.
Credential::smtp_username
Section titled “Credential::smtp_username”pub fn smtp_username(&self) -> &strNo doc comment.
Credential::smtp_app_password
Section titled “Credential::smtp_app_password”pub fn smtp_app_password(&self) -> &strNo doc comment.
Source: crates/plugins/mail/src/crypto.rs:20
EncryptedCredential
Section titled “EncryptedCredential”pub struct EncryptedCredentialOne encrypted app password stored with an account row.
Fields
pub nonce: Vec<u8>pub ciphertext: Vec<u8>
Implements: Clone, fmt::Debug
Source: crates/plugins/mail/src/crypto.rs:76
InstanceKey
Section titled “InstanceKey”pub struct InstanceKey(XChaCha20Poly1305, IntegrationKey);Instance AEAD key. Debug output never includes key bytes.
Implements: Clone, fmt::Debug
InstanceKey::new
Section titled “InstanceKey::new”pub fn new(key: [u8; 32]) -> Result<Self, CredentialError>Build legacy and shared ciphers from one nonzero instance key. The two domains stay separate so image rollback can read old rows (§49 I6, #407).
InstanceKey::encrypt
Section titled “InstanceKey::encrypt”pub fn encrypt( &self, owner_id: &str, account_id: &str, credential: &Credential, ) -> Result<EncryptedCredential, CredentialError>Write a legacy Mail envelope for an unlinked account. Random nonces and owner/account AAD prevent reuse across rows (§49 I6, #407). Encrypt the Mail credential JSON with a fresh 24-byte nonce (#930). Authenticated data binds the User and Connected Account IDs in the Mail credential domain. Store nonce and ciphertext together; never substitute another account’s context (DESIGN §45).
InstanceKey::decrypt
Section titled “InstanceKey::decrypt”pub fn decrypt( &self, owner_id: &str, account_id: &str, encrypted: &EncryptedCredential, ) -> Result<Credential, CredentialError>Read the original Mail envelope for migration or an unlinked account.
Persisted route consumers use decrypt_account instead (§49 I6, #407).
Authenticate the stored envelope under the original User and Connected
Account IDs (#930; DESIGN §45). Reject a nonce that is not 24 bytes.
Only authenticated plaintext is decoded as credential JSON; altered
bytes or a different account context return an authentication error.
InstanceKey::decrypt_integration
Section titled “InstanceKey::decrypt_integration”pub fn decrypt_integration( &self, owner_id: &str, account_id: &str, encrypted: &EncryptedIntegrationCredential, ) -> Result<IntegrationCredential, IntegrationCredentialError>Decrypt the one credential shared by linked Mail and Calendar services.
Source: crates/plugins/mail/src/crypto.rs:89
IntegratedAccountWrite
Section titled “IntegratedAccountWrite”pub struct IntegratedAccountWrite<'a>Mail connection settings for one shared account (#407, DESIGN §49).
Fields
pub owner_id: &'a strpub id: &'a strpub email: &'a strpub provider: &'a strpub imap_host: &'a strpub imap_port: u16pub imap_tls_mode: MailTlsModepub username: &'a strpub smtp_host: &'a strpub smtp_port: u16pub smtp_tls_mode: MailTlsModepub smtp_username: &'a strpub enabled: boolpub created_ms: i64
Source: crates/plugins/mail/src/cache/store.rs:93
MailCreateAccount
Section titled “MailCreateAccount”pub struct MailCreateAccountNo doc comment.
Fields
pub provider: MailProviderpub email: Stringpub imap_host: Stringpub imap_port: u16pub imap_tls_mode: MailTlsModepub username: Stringpub app_password: Stringpub smtp_host: Stringpub smtp_port: u16pub smtp_tls_mode: MailTlsModepub smtp_username: Stringpub smtp_app_password: Option<String>
Implements: Clone, Deserialize, ToSchema, fmt::Debug
Source: crates/plugins/mail/src/routes.rs:385
MailProxyStore
Section titled “MailProxyStore”pub struct MailProxyStoreThe authenticated listener supplies this User; commands cannot replace it.
Implements: NotesStore
MailProxyStore::new
Section titled “MailProxyStore::new”pub fn new(db: Db, owner: &str, key: InstanceKey) -> SelfBind a cache and its encrypted upstream resolver to one authenticated User.
Source: crates/plugins/mail/src/proxy.rs:65
MessagePage
Section titled “MessagePage”pub struct MessagePageNo doc comment.
Fields
pub messages: Vec<MessageSummary>pub next: Option<PageCursor>
Source: crates/plugins/mail/src/cache/store.rs:316
MessageSummary
Section titled “MessageSummary”pub struct MessageSummaryNo doc comment.
Fields
pub id: Stringpub account_id: Stringpub folder_id: Stringpub subject: Stringpub from: Vec<serde_json::Value>pub to: Vec<serde_json::Value>pub cc: Vec<serde_json::Value>pub bcc: Vec<serde_json::Value>pub sent_date_ms: Option<i64>pub received_ms: i64pub preview: Stringpub category: Stringpub thread_id: Stringpub body_truncated: boolpub attachment_count: u32pub flags: Vec<String>pub labels: Vec<String>
Implements: Clone, Debug
Source: crates/plugins/mail/src/cache/store.rs:261
PageCursor
Section titled “PageCursor”pub struct PageCursorStable keyset cursor for list pages.
Fields
pub received_ms: i64pub id: String
Implements: Clone, Debug
Source: crates/plugins/mail/src/cache/store.rs:255
MailProvider
Section titled “MailProvider”pub enum MailProviderNo doc comment.
Variants
GmailIcloudFastmailCustom
Implements: Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, ToSchema
Source: crates/plugins/mail/src/routes.rs:365
MailTlsMode
Section titled “MailTlsMode”pub enum MailTlsModeIMAP transport mode. Cleartext authentication is never allowed.
Variants
ImplicitStarttls
Implements: Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, ToSchema
MailTlsMode::as_str
Section titled “MailTlsMode::as_str”pub fn as_str(self) -> &'static strReturn the stable database value for this transport mode (#407, DESIGN §49).
Source: crates/plugins/mail/src/cache/store.rs:31
Functions
Section titled “Functions”configure_instance_secret_key
Section titled “configure_instance_secret_key”pub fn configure_instance_secret_key(key: [u8; 32]) -> Result<(), crypto::CredentialError>Install the instance secret already loaded by the server for Calendar. Mail uses an independent AEAD domain while sharing the Instance key.
Source: crates/plugins/mail/src/lib.rs:69
create_integration_service_account
Section titled “create_integration_service_account”pub async fn create_integrated_account( transaction: &mut sqlx::Transaction<'_, sqlx::Sqlite>, account: IntegratedAccountWrite<'_>,) -> Result<(), sqlx::Error>Add Mail’s projection in the shared account transaction (#407, DESIGN §49). Sync decrypts the shared row; zero-length ciphertext is a legacy placeholder.
Source: crates/plugins/mail/src/cache/store.rs:461
device_store
Section titled “device_store”pub fn device_store( db: calternal_db::Db, owner: &str,) -> Result<MailProxyStore, crypto::CredentialError>Construct an authenticated Mail reader with the server-loaded Instance key. The caller supplies the authenticated User, never a protocol parameter (#486, DESIGN §53). An unconfigured key fails before any provider access.
Source: crates/plugins/mail/src/lib.rs:81
list_messages
Section titled “list_messages”pub async fn list_messages( db: &Db, owner_id: &str, folder_id: &str, category: Option<&str>, before: Option<&PageCursor>, limit: usize,) -> Result<MessagePage, sqlx::Error>Page one row per live message in a folder, even when several provider UIDs point at it. A covering membership index selects only the requested keyset window; details load for those keys in the same snapshot (#825, #626, DESIGN §45). While intent is queued, include its bounded destinations and pick one unread-first row across real and pending duplicate UIDs (#486).
Source: crates/plugins/mail/src/cache/store.rs:2167
migrations
Section titled “migrations”pub fn migrations() -> calternal_db::MigrationSetCreate the Mail tables in the Index.
Source: crates/plugins/mail/src/cache/store.rs:322
queue_integration_sync
Section titled “queue_integration_sync”pub async fn queue_integration_sync( db: &calternal_db::Db, owner_id: &str, account_id: &str,) -> Result<(), calternal_db::DbError>Queue the first Mail sync for a newly enabled account (#407, DESIGN §49).
Source: crates/plugins/mail/src/lib.rs:51
rewrap_legacy_credential
Section titled “rewrap_legacy_credential”pub fn rewrap_legacy_credential( owner_id: &str, account_id: &str, encrypted: &EncryptedCredential, instance_key: &[u8; 32], integration_key: &IntegrationKey,) -> Result<EncryptedIntegrationCredential, CredentialError>Re-wrap one pre-Connected-Accounts Mail secret without exposing it to logs. The old Mail AAD and the shared Integration AAD differ, so copy the exact Mail and SMTP fields through memory and encrypt them under the shared domain before the account migration commits (#407, DESIGN §49 I6).
Source: crates/plugins/mail/src/crypto.rs:245
test_integration_connection
Section titled “test_integration_connection”pub async fn test_integration_connection(input: &MailCreateAccount) -> Result<usize, String>Check Mail settings and authenticate one IMAP connection before the server stores a shared Connected Account.
Source: crates/plugins/mail/src/lib.rs:39
validate_integration_endpoint
Section titled “validate_integration_endpoint”pub async fn validate_integration_endpoint(host: &str, port: u16) -> Result<(), String>Reject a provider host that resolves to a local or reserved network (#407, DESIGN §49).