Feature Specification: Resource smtp

Created: 2026-10-07

Status: Implemented

Input: Reconstructed from the code of core/resources/smtp/, the setup wiring and the SMTP integration tests at 093e68b6f, pull requests #1184 and #1205, and issue #527.

User Scenarios & Testing (mandatory)

The smtp resource gives an application an account to submit outbound mail. It wraps the smtp contract. A group either runs its own maddy relay or submits to an external relay. The shared resource rules are in the resource family spec.

User Story 1 - Give an application a mail submission account (Priority: P1)

An application must send mail, for example a password reset. It requests a submission account with a sender address. The policy gives the application one account on the relay. The consumer node gets a contract result with the host, the port, the username and a password file. A static client module of the component reads the result to configure the mailer. The request holds only data.

Why this priority: password resets and notifications do not work without outbound mail.

Independent Test: a VM test of the maddy provider; a unit test that each request is plain data; a unit test of the policy: one want per application, the result on the consumer node, the password file of each application, and the secrets of the relay node.

Acceptance Scenarios:

  1. Given two enabled requests from two applications, When the policy runs, Then it gives one request per application, keyed by the application name.
  2. Given a request from an application, When the deployment is evaluated, Then the provider side gets a want with the sender of that application.
  3. Given a routed request, When the consumer side is evaluated, Then the contract result holds the sender, the relay host, the submission port, and the sender as the username.
  4. Given the maddy provider, When the relay node is evaluated, Then the password of each application is in the KV store of the group.
  5. Given the maddy provider and two applications on one node, When the node is evaluated, Then each application gets the rendered password file of its own account.
  6. Given a relay with an account that the configuration does not declare, or with a password that is not the one in the password file, When maddy-ensure-accounts runs, Then it removes that credential and that IMAP account, and sets the password from the file.

User Story 2 - Pick the relay per group (Priority: P1)

The group data states the relay choice of the group: its own maddy relay, with or without an upstream smarthost, or an external relay. The routing reads only this group data. It knows no group name, host name or secret path. A group on the external relay provides smtp without a relay node.

Why this priority: the choice decides if the group runs a mail node at all.

Independent Test: a unit test of the routing.

Acceptance Scenarios:

  1. Given a group on the external relay, When the routing is evaluated, Then the provider is external-relay with the stated host, port, account and password file.
  2. Given a group on the external relay without a host, a username or a password file, When the routing is evaluated, Then the evaluation fails and names the missing value.
  3. Given a group that states no provider, When the routing is evaluated, Then the provider is maddy.
  4. Given a group on maddy with a smarthost, When the routing is evaluated, Then the relay forwards through the stated host, port and account, with a password from the secrets of the group.
  5. Given a group on maddy without a smarthost, When the routing is evaluated, Then the relay delivers directly to the mail exchanger of each recipient.
  6. Given a group with an unknown provider, When the routing is evaluated, Then the evaluation fails.

User Story 3 - Sign outbound mail and accept DMARC reports (Priority: P2)

A receiver checks DKIM and DMARC. The maddy relay signs each message with one key for the organizational domain. When the operator has an email address, the relay accepts the DMARC aggregate reports and forwards them to that address.

Why this priority: the smarthost rewrites the envelope sender. Only a DKIM signature aligns with the sender header.

Independent Test: VM tests of the DKIM signature and of the report intake.

Acceptance Scenarios:

  1. Given DKIM is on, When the relay sends a message, Then the message carries a DKIM signature with the selector that the zone publishes.
  2. Given the generated DKIM key is missing, When the relay starts, Then the start fails, so the relay does not sign with a key that nobody published.
  3. Given report intake is on, When the relay is evaluated, Then the firewall opens the inbound mail port, and mail to the report address goes to the forward address.
  4. Given report intake is off, When the relay is evaluated, Then the firewall opens only the submission port.

Edge Cases

  • One application states two different senders: the evaluation fails.
  • Two applications state the same sender: the evaluation fails, because the sender is the account username.
  • DKIM is on without a signing domain: the evaluation fails.
  • The report address is not under a local mail domain, or the forward address is under one: the evaluation fails.
  • An upstream change of the relay configuration makes a rewrite of that configuration miss: the evaluation fails.
  • When the policy is disabled, it gives nothing.

Requirements (mandatory)

Functional Requirements

  • FR-001: The policy MUST give one request per application.
  • FR-002: The policy MUST give no output when it is disabled.
  • FR-003: The policy MUST make the relay node run the relay daemon.
  • FR-004: The policy MUST give the generated secrets of the routed provider to the relay node.
  • FR-005: The policy MUST declare the relay domain as a TLS domain.
  • FR-006: The maddy provider MUST give each application its own account and password.
  • FR-007: The maddy provider MUST use STARTTLS and password authentication to the smarthost, and MUST rewrite the envelope sender to the smarthost account.
  • FR-008: Without a smarthost, the maddy provider MUST deliver only over encrypted connections, and MUST apply DANE and MTA-STS.
  • FR-009: The maddy provider MUST take its TLS certificate from the ssl contract.
  • FR-010: The external-relay provider MUST NOT run a daemon.
  • FR-011: One source MUST give the sender addresses, the DKIM selector and the report address, for the relay and for the zone records.
  • FR-012: The request MUST contain only data. The consumer side MUST give the request to the smtp contract of the node, not as an argument to a function of the request.
  • FR-013: The routing MUST take the provider, the smarthost and the account from the group data. It MUST NOT name a group, a host or a secret path.
  • FR-014: The maddy provider MUST remove each credential and each IMAP account that the configuration does not declare, and MUST set the password of each declared credential from its password file on each run. It MUST refuse an empty password file.

Key Entities

  • Request: the enable flag and the sender. It holds only data.
  • Client module: the static module of a component that reads the contract result and the account of its application.
  • Policy: the enable flag, the relay domain, the submission port, the instance name and the routing.
  • maddy provider: the relay, with its smarthost, DKIM and report settings, and the list of accounts.
  • external-relay provider: the host, the port, the username and the password file of an external relay.
  • Generated secrets: one password per application, the smarthost password, and the DKIM key.

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001: the VM tests of the maddy provider, the DKIM signature and the report intake pass.
  • SC-002: the unit tests of the routing, the lldap mail, the data-only requests and the policy pass.
  • SC-003: A password reset mail from lldap or Authelia reaches an external mailbox with a passing DKIM signature.

Assumptions

  • The wiring and the setup flow are as in 005. The request and policy model is as in 001.
  • The relay is the smtp component. Its certificate comes from the ssl resource.
  • The consumers include ldap and oidc. Each sends from a noreply address on its own mail domain.
  • An operator provisions the accounts on the external smarthost and seeds their passwords by hand.