Documentation
¶
Overview ¶
Package loopback delivers agent-to-self messages without touching the upstream SMTP relay. A self-send (agent emails itself) is a degenerate case for SMTP: the relay would refuse it as a self-spam guard, and the roundtrip through SES + MX + the local SMTP receiver is wasted work when the destination is local anyway.
The package owns self-recipient detection and byte-identical MIME composition. Persistence lives in the identity store so Sent/Inbox rows, idempotency, and outcome events can share one database transaction.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ComposeMIME ¶
func ComposeMIME(agent *identity.AgentIdentity, req outbound.SendRequest, providerID, fromDomain string) ([]byte, error)
ComposeMIME builds the RFC 5322 / 2046 message bytes the inbound row will store as raw_message.
Delegates to the same composer the real SMTP path uses (outbound.ComposeMessageWithAttachments) so the produced message is byte-equivalent to what an external roundtrip would have generated — same headers, same multipart structure, same attachment encoding. The SDK's InboundEmail.fromPayload → parseRawEmail pipeline then finds body text/html AND attachments without any loopback-specific branch.
Prepends ONE synthetic Received: line per RFC 5321 §4.4 ("each time a message is relayed... the receiving SMTP server MUST insert a 'Received:' line"). Mature local-delivery MTAs (sendmail's local mailer, Postfix's local daemon, Exim's local_smtp transport) all add such a line even for same-host delivery; doing the same here keeps stored messages forensically self-documenting. The "loopback" keyword is the searchable signal — `grep "with loopback"` over raw messages finds every self-send.
Threading: req.ReplyToMessageID and req.References are passed through to the composer, so self-replies that reach this path via the HITL approval finalizer preserve In-Reply-To / References headers — same shape an SMTP-routed reply would carry.
func IsSelfSend ¶
func IsSelfSend(req outbound.SendRequest, agentEmail string) bool
IsSelfSend reports whether req targets only the sender's own inbox — i.e., the agent is writing a note to itself. Returns true only when there's a single To recipient that matches the agent's own address (case-insensitive, trimmed) AND no Cc/Bcc — any mixed/external recipient routes through normal SMTP unchanged.
Callers that have CC/BCC carrying agent aliases (e.g. the reply path with replyAll=true on a self-thread, where the original message's CC list already includes the agent) should strip them via StripAgentSelfAliases before checking — outbound.Sender does the same alias-strip downstream as a self-spam guard, so doing it here is purely "see through" the aliases earlier.
func ProviderID ¶
ProviderID synthesizes an RFC 5322-shaped Message-ID for the outbound row's provider_message_id column. Mirrors what an external MTA would have stamped — keeps the column non-empty and recognizable in operator queries (the "@loopback.<domain>" host portion makes self-sends greppable across the dataset).
func StripAgentSelfAliases ¶
StripAgentSelfAliases removes case-insensitive, whitespace-trimmed matches of agentEmail from addrs. Used to pre-clean reply recipients so IsSelfSend can recognize replyAll-on-a-self-thread as still a self-send. Returns a fresh slice; the input is not mutated.
Types ¶
This section is empty.