email

package
v0.23.0 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package email is the gateway's email channel: a mailbox the agent answers. IMAP (SSL :993) is polled for NEW mail past a durable UID cursor; replies go out over SMTP (STARTTLS :587) with proper threading headers. The mailbox can be the agent's own dedicated account OR a personal inbox: the channel reads with BODY.PEEK and acks by advancing its cursor, NEVER by setting \Seen — it leaves flags, folders, and read state exactly as it found them, and the gateway's allow-list decides which senders it answers at all.

Ack semantics match the gateway contract: the cursor advances ONLY after Deliver returns nil, so a crash between fetch and durable record just re-fetches it. On first connect (or a UIDVALIDITY reset) the cursor initializes to the mailbox's current end — pre-existing mail, including a personal inbox's whole history, is never read, never answered. The durable dedup key is <mailbox>/<UIDVALIDITY>/<UID> — the provider-side identity, robust against malformed or duplicated Message-IDs (which serve threading, not dedup).

IDENTITY CAVEAT: the principal is the RFC From address — weaker than the other channels' platform-authenticated ids, since From can be spoofed by mail that evades the provider's SPF/DKIM/DMARC filtering. The mailbox provider's authentication is the real gate (a mainstream provider rejects or junks spoofed mail before we poll it). Enforcing Authentication-Results=pass explicitly is a tracked follow-up.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Channel

type Channel struct {
	// contains filtered or unexported fields
}

Channel is an email (IMAP+SMTP) connection.

func New

func New(address, password, imapHost, smtpHost string, poll time.Duration, mediaDir string, store CursorStore) *Channel

New builds an email channel. store persists the mailbox cursor. poll <= 0 uses the default (15s). mediaDir is the gateway media spool inbound attachments are saved into; "" disables attachment handling.

func (*Channel) Name

func (c *Channel) Name() string

Name returns the adapter identifier.

func (*Channel) Send

func (c *Channel) Send(ctx context.Context, conversation string, msg channels.Outbound) error

Send replies to a peer over SMTP, threading into the remembered conversation when one is known. Email has no length cap, so no chunking.

func (*Channel) Start

func (c *Channel) Start(ctx context.Context, sink channels.Sink) error

Start polls the mailbox until ctx is cancelled. Connection errors back off exponentially (capped) instead of returning, so a flaky mail server never takes the gateway down.

type CursorStore added in v0.17.0

type CursorStore interface {
	Cursor(ctx context.Context, channel string) (string, error)
	SetCursor(ctx context.Context, channel, cursor string) error
}

CursorStore persists the mailbox position (UIDVALIDITY/last-UID) so a restart resumes where it left off. The gateway state store implements it.

Jump to

Keyboard shortcuts

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