Phase 1: Foundation (Database & CLI)
Overview
Operators can register a mailbox’s outbound SMTP server through the CLI with
encrypted credential storage, strict TLS enforcement, and an explicit TLS-mode
choice (implicit for port 465, starttls_required for port 587). The
outbound_messages table provides the durable queue that all subsequent phases
build on. Nothing is sent yet — this phase is pure data foundation.
Goal
Extend the existing IMAP proxy database schema and CLI to handle SMTP settings and the outbound message queue, setting the foundation for sending capabilities.
TDD Acceptance Criteria
pytest tests/models/test_mailbox.py::test_smtp_fields_existMUST PASSpytest tests/models/test_mailbox.py::test_smtp_password_encryptionMUST PASSpytest tests/models/test_outbound_messages.py::test_table_creation_and_status_enumMUST PASSpytest tests/test_cli.py::test_mailboxes_create_prompts_for_smtpMUST PASS
Technical Specifications
SQLAlchemy Models
Mailbox Model Updates:
smtp_host: String, nullable=Falsesmtp_port: Integer, default=587smtp_username: String, nullable=Falsesmtp_password_encrypted: LargeBinary, nullable=Falsesmtp_tls_mode: Enum (MailboxTlsMode:implicit/starttls_required), nullable=False, defaultstarttls_required. Replaces theuse_tls = smtp_port == 465port heuristic.implicitmeans TLS from byte zero (typical on port 465).starttls_requiredmeans plaintext connect followed by a mandatory STARTTLS upgrade; if the server does not advertise STARTTLS, or if the handshake fails, the worker fails the job closed and never sends AUTH over plaintext. Certificate verification is pinned on (validate_certs=True) for both modes. Closes FEEDBACK.md §1.7.
OutboundMessage Model:
id: UUID (Primary Key)mailbox_id: UUID (Foreign Key)recipient_email: Stringsubject: Stringstatus: Enum (queued,processing,sent,failed,bounced)attempts: Integer, default=0next_retry_at: DateTime, nullable=Trueprocessing_started_at: DateTime, nullable=Truesent_at: DateTime, nullable=Trueopened_at: DateTime, nullable=Trueclicked_at: DateTime, nullable=Trueerror_log: Text, nullable=Truecreated_at: DateTimeupdated_at: DateTime
CLI Updates
- Command
mailboxes:createmust ask for SMTP Host, SMTP Port, SMTP Username, SMTP Password, and SMTP TLS Mode. - The TLS-mode prompt is constrained to
{implicit, starttls_required}and defaults tostarttls_required; the command layer coerces viaMailboxTlsMode(...)so invalid values raiseValueErrorbefore reaching the DB. - Update
messages:createpermission tomessages:sendin JWT issuance logic.
Security Considerations
smtp_password_encryptedmust be encrypted using the exact same Fernet symmetric encryption setup as the existing IMAP password.