Help us shape the future of Go! Take the 2026 Go Developer Survey

loopback

package
v1.9.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

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

func ProviderID(fromDomain string) string

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 ScreeningEvents added in v1.3.0

ScreeningEvents builds the screening-outcome events for the inbound leg of a loopback self-send, mirroring the relay's SMTP-path payload shapes (internal/relay/server.go processInbound) with loopback identities: header_from is the agent itself, envelope_from and authentication are nil (there was no wire hop). msg is the just-created inbound row.

  • gate flag, delivered (not held) → email.flagged (in addition to email.received, which the caller publishes on the delivered path).
  • block → email.blocked: the message was accept-then-quarantined (review_rejected); this is the only signal a subscriber gets.
  • review → email.review_requested: held as pending_review awaiting a human / TTL; carries approval_expires_at.

Deterministic event ids keep a retried local delivery idempotent, matching the relay's MTA-retry semantics.

func StripAgentSelfAliases

func StripAgentSelfAliases(addrs []string, agentEmail string) []string

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL