Skip to main content

Reference

Config Schemas

Hydra validates SMTP account and policy config against these schemas during config composition, including values supplied through command-line overrides.

SMTP account config is registered as arbiter/account/smtp/schema:

class MailTlsMode(str, Enum):
none = "none"
starttls = "starttls"
implicit = "implicit"


class SMTPSentCopyFailureMode(str, Enum):
warn = "warn"
fail = "fail"


@dataclass
class SMTPSentCopyAccountConfig:
folder: str | None = None


@dataclass
class SMTPConfig(Policy):
policy: str = "bot"
description: str = ""
guidance: str = ""
host: str = "localhost"
port: int = 587
authenticate: bool = False
username: str = ""
password: str = ""
from_email: str = "agent@example.com"
from_name: str = "Arbiter"
tls: MailTlsMode = MailTlsMode.starttls
verify_peer: bool = True
timeout_seconds: float = 30.0
sent_copy: SMTPSentCopyAccountConfig = field(
default_factory=SMTPSentCopyAccountConfig
)

SMTP policy config is registered as arbiter/policy/smtp/schema:

@dataclass
class SMTPLimitsConfig:
max_messages_per_minute: int | None = None
max_recipients_per_message: int | None = None


@dataclass
class SMTPIdempotencyConfig:
expiration_days: int = 7


@dataclass
class SMTPRecipientPolicyConfig:
allowed_recipients: list[str] = field(default_factory=list)
blocked_recipients: list[str] = field(default_factory=list)
allowed_domain_patterns: list[str] = field(default_factory=list)
blocked_domain_patterns: list[str] = field(default_factory=list)


@dataclass
class SMTPSentCopyPolicyConfig:
enabled: bool = True
on_failure: SMTPSentCopyFailureMode = SMTPSentCopyFailureMode.warn


@dataclass
class SMTPServicePolicyConfig(Policy):
limits: SMTPLimitsConfig = field(default_factory=SMTPLimitsConfig)
idempotency: SMTPIdempotencyConfig = field(default_factory=SMTPIdempotencyConfig)
recipient_policy: SMTPRecipientPolicyConfig = field(
default_factory=SMTPRecipientPolicyConfig
)
sent_copy: SMTPSentCopyPolicyConfig = field(
default_factory=SMTPSentCopyPolicyConfig
)

Enforcement

The server enforces:

  • account existence
  • configured recipient policy
  • max_recipients_per_message
  • per-account per-process message rate limit
  • keyed idempotency replay/conflict handling
  • sent-copy idempotency, so a replay does not resend SMTP and either avoids a second Sent append or retries a previously failed Sent append
  • no caller override for SMTP host, TLS, credentials, sender identity, or Reply-To

Sent-Copy Reference

Sent-copy saves a submitted SMTP message into a matching IMAP Sent folder. The message is submitted through SMTP first, then appended through IMAP.

sent_copy.enabled lives on the SMTP policy and controls whether Sent-copy is attempted. sent_copy.on_failure also lives on the policy: warn keeps SMTP submission success even when Sent-copy cannot be saved, while fail reports the Sent-copy failure as an operation failure. sent_copy.folder lives on the SMTP account and can name an explicit IMAP folder for the Sent copy.

When sent_copy.folder is set, Arbiter uses that folder on the IMAP account with the same account id. Without an override, Arbiter uses the matching IMAP account only when exactly one folder has kind: SENT. If there is no matching IMAP account, no Sent folder, or multiple Sent folders, Sent-copy is skipped or failed according to sent_copy.on_failure.

The operation result reports Sent-copy as saved, skipped, failed, or disabled. A retry with the same idempotency_key and payload does not submit the SMTP message again. If the previous SMTP submission succeeded but Sent-copy failed or was skipped, the retry can retry only the IMAP Sent-copy step. While that retry is possible, the retry record can contain the submitted message bytes.

Config Checks

CommandWhat it checksContacts SMTP?
arbiter-server config checkStatic SMTP account and policy validity.No
arbiter-server config check --liveStatic checks plus configured account readiness.Yes

Static SMTP checks:

CheckFailure
sent_copy.folder is non-empty when configuredSMTP sent_copy.folder must be non-empty
idempotency.expiration_days is positiveSMTP idempotency expiration_days must be positive

Live SMTP checks:

CheckNotes
connectOpens the configured SMTP connection.
ehloVerifies SMTP greeting/capability exchange.
noopVerifies the authenticated session can issue a read-only command.
tlsVerifies the configured TLS mode as part of connection setup.
idempotency_storageWrites, reads, and deletes a short-lived retry-readiness record.
sent_copy_destinationRuns only when sent_copy.enabled: true and sent_copy.on_failure: fail; verifies the required Sent-copy destination can be resolved.

The live check is read-only for email delivery: it does not send mail.