-
-}
-```
-Shortened variant for inline use:
-```
-pass_table
[table arguments] {
- [additional table config]
-}
-```
-
-Example, read username:password pair from the text file:
-```
-smtp tcp://0.0.0.0:587 {
- auth pass_table file /etc/maddy/smtp_passwd
- ...
-}
-```
-
-## Password hashes
-
-pass_table expects the used table to contain certain structured values with
-hash algorithm name, salt and other necessary parameters.
-
-You should use 'maddyctl hash' command to generate suitable values.
-See 'maddyctl hash --help' for details.
-
-## maddyctl creds
-
-If the underlying table is a "mutable" table (see maddy-tables(5)) then
-the 'maddyctl creds' command can be used to modify the underlying tables
-via pass_table module. It will act a "local credentials store" and will write
-appropriate hash values to the table.
-
-# Separate username and password lookup (auth.plain_separate)
-
-This module implements authentication using username:password pairs but can
-use zero or more "table modules" (maddy-tables(5)) and one or more
-authentication providers to verify credentials.
-
-```
-auth.plain_separate {
- user ...
- user ...
- ...
- pass ...
- pass ...
- ...
-}
-```
-
-How it works:
-- Initial username input is normalized using PRECIS UsernameCaseMapped profile.
-- Each table specified with the 'user' directive looked up using normalized
- username. If match is not found in any table, authentication fails.
-- Each authentication provider specified with the 'pass' directive is tried.
- If authentication with all providers fails - an error is returned.
-
-## Configuration directives
-
-**Syntax:** user _table module_
-
-Configuration block for any module from maddy-tables(5) can be used here.
-
-Example:
-```
-user file /etc/maddy/allowed_users
-```
-
-**Syntax:** pass _auth provider_
-
-Configuration block for any auth. provider module can be used here, even
-'plain_split' itself.
-
-The used auth. provider must provide username:password pair-based
-authentication.
-
-# Dovecot authentication client (auth.dovecot_sasl)
-
-The 'dovecot_sasl' module implements the client side of the Dovecot
-authentication protocol, allowing maddy to use it as a credentials source.
-
-Currently SASL mechanisms support is limited to mechanisms supported by maddy
-so you cannot get e.g. SCRAM-MD5 this way.
-
-```
-auth.dovecot_sasl {
- endpoint unix://socket_path
-}
-
-dovecot_sasl unix://socket_path
-```
-
-## Configuration directives
-
-*Syntax*: endpoint _schema://address_ ++
-*Default*: not set
-
-Set the address to use to contact Dovecot SASL server in the standard endpoint
-format.
-
-tcp://10.0.0.1:2222 for TCP, unix:///var/lib/dovecot/auth.sock for Unix
-domain sockets.
-
-# LDAP BindDN authentication (EXPERIMENTAL) (auth.ldap)
-
-maddy supports authentication via LDAP using DN binding. Passwords are verified
-by the LDAP server.
-
-maddy needs to know the DN to use for binding. It can be obtained either by
-directory search or template .
-
-Note that storage backends conventionally use email addresses, if you use
-non-email identifiers as usernames then you should map them onto
-emails on delivery by using auth_map (see *maddy-storage*(5)).
-
-auth.ldap also can be a used as a table module. This way you can check
-whether the account exists. It works only if DN template is not used.
-
-```
-auth.ldap {
- urls ldap://maddy.test:389
-
- # Specify initial bind credentials. Not required ('bind off')
- # if DN template is used.
- bind plain "cn=maddy,ou=people,dc=maddy,dc=test" "123456"
-
- # Specify DN template to skip lookup.
- dn_template "cn={username},ou=people,dc=maddy,dc=test"
-
- # Specify base_dn and filter to lookup DN.
- base_dn "ou=people,dc=maddy,dc=test"
- filter "(&(objectClass=posixAccount)(uid={username}))"
-
- tls_client { ... }
- starttls off
- debug off
- connect_timeout 1m
-}
-```
-```
-auth.ldap ldap://maddy.test.389 {
- ...
-}
-```
-
-## Configuration directives
-
-*Syntax:* urls _servers..._
-
-REQUIRED.
-
-URLs of the directory servers to use. First available server
-is used - no load-balancing is done.
-
-URLs should use 'ldap://', 'ldaps://', 'ldapi://' schemes.
-
-*Syntax:* bind off ++
- bind unauth ++
- bind external ++
- bind plain _username_ _password_ ++
-*Default:* off
-
-Credentials to use for initial binding. Required if DN lookup is used.
-
-'unauth' performs unauthenticated bind. 'external' performs external binding
-which is useful for Unix socket connections (ldapi://) or TLS client certificate
-authentication (cert. is set using tls_client directive). 'plain' performs a
-simple bind using provided credentials.
-
-*Syntax:* dn_template _template_
-
-DN template to use for binding. '{username}' is replaced with the
-username specified by the user.
-
-*Syntax:* base_dn _dn_
-
-Base DN to use for lookup.
-
-*Syntax:* filter _str_
-
-DN lookup filter. '{username}' is replaced with the username specified
-by the user.
-
-Example:
-```
-(&(objectClass=posixAccount)(uid={username}))
-```
-
-Example (using ActiveDirectory):
-```
-(&(objectCategory=Person)(memberOf=CN=user-group,OU=example,DC=example,DC=org)(sAMAccountName={username})(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))
-```
-
-Example:
-```
-(&(objectClass=Person)(mail={username}))
-```
-
-*Syntax:* starttls _bool_ ++
-*Default:* off
-
-Whether to upgrade connection to TLS using STARTTLS.
-
-*Syntax:* tls_client { ... }
-
-Advanced TLS client configuration. See *maddy-tls*(5) for details.
-
-*Syntax:* connect_timeout _duration_ ++
-*Default:* 1m
-
-Timeout for initial connection to the directory server.
-
-*Syntax:* request_timeout _duration_ ++
-*Default:* 1m
-
-Timeout for each request (binding, lookup).
\ No newline at end of file
diff --git a/docs/man/maddy-blob.5.scd b/docs/man/maddy-blob.5.scd
deleted file mode 100644
index dcc9a11bd..000000000
--- a/docs/man/maddy-blob.5.scd
+++ /dev/null
@@ -1,113 +0,0 @@
-maddy-blob(5) "maddy mail server" "maddy reference documentation"
-
-; TITLE Message blob storage
-
-Some IMAP storage backends support pluggable message storage that allows
-message contents to be stored separately from IMAP index.
-
-Modules described in this page are what can be used with such storage backends.
-In most cases they have to be specified using the 'msg_store' directive, like
-this:
-```
-storage.imapsql local_mailboxes {
- msg_store fs /var/lib/email
-}
-```
-
-Unless explicitly configured, storage backends with pluggable storage will
-store messages in state_dir/messages (e.g. /var/lib/maddy/messages) FS
-directory.
-
-# FS directory storage (storage.blob.fs)
-
-This module stores message bodies in a file system directory.
-
-```
-storage.blob.fs {
- root
-}
-```
-```
-storage.blob.fs
-```
-
-## Configuration directives
-
-*Syntax:* root _path_ ++
-*Default:* not set
-
-Path to the FS directory. Must be readable and writable by the server process.
-If it does not exist - it will be created (parent directory should be writable
-for this). Relative paths are interpreted relatively to server state directory.
-
-# Amazon S3 storage (storage.blob.s3)
-
-This modules stores messages bodies in a bucket on S3-compatible storage.
-
-```
-storage.blob.s3 {
- endpoint play.min.io
- secure yes
- access_key "Q3AM3UQ867SPQQA43P2F"
- secret_key "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"
- bucket maddy-test
-
- # optional
- region eu-central-1
- object_prefix maddy/
-}
-```
-
-Example:
-```
-storage.imapsql local_mailboxes {
- ...
- msg_store s3 {
- endpoint s3.amazonaws.com
- access_key "..."
- secret_key "..."
- bucket maddy-messages
- region us-west-2
- }
-}
-```
-
-## Configuration directives
-
-*Syntax:* endpoint _address:port_
-
-REQUIRED.
-
-Root S3 endpoint. e.g. s3.amazonaws.com
-
-*Syntax:* secure _boolean_ ++
-*Default:* yes
-
-Whether TLS should be used.
-
-*Syntax:* access_key _string_ ++
-*Syntax:* secret_key _string_
-
-REQUIRED.
-
-Static S3 credentials.
-
-*Syntax:* bucket _name_
-
-REQUIRED.
-
-S3 bucket name. The bucket must exist and
-be read-writable.
-
-*Syntax:* region _string_ ++
-*Default:* not set
-
-S3 bucket location. May be called "endpoint"
-in some manuals.
-
-*Syntax:* object_prefix _string_ ++
-*Default:* empty string
-
-String to add to all keys stored by maddy.
-
-Can be useful when S3 is used as a file system.
\ No newline at end of file
diff --git a/docs/man/maddy-filters.5.scd b/docs/man/maddy-filters.5.scd
deleted file mode 100644
index 6fc436311..000000000
--- a/docs/man/maddy-filters.5.scd
+++ /dev/null
@@ -1,953 +0,0 @@
-maddy-filters(5) "maddy mail server" "maddy reference documentation"
-
-; TITLE Message filtering
-
-maddy does have two distinct types of modules that do message filtering.
-"Checks" and "modifiers".
-
-"Checks" are meant to be used to reject or quarantine
-messages that are unwanted, such as potential spam or messages with spoofed
-sender address. They are limited in ways they can modify the message and their
-execution is heavily parallelized to improve performance.
-
-"Modifiers" are executed serially in order they are referenced in the
-configuration and are allowed to modify the message data and meta-data.
-
-# Check actions
-
-When a certain check module thinks the message is "bad", it takes some actions
-depending on its configuration. Most checks follow the same configuration
-structure and allow following actions to be taken on check failure:
-
-- Do nothing ('action ignore')
-
-Useful for testing deployment of new checks. Check failures are still logged
-but they have no effect on message delivery.
-
-- Reject the message ('action reject')
-
-Reject the message at connection time. No bounce is generated locally.
-
-- Quarantine the message ('action quarantine')
-
-Mark message as 'quarantined'. If message is then delivered to the local
-storage, the storage backend can place the message in the 'Junk' mailbox.
-Another thing to keep in mind that 'remote' module (see *maddy-targets*(5))
-will refuse to send quarantined messages.
-
-# Simple checks
-
-## Configuration directives
-
-Following directives are defined for all modules listed below.
-
-*Syntax*: ++
- fail_action ignore ++
- fail_action reject ++
- fail_action quarantine ++
-*Default*: quarantine
-
-Action to take when check fails. See Check actions for details.
-
-*Syntax*: debug _boolean_ ++
-*Default*: global directive value
-
-Log both sucessfull and unsucessfull check executions instead of just
-unsucessfull.
-
-## require_mx_record
-
-Check that domain in MAIL FROM command does have a MX record and none of them
-are "null" (contain a single dot as the host).
-
-By default, quarantines messages coming from servers missing MX records,
-use 'fail_action' directive to change that.
-
-## require_matching_rdns
-
-Check that source server IP does have a PTR record point to the domain
-specified in EHLO/HELO command.
-
-By default, quarantines messages coming from servers with mismatched or missing
-PTR record, use 'fail_action' directive to change that.
-
-## require_tls
-
-Check that the source server is connected via TLS; either directly, or by using
-the STARTTLS command.
-
-By default, rejects messages coming from unencrypted servers. Use the
-'fail_action' directive to change that.
-
-# DKIM authentication module (check.dkim)
-
-This is the check module that performs verification of the DKIM signatures
-present on the incoming messages.
-
-```
-check.dkim {
- debug no
- required_fields From Subject
- allow_body_subset no
- no_sig_action ignore
- broken_sig_action ignore
- fail_open no
-}
-```
-
-## Configuration directives
-
-*Syntax*: debug _boolean_ ++
-*Default*: global directive value
-
-Log both sucessfull and unsucessfull check executions instead of just
-unsucessfull.
-
-*Syntax*: required_fields _string..._ ++
-*Default*: From Subject
-
-Header fields that should be included in each signature. If signature
-lacks any field listed in that directive, it will be considered invalid.
-
-Note that From is always required to be signed, even if it is not included in
-this directive.
-
-*Syntax*: no_sig_action _action_ ++
-*Default*: ignore (recommended by RFC 6376)
-
-Action to take when message without any signature is received.
-
-Note that DMARC policy of the sender domain can request more strict handling of
-missing DKIM signatures.
-
-*Syntax*: broken_sig_action _action_ ++
-*Default*: ignore (recommended by RFC 6376)
-
-Action to take when there are not valid signatures in a message.
-
-Note that DMARC policy of the sender domain can request more strict handling of
-broken DKIM signatures.
-
-*Syntax*: fail_open _boolean_ ++
-*Default*: no
-
-Whether to accept the message if a temporary error occurs during DKIM
-verification. Rejecting the message with a 4xx code will require the sender
-to resend it later in a hope that the problem will be resolved.
-
-# SPF policy enforcement module (check.spf)
-
-This is the check module that verifies whether IP address of the client is
-authorized to send messages for domain in MAIL FROM address.
-
-```
-check.spf {
- debug no
- enforce_early no
- fail_action quarantine
- softfail_action ignore
- permerr_action reject
- temperr_action reject
-}
-```
-
-## DMARC override
-
-It is recommended by the DMARC standard to don't fail delivery based solely on
-SPF policy and always check DMARC policy and take action based on it.
-
-If enforce_early is no, check.spf module will not take any action on SPF
-policy failure if sender domain does have a DMARC record with 'quarantine' or
-'reject' policy. Instead it will rely on DMARC support to take necesary
-actions using SPF results as an input.
-
-Disabling enforce_early without enabling DMARC support will make SPF policies
-no-op and is considered insecure.
-
-## Configuration directives
-
-*Syntax*: debug _boolean_ ++
-*Default*: global directive value
-
-Enable verbose logging for check.spf.
-
-*Syntax*: enforce_early _boolean_ ++
-*Default*: no
-
-Make policy decision on MAIL FROM stage (before the message body is received).
-This makes it impossible to apply DMARC override (see above).
-
-*Syntax*: none_action reject|qurantine|ignore ++
-*Default*: ignore
-
-Action to take when SPF policy evaluates to a 'none' result.
-
-See https://tools.ietf.org/html/rfc7208#section-2.6 for meaning of
-SPF results.
-
-*Syntax*: neutral_action reject|qurantine|ignore ++
-*Default*: ignore
-
-Action to take when SPF policy evaluates to a 'neutral' result.
-
-See https://tools.ietf.org/html/rfc7208#section-2.6 for meaning of
-SPF results.
-
-*Syntax*: fail_action reject|qurantine|ignore ++
-*Default*: quarantine
-
-Action to take when SPF policy evaluates to a 'fail' result.
-
-*Syntax*: softfail_action reject|qurantine|ignore ++
-*Default*: ignore
-
-Action to take when SPF policy evaluates to a 'softfail' result.
-
-*Syntax*: permerr_action reject|qurantine|ignore ++
-*Default*: reject
-
-Action to take when SPF policy evaluates to a 'permerror' result.
-
-*Syntax*: temperr_action reject|qurantine|ignore ++
-*Default*: reject
-
-Action to take when SPF policy evaluates to a 'temperror' result.
-
-# DNSBL lookup module (check.dnsbl)
-
-The dnsbl module implements checking of source IP and hostnames against a set
-of DNS-based Blackhole lists (DNSBLs).
-
-Its configuration consists of module configuration directives and a set
-of blocks specifing lists to use and kind of lookups to perform on them.
-
-```
-check.dnsbl {
- debug no
- check_early no
-
- quarantine_threshold 1
- reject_threshold 1
-
- # Lists configuration example.
- dnsbl.example.org {
- client_ipv4 yes
- client_ipv6 no
- ehlo no
- mailfrom no
- score 1
- }
- hsrbl.example.org {
- client_ipv4 no
- client_ipv6 no
- ehlo yes
- mailfrom yes
- score 1
- }
-}
-```
-
-## Arguments
-
-Arguments specify the list of IP-based BLs to use.
-
-The following configurations are equivalent.
-
-```
-check {
- dnsbl dnsbl.example.org dnsbl2.example.org
-}
-```
-
-```
-check {
- dnsbl {
- dnsbl.example.org dnsbl2.example.org {
- client_ipv4 yes
- client_ipv6 no
- ehlo no
- mailfrom no
- score 1
- }
- }
-}
-```
-
-## Configuration directives
-
-*Syntax*: debug _boolean_ ++
-*Default*: global directive value
-
-Enable verbose logging.
-
-*Syntax*: check_early _boolean_ ++
-*Default*: no
-
-Check BLs before mail delivery starts and silently reject blacklisted clients.
-
-For this to work correctly, check should not be used in source/destination
-pipeline block.
-
-In particular, this means:
-- No logging is done for rejected messages.
-- No action is taken if quarantine_threshold is hit, only reject_threshold
- applies.
-- defer_sender_reject from SMTP configuration takes no effect.
-- MAIL FROM is not checked, even if specified.
-
-If you often get hit by spam attacks, this is recommended to enable this
-setting to save server resources.
-
-*Syntax*: quarantine_threshold _integer_ ++
-*Default*: 1
-
-DNSBL score needed (equals-or-higher) to quarantine the message.
-
-*Syntax*: reject_threshold _integer_ ++
-*Default*: 9999
-
-DNSBL score needed (equals-or-higher) to reject the message.
-
-## List configuration
-
-```
-dnsbl.example.org dnsbl.example.com {
- client_ipv4 yes
- client_ipv6 no
- ehlo no
- mailfrom no
- responses 127.0.0.1/24
- score 1
-}
-```
-
-Directive name and arguments specify the actual DNS zone to query when checking
-the list. Using multiple arguments is equivalent to specifying the same
-configuration separately for each list.
-
-*Syntax*: client_ipv4 _boolean_ ++
-*Default*: yes
-
-Whether to check address of the IPv4 clients against the list.
-
-*Syntax*: client_ipv6 _boolean_ ++
-*Default*: yes
-
-Whether to check address of the IPv6 clients against the list.
-
-*Syntax*: ehlo _boolean_ ++
-*Default*: no
-
-Whether to check hostname specified n the HELO/EHLO command
-against the list.
-
-This works correctly only with domain-based DNSBLs.
-
-*Syntax*: mailfrom _boolean_ ++
-*Default*: no
-
-Whether to check domain part of the MAIL FROM address against the list.
-
-This works correctly only with domain-based DNSBLs.
-
-*Syntax*: responses _cidr|ip..._ ++
-*Default*: 127.0.0.1/24
-
-IP networks (in CIDR notation) or addresses to permit in list lookup results.
-Addresses not matching any entry in this directives will be ignored.
-
-*Syntax*: score _integer_ ++
-*Default*: 1
-
-Score value to add for the message if it is listed.
-
-If sum of list scores is equals or higher than quarantine_threshold, the
-message will be quarantined.
-
-If sum of list scores is equals or higher than rejected_threshold, the message
-will be rejected.
-
-It is possible to specify a negative value to make list act like a whitelist
-and override results of other blocklists.
-
-# DKIM signing module (modify.dkim)
-
-modify.dkim module is a modifier that signs messages using DKIM
-protocol (RFC 6376).
-
-```
-modify.dkim {
- debug no
- domains example.org example.com
- selector default
- key_path dkim-keys/{domain}-{selector}.key
- oversign_fields ...
- sign_fields ...
- header_canon relaxed
- body_canon relaxed
- sig_expiry 120h # 5 days
- hash sha256
- newkey_algo rsa2048
-}
-```
-
-## Arguments
-
-domains and selector can be specified in arguments, so actual modify.dkim use can
-be shortened to the following:
-```
-modify {
- dkim example.org selector
-}
-```
-
-## Configuration directives
-
-*Syntax*: debug _boolean_ ++
-*Default*: global directive value
-
-Enable verbose logging.
-
-*Syntax*: domains _string list_ ++
-*Default*: not specified
-
-*REQUIRED.*
-
-ADministrative Management Domains (ADMDs) taking responsibility for messages.
-
-A key will be generated or read for each domain specified here, the key to use
-for each message will be selected based on the SMTP envelope sender. Exception
-for that is that for domain-less postmaster address and null address, the
-key for the first domain will be used. If domain in envelope sender
-does not match any of loaded keys, message will not be signed.
-
-Should be specified either as a directive or as an argument.
-
-*Syntax*: selector _string_ ++
-*Default*: not specified
-
-*REQUIRED.*
-
-Identifier of used key within the ADMD.
-Should be specified either as a directive or as an argument.
-
-*Syntax*: key_path _string_ ++
-*Default*: dkim_keys/{domain}\_{selector}.key
-
-Path to private key. It should be in PKCS#8 format wrapped in PAM encoding.
-If key does not exist, it will be generated using algorithm specified
-in newkey_algo.
-
-Placeholders '{domain}' and '{selector}' will be replaced with corresponding
-values from domain and selector directives.
-
-Additionally, keys in PKCS#1 ("RSA PRIVATE KEY") and
-RFC 5915 ("EC PRIVATE KEY") can be read by modify.dkim. Note, however that
-newly generated keys are always in PKCS#8.
-
-*Syntax*: oversign_fields _list..._ ++
-*Default*: see below
-
-Header fields that should be signed n+1 times where n is times they are
-present in the message. This makes it impossible to replace field
-value by prepending another field with the same name to the message.
-
-Fields specified here don't have to be also specified in sign_fields.
-
-Default set of oversigned fields:
-- Subject
-- To
-- From
-- Date
-- MIME-Version
-- Content-Type
-- Content-Transfer-Encoding
-- Reply-To
-- Message-Id
-- References
-- Autocrypt
-- Openpgp
-
-*Syntax*: sign_fields _list..._ ++
-*Default*: see below
-
-Header fields that should be signed n+1 times where n is times they are
-present in the message. For these fields, additional values can be prepended
-by intermediate relays, but existing values can't be changed.
-
-Default set of signed fields:
-- List-Id
-- List-Help
-- List-Unsubscribe
-- List-Post
-- List-Owner
-- List-Archive
-- Resent-To
-- Resent-Sender
-- Resent-Message-Id
-- Resent-Date
-- Resent-From
-- Resent-Cc
-
-*Syntax*: header_canon relaxed|simple ++
-*Default*: relaxed
-
-Canonicalization algorithm to use for header fields. With 'relaxed', whitespace within
-fields can be modified without breaking the signature, with 'simple' no
-modifications are allowed.
-
-*Syntax*: body_canon relaxed|simple ++
-*Default*: relaxed
-
-Canonicalization algorithm to use for message body. With 'relaxed', whitespace within
-can be modified without breaking the signature, with 'simple' no
-modifications are allowed.
-
-*Syntax*: sig_expiry _duration_ ++
-*Default*: 120h
-
-Time for which signature should be considered valid. Mainly used to prevent
-unauthorized resending of old messages.
-
-*Syntax*: hash _hash_ ++
-*Default*: sha256
-
-Hash algorithm to use when computing body hash.
-
-sha256 is the only supported algorithm now.
-
-*Syntax*: newkey_algo rsa4096|rsa2048|ed25519 ++
-*Default*: rsa2048
-
-Algorithm to use when generating a new key.
-
-*Syntax*: require_sender_match _ids..._ ++
-*Default*: envelope auth
-
-Require specified identifiers to match From header field and key domain,
-otherwise - don't sign the message.
-
-If From field contains multiple addresses, message will not be
-signed unless allow_multiple_from is also specified. In that
-case only first address will be compared.
-
-Matching is done in a case-insensitive way.
-
-Valid values:
-- off +
- Disable check, always sign.
-- envelope +
- Require MAIL FROM address to match From header.
-- auth +
- If authorization identity contains @ - then require it to
- fully match From header. Otherwise, check only local-part
- (username).
-
-*Syntax*: allow_multiple_from _boolean_ ++
-*Default*: no
-
-Allow multiple addresses in From header field for purposes of
-require_sender_match checks. Only first address will be checked, however.
-
-*Syntax*: sign_subdomains _boolean_ ++
-*Default*: no
-
-Sign emails from subdomains using a top domain key.
-
-Allows only one domain to be specified (can be workarounded using modify.dkim
-multiple times).
-
-# Envelope sender / recipient rewriting (modify.replace_sender, modify.replace_rcpt)
-
-'replace_sender' and 'replace_rcpt' modules replace SMTP envelope addresses
-based on the mapping defined by the table module (maddy-tables(5)). Currently,
-only 1:1 mappings are supported (that is, it is not possible to specify
-multiple replacements for a single address).
-
-The address is normalized before lookup (Punycode in domain-part is decoded,
-Unicode is normalized to NFC, the whole string is case-folded).
-
-First, the whole address is looked up. If there is no replacement, local-part
-of the address is looked up separately and is replaced in the address while
-keeping the domain part intact. Replacements are not applied recursively, that
-is, lookup is not repeated for the replacement.
-
-Recipients are not deduplicated after expansion, so message may be delivered
-multiple times to a single recipient. However, used delivery target can apply
-such deduplication (imapsql storage does it).
-
-Definition:
-```
-replace_rcpt
[table arguments] {
- [extended table config]
-}
-```
-
-Use examples:
-```
-modify {
- replace_rcpt file /etc/maddy/aliases
- replace_rcpt static {
- entry a@example.org b@example.org
- }
- replace_rcpt regexp "(.+)@example.net" "1ドル@example.org"
-}
-```
-
-Possible contents of /etc/maddy/aliases in the example above:
-```
-# Replace 'cat' with any domain to 'dog'.
-# E.g. cat@example.net -> dog@example.net
-cat: dog
-
-# Replace cat@example.org with cat@example.com.
-# Takes priority over the previous line.
-cat@example.org: cat@example.com
-```
-
-# System command filter (check.command)
-
-This module executes an arbitrary system command during a specified stage of
-checks execution.
-
-```
-command executable_name arg0 arg1 ... {
- run_on body
-
- code 1 reject
- code 2 quarantine
-}
-```
-
-## Arguments
-
-The module arguments specify the command to run. If the first argument is not
-an absolute path, it is looked up in the Libexec Directory (/usr/lib/maddy on
-Linux) and in $PATH (in that ordering). Note that no additional handling
-of arguments is done, especially, the command is executed directly, not via the
-system shell.
-
-There is a set of special strings that are replaced with the corresponding
-message-specific values:
-
-- {source_ip}
-
- IPv4/IPv6 address of the sending MTA.
-
-- {source_host}
-
- Hostname of the sending MTA, from the HELO/EHLO command.
-
-- {source_rdns}
-
- PTR record of the sending MTA IP address.
-
-- {msg_id}
-
- Internal message identifier. Unique for each delivery.
-
-- {auth_user}
-
- Client username, if authenticated using SASL PLAIN
-
-- {sender}
-
- Message sender address, as specified in the MAIL FROM SMTP command.
-
-- {rcpts}
-
- List of accepted recipient addresses, including the currently handled
- one.
-
-- {address}
-
- Currently handled address. This is a recipient address if the command
- is called during RCPT TO command handling ('run_on rcpt') or a sender
- address if the command is called during MAIL FROM command handling ('run_on
- sender').
-
-
-If value is undefined (e.g. {source_ip} for a message accepted over a Unix
-socket) or unavailable (the command is executed too early), the placeholder
-is replaced with an empty string. Note that it can not remove the argument.
-E.g. -i {source_ip} will not become just -i, it will be -i ""
-
-Undefined placeholders are not replaced.
-
-## Command stdout
-
-The command stdout must be either empty or contain a valid RFC 5322 header.
-If it contains a byte stream that does not look a valid header, the message
-will be rejected with a temporary error.
-
-The header from stdout will be *prepended* to the message header.
-
-## Configuration directives
-
-*Syntax*: run_on conn|sender|rcpt|body ++
-*Default*: body
-
-When to run the command. This directive also affects the information visible
-for the message.
-
-- conn
-
- Run before the sender address (MAIL FROM) is handled.
-
- *Stdin*: Empty ++
-*Available placeholders*: {source_ip}, {source_host}, {msg_id}, {auth_user}.
-
-- sender
-
- Run during sender address (MAIL FROM) handling.
-
- *Stdin*: Empty ++
-*Available placeholders*: conn placeholders + {sender}, {address}.
-
- The {address} placeholder contains the MAIL FROM address.
-
-- rcpt
-
- Run during recipient address (RCPT TO) handling. The command is executed
- once for each RCPT TO command, even if the same recipient is specified
- multiple times.
-
- *Stdin*: Empty ++
-*Available placeholders*: sender placeholders + {rcpts}.
-
- The {address} placeholder contains the recipient address.
-
-- body
-
- Run during message body handling.
-
- *Stdin*: The message header + body ++
-*Available placeholders*: all except for {address}.
-
-*Syntax*: ++
- code _integer_ ignore ++
- code _integer_ quarantine ++
- code _integer_ reject [SMTP code] [SMTP enhanced code] [SMTP message]
-
-This directives specified the mapping from the command exit code _integer_ to
-the message pipeline action.
-
-Two codes are defined implicitly, exit code 1 causes the message to be rejected
-with a permanent error, exit code 2 causes the message to be quarantined. Both
-action can be overriden using the 'code' directive.
-
-## Milter protocol check (check.milter)
-
-The 'milter' implements subset of Sendmail's milter protocol that can be used
-to integrate external software in maddy.
-
-Notable limitations of protocol implementation in maddy include:
-1. Changes of envelope sender address are not supported
-2. Removal and addition of envelope recipients is not supported
-3. Removal and replacement of header fields is not supported
-4. Headers fields can be inserted only on top
-5. Milter does not receive some "macros" provided by sendmail.
-
-Restrictions 1 and 2 are inherent to the maddy checks interface and cannot be
-removed without major changes to it. Restrictions 3, 4 and 5 are temporary due to
-incomplete implementation.
-
-```
-check.milter {
- endpoint
- fail_open false
-}
-
-milter
-```
-
-## Arguments
-
-When defined inline, the first argument specifies endpoint to access milter
-via. See below.
-
-## Configuration directives
-
-**Syntax:** endpoint _scheme://path_ ++
-**Default:** not set
-
-Specifies milter protocol endpoint to use.
-The endpoit is specified in standard URL-like format:
-'tcp://127.0.0.1:6669' or 'unix:///var/lib/milter/filter.sock'
-
-**Syntax:** fail_open _boolean_ ++
-**Default:** false
-
-Toggles behavior on milter I/O errors. If false ("fail closed") - message is
-rejected with temporary error code. If true ("fail open") - check is skipped.
-
-## rspamd check (check.rspamd)
-
-The 'rspamd' module implements message filtering by contacting the rspamd
-server via HTTP API.
-
-```
-check.rspamd {
- tls_client { ... }
- api_path http://127.0.0.1:11333
- settings_id whatever
- tag maddy
- hostname mx.example.org
- io_error_action ignore
- error_resp_action ignore
- add_header_action quarantine
- rewrite_subj_action quarantine
- flags pass_all
-}
-
-rspamd http://127.0.0.1:11333
-```
-
-## Configuration directives
-
-*Syntax:* tls_client { ... } ++
-*Default:* not set
-
-Configure TLS client if HTTPS is used, see *maddy-tls*(5) for details.
-
-*Syntax:* api_path _url_ ++
-*Default:* http://127.0.0.1:11333
-
-URL of HTTP API endpoint. Supports both HTTP and HTTPS and can include
-path element.
-
-*Syntax:* settings_id _string_ ++
-*Default:* not set
-
-Settings ID to pass to the server.
-
-*Syntax:* tag _string_ ++
-*Default:* maddy
-
-Value to send in MTA-Tag header field.
-
-*Syntax:* hostname _string_ ++
-*Default:* value of global directive
-
-Value to send in MTA-Name header field.
-
-*Syntax:* io_error_action _action_ ++
-*Default:* ignore
-
-Action to take in case of inability to contact the rspamd server.
-
-*Syntax:* error_resp_action _action_ ++
-*Default:* ignore
-
-Action to take in case of 5xx or 4xx response received from the rspamd server.
-
-*Syntax:* add_header_action _action_ ++
-*Default:* quarantine
-
-Action to take when rspamd requests to "add header".
-
-X-Spam-Flag and X-Spam-Score are added to the header irregardless of value.
-
-*Syntax:* rewrite_subj_action _action_ ++
-*Default:* quarantine
-
-Action to take when rspamd requests to "rewrite subject".
-
-X-Spam-Flag and X-Spam-Score are added to the header irregardless of value.
-
-*Syntax:* flags _string list..._ ++
-*Default:* pass_all
-
-Flags to pass to the rspamd server.
-See https://rspamd.com/doc/architecture/protocol.html for details.
-
-## MAIL FROM and From authorization (check.authorize_sender)
-
-This check verifies that envelope and header sender addresses belong
-to the authenticated user. Address ownership is established via table
-that maps each user account to a email address it is allowed to use.
-There are some special cases, see user_to_email description below.
-
-```
-check.authorize_sender {
- prepare_email identity
- user_to_email identity
- check_header yes
-
- unauth_action reject
- no_match_action reject
- malformed_action reject
- err_action reject
-
- auth_normalize precis_casefold_email
- from_normalize precis_casefold_email
-}
-```
-```
-check {
- authorize_sender { ... }
-}
-```
-
-## Configuration directives
-
-*Syntax:* user_to_email _table_ ++
-*Default:* identity
-
-Table to use for lookups. Result of the lookup should contain either the
-domain name, the full email address or "*" string. If it is just domain - user
-will be allowed to use any mailbox within a domain as a sender address.
-If result contains "*" - user will be allowed to use any address.
-
-*Syntax:* check_header _boolean_ ++
-*Default:* yes
-
-Whether to verify header sender in addition to envelope.
-
-Either Sender or From field value should match the
-authorization identity.
-
-*Syntax:* unauth_action _action_ ++
-*Default:* reject
-
-What to do if the user is not authenticated at all.
-
-*Syntax:* no_match_action _action_ ++
-*Default:* reject
-
-What to do if user is not allowed to use the sender address specified.
-
-*Syntax:* malformed_action _action_ ++
-*Default:* reject
-
-What to do if From or Sender header fields contain malformed values.
-
-*Syntax:* err_action _action_ ++
-*Default:* reject
-
-What to do if error happens during prepare_email or user_to_email lookup.
-
-*Syntax:* auth_normalize _action_ ++
-*Default:* precis_casefold_email
-
-Normalization function to apply to authorization username before
-further processing.
-
-Available options:
-- precis_casefold_email PRECIS UsernameCaseMapped profile + U-labels form for domain
-- precis_casefold PRECIS UsernameCaseMapped profile for the entire string
-- precis_email PRECIS UsernameCasePreserved profile + U-labels form for domain
-- precis PRECIS UsernameCasePreserved profile for the entire string
-- casefold Convert to lower case
-- noop Nothing
-
-*Syntax:* from_normalize _action_ ++
-*Default:* precis_casefold_email
-
-Normalization function to apply to email addresses before
-further processing.
-
-Available options are same as for auth_normalize.
\ No newline at end of file
diff --git a/docs/man/maddy-imap.5.scd b/docs/man/maddy-imap.5.scd
deleted file mode 100644
index e701a6e20..000000000
--- a/docs/man/maddy-imap.5.scd
+++ /dev/null
@@ -1,130 +0,0 @@
-maddy-imap(5) "maddy mail server" "maddy reference documentation"
-
-; TITLE IMAP endpoint module
-
-Module 'imap' is a listener that implements IMAP4rev1 protocol and provides
-access to local messages storage specified by 'storage' directive. See
-*maddy-storage*(5) for support storage backends and corresponding
-configuration options.
-
-```
-imap tcp://0.0.0.0:143 tls://0.0.0.0:993 {
- tls /etc/ssl/private/cert.pem /etc/ssl/private/pkey.key
- io_debug no
- debug no
- insecure_auth no
- auth pam
- storage &local_mailboxes
-}
-```
-
-## Configuration directives
-
-*Syntax*: tls _certificate_path_ _key_path_ { ... } ++
-*Default*: global directive value
-
-TLS certificate & key to use. Fine-tuning of other TLS properties is possible
-by specifing a configuration block and options inside it:
-```
-tls cert.crt key.key {
- protocols tls1.2 tls1.3
-}
-```
-See section 'TLS configuration' in *maddy*(1) for valid options.
-
-*Syntax*: io_debug _boolean_ ++
-*Default*: no
-
-Write all commands and responses to stderr.
-
-*Syntax*: io_errors _boolean_ ++
-*Default*: no
-
-Log I/O errors.
-
-*Syntax*: debug _boolean_ ++
-*Default*: global directive value
-
-Enable verbose logging.
-
-*Syntax*: insecure_auth _boolean_ ++
-*Default*: no (yes if TLS is disabled)
-
-*Syntax*: auth _module_reference_
-
-Use the specified module for authentication.
-*Required.*
-
-*Syntax*: storage _module_reference_
-
-Use the specified module for message storage.
-*Required.*
-
-## IMAP filters
-
-Most storage backends support application of custom code late in delivery
-process. As opposed to using SMTP pipeline modifiers or checks, it allows
-modifying IMAP-specific message attributes. In particular, it allows
-code to change target folder and add IMAP flags (keywords) to the message.
-
-There is no way to reject message using IMAP filters, this should be done
-eariler in SMTP pipeline logic. Quarantined messages are not processed
-by IMAP filters and are unconditionally delivered to Junk folder (or other
-folder with \Junk special-use attribute).
-
-To use an IMAP filter, specify it in the 'imap_filter' directive for the
-used storage backend, like this:
-```
-storage.imapsql local_mailboxes {
- ...
-
- imap_filter {
- command /etc/maddy/sieve.sh {account_name}
- }
-}
-```
-
-## System command filter (imap.filter.command)
-
-This filter is similar to check.command module described in *maddy-filters*(5)
-and runs system command
-
-Usage:
-```
-command executable_name args... { }
-```
-
-Same as check.command, following placeholders are supported for command
-arguments: {source_ip}, {source_host}, {source_rdns}, {msg_id}, {auth_user},
-{sender}. Note: placeholders in command name are not processed to avoid
-possible command injection attacks.
-
-Additionally, for imap.filter.command, {account_name} placeholder is replaced
-with effective IMAP account name.
-
-Note that if you use provided systemd units on Linux, maddy executable is
-sandboxed - all commands will be executed with heavily restricted filesystem
-acccess and other privileges. Notably, /tmp is isolated and all directories
-except for /var/lib/maddy and /run/maddy are read-only. You will need to modify
-systemd unit if your command needs more privileges.
-
-Command output should consist of zero or more lines. First one, if non-empty, overrides
-destination folder. All other lines contain additional IMAP flags to add
-to the message. If command wants to add flags without changing folder - first
-line should be empty.
-
-It is valid for command to not write anything to stdout. In this case its
-execution will have no effect on delivery.
-
-Output example:
-```
-Junk
-```
-In this case, message will be placed in the Junk folder.
-
-```
-
-$Label1
-```
-In this case, message will be placed in inbox and will have
-'$Label1' added.
diff --git a/docs/man/maddy-smtp.5.scd b/docs/man/maddy-smtp.5.scd
deleted file mode 100644
index 899ae044f..000000000
--- a/docs/man/maddy-smtp.5.scd
+++ /dev/null
@@ -1,642 +0,0 @@
-maddy-smtp(5) "maddy mail server" "maddy reference documentation"
-
-; TITLE SMTP endpoint module
-
-# SMTP endpoint module (smtp)
-
-Module 'smtp' is a listener that implements ESMTP protocol with optional
-authentication, LMTP and Submission support. Incoming messages are processed in
-accordance with pipeline rules (explained in Message pipeline section below).
-
-```
-smtp tcp://0.0.0.0:25 {
- hostname example.org
- tls /etc/ssl/private/cert.pem /etc/ssl/private/pkey.key
- io_debug no
- debug no
- insecure_auth no
- read_timeout 10m
- write_timeout 1m
- max_message_size 32M
- max_header_size 1M
- auth pam
- defer_sender_reject yes
- dmarc yes
- smtp_max_line_length 4000
- limits {
- endpoint rate 10
- endpoint concurrency 500
- }
-
- # Example pipeline ocnfiguration.
- destination example.org {
- deliver_to &local_mailboxes
- }
- default_destination {
- reject
- }
-}
-```
-
-## Configuration directives
-
-*Syntax*: hostname _string_ ++
-*Default*: global directive value
-
-Server name to use in SMTP banner.
-
-```
-220 example.org ESMTP Service Ready
-```
-
-*Syntax*: tls _certificate_path_ _key_path_ { ... } ++
-*Default*: global directive value
-
-TLS certificate & key to use. Fine-tuning of other TLS properties is possible
-by specifing a configuration block and options inside it:
-```
-tls cert.crt key.key {
- protocols tls1.2 tls1.3
-}
-```
-See section 'TLS configuration' in *maddy*(1) for valid options.
-
-*Syntax*: io_debug _boolean_ ++
-*Default*: no
-
-Write all commands and responses to stderr.
-
-*Syntax*: debug _boolean_ ++
-*Default*: global directive value
-
-Enable verbose logging.
-
-*Syntax*: insecure_auth _boolean_ ++
-*Default*: no (yes if TLS is disabled)
-
-Allow plain-text authentication over unencrypted connections. Not recommended!
-
-*Syntax*: read_timeout _duration_ ++
-*Default*: 10m
-
-I/O read timeout.
-
-*Syntax*: write_timeout _duration_ ++
-*Default*: 1m
-
-I/O write timeout.
-
-*Syntax*: max_message_size _size_ ++
-*Default*: 32M
-
-Limit the size of incoming messages to 'size'.
-
-*Syntax*: max_header_size _size_ ++
-*Default*: 1M
-
-Limit the size of incoming message headers to 'size'.
-
-*Syntax*: auth _module_reference_ ++
-*Default*: not specified
-
-Use the specified module for authentication.
-
-*Syntax*: defer_sender_reject _boolean_ ++
-*Default*: yes
-
-Apply sender-based checks and routing logic when first RCPT TO command
-is received. This allows maddy to log recipient address of the rejected
-message and also improves interoperability with (improperly implemented)
-clients that don't expect an error early in session.
-
-*Syntax*: max_logged_rcpt_errors _integer_ ++
-*Default*: 5
-
-Amount of RCPT-time errors that should be logged. Further errors will be
-handled silently. This is to prevent log flooding during email dictonary
-attacks (address probing).
-
-*Syntax*: max_received _integer_ ++
-*Default*: 50
-
-Max. amount of Received header fields in the message header. If the incoming
-message has more fields than this number, it will be rejected with the permanent error
-5.4.6 ("Routing loop detected").
-
-*Syntax*: ++
- buffer ram ++
- buffer fs _[path]_ ++
- buffer auto _max_size_ _[path]_ ++
-*Default*: auto 1M StateDirectory/buffer
-
-Temporary storage to use for the body of accepted messages.
-
-- ram
-
-Store the body in RAM.
-
-- fs
-
-Write out the message to the FS and read it back as needed.
-_path_ can be omitted and defaults to StateDirectory/buffer.
-
-- auto
-
-Store message bodies smaller than _max_size_ entirely in RAM, otherwise write
-them out to the FS.
-_path_ can be omitted and defaults to StateDirectory/buffer.
-
-*Syntax*: smtp_max_line_length _integer_ ++
-*Default*: 4000
-
-The maximum line length allowed in the SMTP input stream. If client sends a
-longer line - connection will be closed and message (if any) will be rejected
-with a permanent error.
-
-RFC 5321 has the recommended limit of 998 bytes. Servers are not required
-to handle longer lines correctly but some senders may produce them.
-
-Unless BDAT extension is used by the sender, this limitation also applies to
-the message body.
-
-*Syntax*: dmarc _boolean_ ++
-*Default*: yes
-
-Enforce sender's DMARC policy. Due to implementation limitations, it is not a
-check module.
-
-*NOTE*: Report generation is not implemented now.
-
-*NOTE*: DMARC needs SPF and DKIM checks to function correctly.
-Without these, DMARC check will not run.
-
-## Rate & concurrency limiting
-
-*Syntax*: limits _config block_ ++
-*Default*: no limits
-
-This allows configuring a set of message flow restrictions including
-max. concurrency and rate per-endpoint, per-source, per-destination.
-
-Limits are specified as directives inside the block:
-```
-limits {
- all rate 20
- destination concurrency 5
-}
-```
-
-Supported limits:
-
-- Rate limit
-
-*Syntax*: _scope_ rate _burst_ _[period]_ ++
-Restrict the amount of messages processed in _period_ to _burst_ messages.
-If period is not specified, 1 second is used.
-
-- Concurrency limit
-
-*Syntax*: _scope_ concurrency _max_ ++
-Restrict the amount of messages processed in parallel to _max_.
-
-For each supported limitation, _scope_ determines whether it should be applied
-for all messages ("all"), per-sender IP ("ip"), per-sender domain ("source") or
-per-recipient domain ("destination"). Having a scope other than "all" means
-that the restriction will be enforced independently for each group determined
-by scope. E.g. "ip rate 20" means that the same IP cannot send more than 20
-messages in a scond. "destination concurrency 5" means that no more than 5
-messages can be sent in parallel to a single domain.
-
-*Note*: At the moment, SMTP endpoint on its own does not support per-recipient
-limits. They will be no-op. If you want to enforce a per-recipient restriction
-on outbound messages, do so using 'limits' directive for the 'remote' module
-(see *maddy-targets*(5)).
-
-It is possible to share limit counters between multiple endpoints (or any other
-modules). To do so define a top-level configuration block for module "limits"
-and reference it where needed using standard & syntax. E.g.
-```
-limits inbound_limits {
- all rate 20
-}
-
-smtp smtp://0.0.0.0:25 {
- limits &inbound_limits
- ...
-}
-
-submission tls://0.0.0.0:465 {
- limits &inbound_limits
- ...
-}
-```
-Using an "all rate" restriction in such way means that no more than 20
-messages can enter the server through both endpoints in one second.
-
-# Submission module (submission)
-
-Module 'submission' implements all functionality of the 'smtp' module and adds
-certain message preprocessing on top of it, additionaly authentication is
-always required.
-
-'submission' module checks whether addresses in header fields From, Sender, To,
-Cc, Bcc, Reply-To are correct and adds Message-ID and Date if it is missing.
-
-```
-submission tcp://0.0.0.0:587 tls://0.0.0.0:465 {
- # ... same as smtp ...
-}
-```
-
-# LMTP module (lmtp)
-
-Module 'lmtp' implements all functionality of the 'smtp' module but uses
-LMTP (RFC 2033) protocol.
-
-```
-lmtp unix://lmtp.sock {
- # ... same as smtp ...
-}
-```
-
-## Limitations of LMTP implementation
-
-- Can't be used with TCP.
-
-- Per-recipient status is not supported.
-
-- Delivery to 'sql' module storage is always atomic, either all recipients will
- succeed or none of them will.
-
-# Mesage pipeline
-
-Message pipeline is a set of module references and associated rules that
-describe how to handle messages.
-
-The pipeline is responsible for
-- Running message filters (called "checks"), (e.g. DKIM signature verification,
- DNSBL lookup and so on).
-
-- Running message modifiers (e.g. DKIM signature creation).
-
-- Assocating each message recipient with one or more delivery targets.
- Delivery target is a module that does final processing (delivery) of the
- message.
-
-Message handling flow is as follows:
-- Execute checks referenced in top-level 'check' blocks (if any)
-
-- Execute modifiers referenced in top-level 'modify' blocks (if any)
-
-- If there are 'source' blocks - select one that matches message sender (as
- specified in MAIL FROM). If there are no 'source' blocks - entire
- configuration is assumed to be the 'default_source' block.
-
-- Execute checks referenced in 'check' blocks inside selected 'source' block
- (if any).
-
-- Execute modifiers referenced in 'modify' blocks inside selected 'source'
- block (if any).
-
-Then, for each recipient:
-- Select 'destination' block that matches it. If there are
- no 'destination' blocks - entire used 'source' block is interpreted as if it
- was a 'default_destination' block.
-
-- Execute checks referenced in 'check' block inside selected 'destination' block
- (if any).
-
-- Execute modifiers referenced in 'modify' block inside selected 'destination'
- block (if any).
-
-- If used block contains 'reject' directive - reject the recipient with
- specified SMTP status code.
-
-- If used block contains 'deliver_to' directive - pass the message to the
- specified target module. Only recipients that are handled
- by used block are visible to the target.
-
-Each recipient is handled only by a single 'destination' block, in case of
-overlapping 'destination' - first one takes priority.
-```
-destination example.org {
- deliver_to targetA
-}
-destination example.org { # ambiguous and thus not allowed
- deliver_to targetB
-}
-```
-Same goes for 'source' blocks, each message is handled only by a single block.
-
-Each recipient block should contain at least one 'deliver_to' directive or
-'reject' directive. If 'destination' blocks are used, then
-'default_destination' block should also be used to specify behavior for
-unmatched recipients. Same goes for source blocks, 'default_source' should be
-used if 'source' is used.
-
-That is, pipeline configuration should explicitly specify behavior for each
-possible sender/recipient combination.
-
-Additionally, directives that specify final handling decision ('deliver_to',
-'reject') can't be used at the same level as source/destination rules.
-Consider example:
-```
-destination example.org {
- deliver_to local_mboxes
-}
-reject
-```
-It is not obvious whether 'reject' applies to all recipients or
-just for non-example.org ones, hence this is not allowed.
-
-Complete configuration example using all of the mentioned directives:
-```
-check {
- # Run a check to make sure source SMTP server identification
- # is legit.
- require_matching_ehlo
-}
-
-# Messages coming from senders at example.org will be handled in
-# accordance with the following configuration block.
-source example.org {
- # We are example.com, so deliver all messages with recipients
- # at example.com to our local mailboxes.
- destination example.com {
- deliver_to &local_mailboxes
- }
-
- # We don't do anything with recipients at different domains
- # because we are not an open relay, thus we reject them.
- default_destination {
- reject 521 5.0.0 "User not local"
- }
-}
-
-# We do our business only with example.org, so reject all
-# other senders.
-default_source {
- reject
-}
-```
-
-## Directives
-
-*Syntax*: check _block name_ { ... } ++
-*Context*: pipeline configuration, source block, destination block
-
-List of the module references for checks that should be executed on
-messages handled by block where 'check' is placed in.
-
-Note that message body checks placed in destination block are currently
-ignored. Due to the way SMTP protocol is defined, they would cause message to
-be rejected for all recipients which is not what you usually want when using
-such configurations.
-
-Example:
-```
-check {
- # Reference implicitly defined default configuration for check.
- require_matching_ehlo
-
- # Inline definition of custom config.
- require_source_mx {
- # Configuration for require_source_mx goes here.
- fail_action reject
- }
-}
-```
-
-It is also possible to define the block of checks at the top level
-as "checks" module and reference it using & syntax. Example:
-```
-checks inbound_checks {
- require_matching_ehlo
-}
-
-# ... somewhere else ...
-{
- ...
- check &inbound_checks
-}
-```
-
-*Syntax*: modify { ... } ++
-*Default*: not specified ++
-*Context*: pipeline configuration, source block, destination block
-
-List of the module references for modifiers that should be executed on
-messages handled by block where 'modify' is placed in.
-
-Message modifiers are similar to checks with the difference in that checks
-purpose is to verify whether the message is legitimate and valid per local
-policy, while modifier purpose is to post-process message and its metadata
-before final delivery.
-
-For example, modifier can replace recipient address to make message delivered
-to the different mailbox or it can cryptographically sign outgoing message
-(e.g. using DKIM). Some modifier can perform multiple unrelated modifications
-on the message.
-
-*Note*: Modifiers that affect source address can be used only globally or on
-per-source basis, they will be no-op inside destination blocks. Modifiers that
-affect the message header will affect it for all recipients.
-
-It is also possible to define the block of modifiers at the top level
-as "modiifers" module and reference it using & syntax. Example:
-```
-modifiers local_modifiers {
- replace_rcpt file /etc/maddy/aliases
-}
-
-# ... somewhere else ...
-{
- ...
- modify &local_modifiers
-}
-```
-
-*Syntax*: ++
- reject _smtp_code_ _smtp_enhanced_code_ _error_description_ ++
- reject _smtp_code_ _smtp_enhanced_code_ ++
- reject _smtp_code_ ++
- reject ++
-*Context*: destination block
-
-Messages handled by the configuration block with this directive will be
-rejected with the specified SMTP error.
-
-If you aren't sure which codes to use, use 541 and 5.4.0 with your message or
-just leave all arguments out, the error description will say "message is
-rejected due to policy reasons" which is usually what you want to mean.
-
-'reject' can't be used in the same block with 'deliver_to' or
-'destination/source' directives.
-
-Example:
-```
-reject 541 5.4.0 "We don't like example.org, go away"
-```
-
-*Syntax*: deliver_to _target-config-block_ ++
-*Context*: pipeline configuration, source block, destination block
-
-Deliver the message to the referenced delivery target. What happens next is
-defined solely by used target. If deliver_to is used inside 'destination'
-block, only matching recipients will be passed to the target.
-
-*Syntax*: source_in _table reference_ { ... } ++
-*Context*: pipeline configuration
-
-Handle messages with envelope senders present in the specified table in
-accordance with the specified configuration block.
-
-Takes precedence over all 'sender' directives.
-
-Example:
-```
-source_in file /etc/maddy/banned_addrs {
- reject 550 5.7.0 "You are not welcome here"
-}
-source example.org {
- ...
-}
-...
-```
-
-See 'destination_in' documentation for note about table configuration.
-
-*Syntax*: source _rules..._ { ... } ++
-*Context*: pipeline configuration
-
-Handle messages with MAIL FROM value (sender address) matching any of the rules
-in accordance with the specified configuration block.
-
-"Rule" is either a domain or a complete address. In case of overlapping
-'rules', first one takes priority. Matching is case-insensitive.
-
-Example:
-```
-# All messages coming from example.org domain will be delivered
-# to local_mailboxes.
-source example.org {
- deliver_to &local_mailboxes
-}
-# Messages coming from different domains will be rejected.
-default_source {
- reject 521 5.0.0 "You were not invited"
-}
-```
-
-*Syntax*: reroute { ... } ++
-*Context*: pipeline configuration, source block, destination block
-
-This directive allows to make message routing decisions based on the
-result of modifiers. The block can contain all pipeline directives and they
-will be handled the same with the exception that source and destination rules
-will use the final recipient and sender values (e.g. after all modifiers are
-applied).
-
-Here is the concrete example how it can be useful:
-```
-destination example.org {
- modify {
- replace_rcpt file /etc/maddy/aliases
- }
- reroute {
- destination example.org {
- deliver_to &local_mailboxes
- }
- default_destination {
- deliver_to &remote_queue
- }
- }
-}
-```
-
-This configuration allows to specify alias local addresses to remote ones
-without being an open relay, since remote_queue can be used only if remote
-address was introduced as a result of rewrite of local address.
-
-*WARNING*: If you have DMARC enabled (default), results generated by SPF
-and DKIM checks inside a reroute block *will not* be considered in DMARC
-evaluation.
-
-*Syntax*: destination_in _table reference_ { ... } ++
-*Context*: pipeline configuration, source block
-
-Handle messages with envelope recipients present in the specified table in
-accordance with the specified configuration block.
-
-Takes precedence over all 'destination' directives.
-
-Example:
-```
-destination_in file /etc/maddy/remote_addrs {
- deliver_to smtp tcp://10.0.0.7:25
-}
-destination example.com {
- deliver_to &local_mailboxes
-}
-...
-```
-
-Note that due to the syntax restrictions, it is not possible to specify
-extended configuration for table module. E.g. this is not valid:
-```
-destination_in sql_table {
- dsn ...
- driver ...
-} {
- deliver_to whatever
-}
-```
-
-In this case, configuration should be specified separately and be referneced
-using '&' syntax:
-```
-table.sql_table remote_addrs {
- dsn ...
- driver ...
-}
-
-whatever {
- destination_in &remote_addrs {
- deliver_to whatever
- }
-}
-```
-
-*Syntax*: destination _rule..._ { ... } ++
-*Context*: pipeline configuration, source block
-
-Handle messages with RCPT TO value (recipient address) matching any of the
-rules in accordance with the specified configuration block.
-
-"Rule" is either a domain or a complete address. Duplicate rules are not
-allowed. Matching is case-insensitive.
-
-Note that messages with multiple recipients are split into multiple messages if
-they have recipients matched by multiple blocks. Each block will see the
-message only with recipients matched by its rules.
-
-Example:
-```
-# Messages with recipients at example.com domain will be
-# delivered to local_mailboxes target.
-destination example.com {
- deliver_to &local_mailboxes
-}
-
-# Messages with other recipients will be rejected.
-default_destination {
- rejected 541 5.0.0 "User not local"
-}
-```
-
-## Reusable pipeline parts (msgpipeline module)
-
-The message pipeline can be used independently of the SMTP module in other
-contexts that require a delivery target.
-
-Full pipeline functionality can be used where a delivery target is expected.
diff --git a/docs/man/maddy-storage.5.scd b/docs/man/maddy-storage.5.scd
deleted file mode 100644
index c22f9cf35..000000000
--- a/docs/man/maddy-storage.5.scd
+++ /dev/null
@@ -1,201 +0,0 @@
-maddy-targets(5) "maddy mail server" "maddy reference documentation"
-
-; TITLE Storage backends
-
-maddy storage interface is built with IMAP in mind and directly represents
-IMAP data model. That is, maddy storage does have the concept of folders,
-flags, message UIDs, etc defined as in RFC 3501.
-
-This man page lists supported storage backends along with supported
-configuration directives for each.
-
-Most likely, you are going to use modules listed here in 'storage' directive
-for IMAP endpoint module (see *maddy-imap*(5)).
-
-In most cases, local storage modules will auto-create accounts when they are
-accessed via IMAP. This relies on authentication provider used by IMAP endpoint
-to provide what essentially is access control. There is a caveat, however: this
-auto-creation will not happen when delivering incoming messages via SMTP as
-there is no authentication to confirm that this account should indeed be
-created.
-
-# SQL-based database module (storage.imapsql)
-
-The imapsql module implements database for IMAP index and message
-metadata using SQL-based relational database.
-
-Message contents are stored in an "external store" defined by msg_store
-directive. By default this is a file system directory under /var/lib/maddy.
-
-Supported RDBMS:
-- SQLite 3.25.0
-- PostgreSQL 9.6 or newer
-
-Account names are required to have the form of a email address and are
-case-insensitive. UTF-8 names are supported with restrictions defined in the
-PRECIS UsernameCaseMapped profile.
-
-```
-storage.imapsql {
- driver sqlite3
- dsn imapsql.db
- msg_store fs messages/
-}
-```
-
-imapsql module also can be used as a lookup table (*maddy-table*(5)).
-It returns empty string values for existing usernames. This might be useful
-with destination_in directive (*maddy-smtp*(5)) e.g. to implement catch-all
-addresses (this is a bad idea to do so, this is just an example):
-```
-destination_in &local_mailboxes {
- deliver_to &local_mailboxes
-}
-destination example.org {
- modify {
- replace_rcpt regexp ".*" "catchall@example.org"
- }
- deliver_to &local_mailboxes
-}
-```
-
-
-## Arguments
-
-Specify the driver and DSN.
-
-## Configuration directives
-
-*Syntax*: driver _string_ ++
-*Default*: not specified
-
-REQUIRED.
-
-Use a specified driver to communicate with the database. Supported values:
-sqlite3, postgres.
-
-Should be specified either via an argument or via this directive.
-
-*Syntax*: dsn _string_ ++
-*Default*: not specified
-
-REQUIRED.
-
-Data Source Name, the driver-specific value that specifies the database to use.
-
-For SQLite3 this is just a file path.
-For PostgreSQL: https://godoc.org/github.com/lib/pq#hdr-Connection_String_Parameters
-
-Should be specified either via an argument or via this directive.
-
-*Syntax*: msg_store _store_ ++
-*Default*: fs messages/
-
-Module to use for message bodies storage.
-
-See *maddy-blob*(5) for details.
-
-*Syntax*: ++
- compression off ++
- compression _algorithm_ ++
- compression _algorithm_ _level_ ++
-*Default*: off
-
-Apply compression to message contents.
-Supported algorithms: lz4, zstd.
-
-*Syntax*: appendlimit _size_ ++
-*Default*: 32M
-
-Don't allow users to add new messages larger than 'size'.
-
-This does not affect messages added when using module as a delivery target.
-Use 'max_message_size' directive in SMTP endpoint module to restrict it too.
-
-*Syntax*: debug _boolean_ ++
-*Default*: global directive value
-
-Enable verbose logging.
-
-*Syntax*: junk_mailbox _name_ ++
-*Default*: Junk
-
-The folder to put quarantined messages in. Thishis setting is not used if user
-does have a folder with "Junk" special-use attribute.
-
-*Syntax*: sqlite_exclusive_lock _boolean_ ++
-*Default*: no
-
-SQLite-specific performance tuning option. Slightly decereases ovehead of
-DB locking at cost of making DB inaccessible for other processes (including
-maddyctl utility).
-
-*Syntax*: sqlite_cache_size _integer_ ++
-*Default*: defined by SQLite
-
-SQLite page cache size. If positive - specifies amount of pages (1 page - 4
-KiB) to keep in cache. If negative - specifies approximate upper bound
-of cache size in KiB.
-
-*Syntax*: sqlite_busy_timeout _integer_ ++
-*Default*: 5000000
-
-SQLite-specific performance tuning option. Amount of milliseconds to wait
-before giving up on DB lock.
-
-*Syntax*: imap_filter { ... } ++
-*Default*: not set
-
-Specifies IMAP filters to apply for messages delivered from SMTP pipeline.
-
-See *maddy-imap*(5) for filter modules usable here.
-
-Ex.
-```
-imap_filter {
- command /etc/maddy/sieve.sh {account_name}
-}
-```
-
-*Syntax:* delivery_map *table* ++
-*Default:* identity
-
-Use specified table module (*maddy-tables*(5)) to map recipient
-addresses from incoming messages to mailbox names.
-
-Normalization algorithm specified in delivery_normalize is appied before
-delivery_map.
-
-*Syntax:* delivery_normalize _name_ ++
-*Default:* precis_casefold_email
-
-Normalization function to apply to email addresses before mapping them
-to mailboxes.
-
-See auth_normalize.
-
-*Syntax*: auth_map *table* ++
-*Default*: identity
-
-Use specified table module (*maddy-tables*(5)) to map authentication
-usernames to mailbox names.
-
-Normalization algorithm specified in auth_normalize is applied before
-auth_map.
-
-*Syntax*: auth_normalize _name_ ++
-*Default*: precis_casefold_email
-
-Normalization function to apply to authentication usernames before mapping
-them to mailboxes.
-
-Available options:
-- precis_casefold_email PRECIS UsernameCaseMapped profile + U-labels form for domain
-- precis_casefold PRECIS UsernameCaseMapped profile for the entire string
-- precis_email PRECIS UsernameCasePreserved profile + U-labels form for domain
-- precis PRECIS UsernameCasePreserved profile for the entire string
-- casefold Convert to lower case
-- noop Nothing
-
-Note: On message delivery, recipient address is unconditionally normalized
-using precis_casefold_email function.
diff --git a/docs/man/maddy-tables.5.scd b/docs/man/maddy-tables.5.scd
deleted file mode 100644
index 19a27e0e0..000000000
--- a/docs/man/maddy-tables.5.scd
+++ /dev/null
@@ -1,314 +0,0 @@
-maddy-tables(5) "maddy mail server" "maddy reference documentation"
-
-; TITLE String-string translation
-
-Whenever you need to replace one string with another when handling anything in
-maddy, you can use any of the following modules to obtain the replacement
-string. They are commonly called "table modules" or just "tables".
-
-Some table modules implement write options allowing other maddy modules to
-change the source of data, effectively turning the table into a complete
-interface to a key-value store for maddy. Such tables are referred to as
-"mutable tables".
-
-# File mapping (table.file)
-
-This module builds string-string mapping from a text file.
-
-File is reloaded every 15 seconds if there are any changes (detected using
-modification time). No changes are applied if file contains syntax errors.
-
-Definition:
-```
-file
-```
-or
-```
-file {
- file
-}
-```
-
-Usage example:
-```
-# Resolve SMTP address aliases using text file mapping.
-modify {
- replace_rcpt file /etc/maddy/aliases
-}
-```
-
-## Syntax
-
-Better demonstrated by examples:
-
-```
-# Lines starting with # are ignored.
-
-# And so are lines only with whitespace.
-
-# Whenever 'aaa' is looked up, return 'bbb'
-aaa: bbb
-
- # Trailing and leading whitespace is ignored.
- ccc: ddd
-
-# If there is no colon, the string is translated into ""
-# That is, the following line is equivalent to
-# aaa:
-aaa
-
-# If the same key is used multiple times - table.file will return
-# multiple values when queries. Note that this is not used by
-# most modules. E.g. replace_rcpt does not (intentionally) support
-# 1-to-N alias expansion.
-ddd: firstvalue
-ddd: secondvalue
-```
-
-# SQL query mapping (table.sql_query)
-
-The sql_query module implements table interface using SQL queries.
-
-Definition:
-```
-table.sql_query {
- driver
- dsn
- lookup
-
- # Optional:
- init
- list
- add
- del
- set
-}
-```
-
-Usage example:
-```
-# Resolve SMTP address aliases using PostgreSQL DB.
-modify {
- replace_rcpt sql_query {
- driver postgres
- dsn "dbname=maddy user=maddy"
- lookup "SELECT alias FROM aliases WHERE address = 1ドル"
- }
-}
-```
-
-## Configuration directives
-
-**Syntax**: driver _driver name_ ++
-**REQUIRED**
-
-Driver to use to access the database.
-
-Supported drivers: postgres, sqlite3 (if compiled with C support)
-
-**Syntax**: dsn _data source name_ ++
-**REQUIRED**
-
-Data Source Name to pass to the driver. For SQLite3 this is just a path to DB
-file. For Postgres, see
-https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection_String_Parameters
-
-**Syntax**: lookup _query_ ++
-**REQUIRED**
-
-SQL query to use to obtain the lookup result.
-
-It will get one named argument containing the lookup key. Use :key
-placeholder to access it in SQL. The result row set should contain one row, one
-column with the string that will be used as a lookup result. If there are more
-rows, they will be ignored. If there are more columns, lookup will fail. If
-there are no rows, lookup returns "no results". If there are any error - lookup
-will fail.
-
-**Syntax**: init _queries..._ ++
-**Default**: empty
-
-List of queries to execute on initialization. Can be used to configure RDBMS.
-
-Example, to improve SQLite3 performance:
-```
-table.sql_query {
- driver sqlite3
- dsn whatever.db
- init "PRAGMA journal_mode=WAL" \
- "PRAGMA synchronous=NORMAL"
- lookup "SELECT alias FROM aliases WHERE address = 1ドル"
-}
-```
-
-*Syntax:* named_args _boolean_ ++
-*Default:* yes
-
-Whether to use named parameters binding when executing SQL queries
-or not.
-
-Note that maddy's PostgreSQL driver does not support named parameters and
-SQLite3 driver has issues handling numbered parameters:
-https://github.com/mattn/go-sqlite3/issues/472
-
-**Syntax:** add _query_ ++
-**Syntax:** list _query_ ++
-**Syntax:** set _query_ ++
-**Syntax:** del _query_ ++
-**Default:** none
-
-If queries are set to implement corresponding table operations - table becomes
-"mutable" and can be used in contexts that require writable key-value store.
-
-'add' query gets :key, :value named arguments - key and value strings to store.
-They should be added to the store. The query *should* not add multiple values
-for the same key and *should* fail if the key already exists.
-
-'list' query gets no arguments and should return a column with all keys in
-the store.
-
-'set' query gets :key, :value named arguments - key and value and should replace the existing
-entry in the database.
-
-'del' query gets :key argument - key and should remove it from the database.
-
-If named_args is set to "no" - key is passed as the first numbered parameter
-(1ドル), value is passed as the second numbered parameter (2ドル).
-
-# Static table (table.static)
-
-The 'static' module implements table lookups using key-value pairs in its
-configuration.
-
-```
-table.static {
- entry KEY1 VALUE1
- entry KEY2 VALUE2
- ...
-}
-```
-
-## Configuration directives
-
-**Syntax**: entry _key_ _value_
-
-Add an entry to the table.
-
-If the same key is used multiple times, the last one takes effect.
-
-# Regexp rewrite table (table.regexp)
-
-The 'regexp' module implements table lookups by applying a regular expression
-to the key value. If it matches - 'replacement' value is returned with $N
-placeholders being replaced with corresponding capture groups from the match.
-Otherwise, no value is returned.
-
-The regular expression syntax is the subset of PCRE. See
-https://golang.org/pkg/regexp/syntax/ for details.
-
-```
-table.regexp [replacement] {
- full_match yes
- case_insensitive yes
- expand_placeholders yes
-}
-```
-
-Note that [replacement] is optional. If it is not included - table.regexp
-will return the original string, therefore acting as a regexp match check.
-This can be useful in combination in destination_in (*maddy-smtp*(5)) for
-advanced matching:
-```
-destination_in regexp ".*-bounce+.*@example.com" {
- ...
-}
-```
-
-## Configuration directives
-
-**Syntax**: full_match _boolean_ ++
-**Default**: yes
-
-Whether to implicitly add start/end anchors to the regular expression.
-That is, if 'full_match' is yes, then the provided regular expression should
-match the whole string. With no - partial match is enough.
-
-**Syntax**: case_insensitive _boolean_ ++
-**Default**: yes
-
-Whether to make matching case-insensitive.
-
-**Syntax**: expand_placeholders _boolean_ ++
-**Default**: yes
-
-Replace '$name' and '${name}' in the replacement string with contents of
-corresponding capture groups from the match.
-
-To insert a literal $ in the output, use $$ in the template.
-
-# Identity table (table.identity)
-
-The module 'identity' is a table module that just returns the key looked up.
-
-```
-table.identity { }
-```
-
-# No-op table (dummy)
-
-The module 'dummy' represents an empty table.
-
-```
-dummy { }
-```
-
-# Email local part (table.email_localpart)
-
-The module 'email_localpart' extracts and unescaped local ("username") part
-of the email address.
-
-E.g.
-test@example.org => test
-"test @ a"@example.org => test @ a
-
-```
-table.email_localpart { }
-```
-
-# Table chaining module (table.chain)
-
-The table.chain module allows chaining together multiple table modules
-by using value returned by a previous table as an input for the second
-table.
-
-Example:
-```
-table.chain {
- step regexp "(.+)(\\+[^+"@]+)?@example.org" "1ドル@example.org"
- step file /etc/maddy/emails
-}
-```
-This will strip +prefix from mailbox before looking it up
-in /etc/maddy/emails list.
-
-## Configuration directives
-
-*Syntax*: step _table_
-
-Adds a table module to the chain. If input value is not in the table
-(e.g. file) - return "not exists" error.
-
-*Syntax*: optional_step _table_
-
-Same as step but if input value is not in the table - it is passed to the
-next step without changes.
-
-Example:
-Something like this can be used to map emails to usernames
-after translating them via aliases map:
-```
-table.chain {
- optional_step file /etc/maddy/aliases
- step regexp "(.+)@(.+)" "1ドル"
-}
-```
diff --git a/docs/man/maddy-targets.5.scd b/docs/man/maddy-targets.5.scd
deleted file mode 100644
index eddcc0cca..000000000
--- a/docs/man/maddy-targets.5.scd
+++ /dev/null
@@ -1,468 +0,0 @@
-maddy-targets(5) "maddy mail server" "maddy reference documentation"
-
-; TITLE Delivery targets
-
-This man page describes modules that can used with 'deliver_to' directive
-of SMTP endpoint module.
-
-# SQL module (target.imapsql)
-
-SQL module described in *maddy-storage*(5) can also be used as a delivery
-target.
-
-# Queue module (target.queue)
-
-Queue module buffers messages on disk and retries delivery multiple times to
-another target to ensure reliable delivery.
-
-```
-target.queue {
- target remote
- location ...
- max_parallelism 16
- max_tries 4
- bounce {
- destination example.org {
- deliver_to &local_mailboxes
- }
- default_destination {
- reject
- }
- }
-
- autogenerated_msg_domain example.org
- debug no
-}
-```
-
-## Arguments
-
-First argument specifies directory to use for storage.
-Relative paths are relative to the StateDirectory.
-
-## Configuration directives
-
-*Syntax*: target _block_name_ ++
-*Default*: not specified
-
-REQUIRED.
-
-Delivery target to use for final delivery.
-
-*Syntax*: location _directory_ ++
-*Default*: StateDirectory/configuration_block_name
-
-File system directory to use to store queued messages.
-Relative paths are relative to the StateDirectory.
-
-*Syntax*: max_parallelism _integer_ ++
-*Default*: 16
-
-Start up to _integer_ goroutines for message processing. Basically, this option
-limits amount of messages tried to be delivered concurrently.
-
-*Syntax*: max_tries _integer_ ++
-*Default*: 20
-
-Attempt delivery up to _integer_ times. Note that no more attempts will be done
-is permanent error occured during previous attempt.
-
-Delay before the next attempt will be increased exponentally using the
-following formula: 15mins \* 1.2 ^ (n - 1) where n is the attempt number.
-This gives you approximately the following sequence of delays:
-18mins, 21mins, 25mins, 31mins, 37mins, 44mins, 53mins, 64mins, ...
-
-*Syntax*: bounce { ... } ++
-*Default*: not specified
-
-This configuration contains pipeline configuration to be used for generated DSN
-(Delivery Status Notifiaction) messages.
-
-If this is block is not present in configuration, DSNs will not be generated.
-Note, however, this is not what you want most of the time.
-
-*Syntax*: autogenerated_msg_domain _domain_ ++
-*Default*: global directive value
-
-Domain to use in sender address for DSNs. Should be specified too if 'bounce'
-block is specified.
-
-*Syntax*: debug _boolean_ ++
-*Default*: no
-
-Enable verbose logging.
-
-# Remote MX module (remote)
-
-Module that implements message delivery to remote MTAs discovered via DNS MX
-records. You probably want to use it with queue module for reliability.
-
-```
-target.remote {
- hostname mx.example.org
- debug no
-}
-```
-
-If a message check marks a message as 'quarantined', remote module
-will refuse to deliver it.
-
-## Configuration directives
-
-*Syntax*: hostname _domain_ ++
-*Default*: global directive value
-
-Hostname to use client greeting (EHLO/HELO command). Some servers require it to
-be FQDN, SPF-capable servers check whether it corresponds to the server IP
-address, so it is better to set it to a domain that resolves to the server IP.
-
-*Syntax*: limits _config block_ ++
-*Default*: no limits
-
-See 'limits' directive in *maddy-smtp*(5) for SMTP endpoint.
-It works the same except for address domains used for
-per-source/per-destination are as observed when message exits the server.
-
-*Syntax*: local_ip _IP address_ ++
-*Default*: empty
-
-Choose the local IP to bind for outbound SMTP connections.
-
-*Syntax*: force_ipv4 _boolean_ ++
-*Default*: false
-
-Force resolving outbound SMTP domains to IPv4 addresses. Some server providers
-do not offer a way to properly set reverse PTR domains for IPv6 addresses; this
-option makes maddy only connect to IPv4 addresses so that its public IPv4 address
-is used to connect to that server, and thus reverse PTR checks are made against
-its IPv4 address.
-
-Warning: this may break sending outgoing mail to IPv6-only SMTP servers.
-
-*Syntax*: connect_timeout _duration_ ++
-*Default*: 5m
-
-Timeout for TCP connection establishment.
-
-RFC 5321 recommends 5 minutes for "initial greeting" that includes TCP
-handshake. maddy uses two separate timers - one for "dialing" (DNS A/AAAA
-lookup + TCP handshake) and another for "initial greeting". This directive
-configures the former. The latter is not configurable and is hardcoded to be
-5 minutes.
-
-*Syntax*: command_timeout _duration_ ++
-*Default*: 5m
-
-Timeout for any SMTP command (EHLO, MAIL, RCPT, DATA, etc).
-
-If STARTTLS is used this timeout also applies to TLS handshake.
-
-RFC 5321 recommends 5 minutes for MAIL/RCPT and 3 minutes for
-DATA.
-
-*Syntax*: submission_timeout _duration_ ++
-*Default*: 12m
-
-Time to wait after the entire message is sent (after "final dot").
-
-RFC 5321 recommends 10 minutes.
-
-*Syntax*: debug _boolean_ ++
-*Default*: global directive value
-
-Enable verbose logging.
-
-*Syntax*: requiretls_override _boolean_ ++
-*Default*: true
-
-Allow local security policy to be disabled using 'TLS-Required' header field in
-sent messages. Note that the field has no effect if transparent forwarding is
-used, message body should be processed before outbound delivery starts for it
-to take effect (e.g. message should be queued using 'queue' module).
-
-*Syntax*: relaxed_requiretls _boolean_ ++
-*Default*: true
-
-This option disables strict conformance with REQUIRETLS specification and
-allows forwarding of messages 'tagged' with REQUIRETLS to MXes that are not
-advertising REQUIRETLS support. It is meant to allow REQUIRETLS use without the
-need to have support from all servers. It is based on the assumption that
-server referenced by MX record is likely the final destination and therefore
-there is only need to secure communication towards it and not beyond.
-
-*Syntax*: conn_reuse_limit _integer_ ++
-*Default*: 10
-
-Amount of times the same SMTP connection can be used.
-Connections are never reused if the previous DATA command failed.
-
-*Syntax*: conn_max_idle_count _integer_ ++
-*Default*: 10
-
-Max. amount of idle connections per recipient domains to keep in cache.
-
-*Syntax*: conn_max_idle_time _integer_ ++
-*Default*: 150 (2.5 min)
-
-Amount of time the idle connection is still considered potentially usable.
-
-## Security policies
-
-*Syntax*: mx_auth _config block_ ++
-*Default*: no policies
-
-'remote' module implements a number of of schemes and protocols necessary to
-ensure security of message delivery. Most of these schemes are concerned with
-authentication of recipient server and TLS enforcement.
-
-To enable mechanism, specify its name in the mx_auth directive block:
-```
-mx_auth {
- dane
- mtasts
-}
-```
-Additional configuration is possible if supported by the mechanism by
-specifying additional options as a block for the corresponding mechanism.
-E.g.
-```
-mtasts {
- cache ram
-}
-```
-
-If the mx_auth directive is not specified, no mechanisms are enabled. Note
-that, however, this makes outbound SMTP vulnerable to a numberous downgrade
-attacks and hence not recommended.
-
-It is possible to share the same set of policies for multiple 'remote' module
-instances by defining it at the top-level using 'mx_auth' module and then
-referencing it using standard & syntax:
-```
-mx_auth outbound_policy {
- dane
- mtasts {
- cache ram
- }
-}
-
-# ... somewhere else ...
-
-deliver_to remote {
- mx_auth &outbound_policy
-}
-
-# ... somewhere else ...
-
-deliver_to remote {
- mx_auth &outbound_policy
- tls_client { ... }
-}
-```
-
-## Security policies: MTA-STS
-
-Checks MTA-STS policy of the recipient domain. Provides proper authentication
-and TLS enforcement for delivery, but partially vulnerable to persistent active
-attacks.
-
-Sets MX level to "mtasts" if the used MX matches MTA-STS policy even if it is
-not set to "enforce" mode.
-
-```
-mtasts {
- cache fs
- fs_dir StateDirectory/mtasts_cache
-}
-```
-
-*Syntax*: cache fs|ram ++
-*Default*: fs
-
-Storage to use for MTA-STS cache. 'fs' is to use a filesystem directory, 'ram'
-to store the cache in memory.
-
-It is recommended to use 'fs' since that will not discard the cache (and thus
-cause MTA-STS security to disappear) on server restart. However, using the RAM
-cache can make sense for high-load configurations with good uptime.
-
-*Syntax*: fs_dir _directory_ ++
-*Default*: StateDirectory/mtasts_cache
-
-Filesystem directory to use for policies caching if 'cache' is set to 'fs'.
-
-## Security policies: DNSSEC
-
-Checks whether MX records are signed. Sets MX level to "dnssec" is they are.
-
-maddy does not validate DNSSEC signatures on its own. Instead it reslies on
-the upstream resolver to do so by causing lookup to fail when verification
-fails and setting the AD flag for signed and verfified zones. As a safety
-measure, if the resolver is not 127.0.0.1 or ::1, the AD flag is ignored.
-
-DNSSEC is currently not supported on Windows and other platforms that do not
-have the /etc/resolv.conf file in the standard format.
-
-```
-dnssec { }
-```
-
-## Security policies: DANE
-
-Checks TLSA records for the recipient MX. Provides downgrade-resistant TLS
-enforcement.
-
-Sets TLS level to "authenticated" if a valid and matching TLSA record uses
-DANE-EE or DANE-TA usage type.
-
-See above for notes on DNSSEC. DNSSEC support is required for DANE to work.
-
-```
-dane { }
-```
-
-## Security policies: Local policy
-
-Checks effective TLS and MX levels (as set by other policies) against local
-configuration.
-
-```
-local_policy {
- min_tls_level none
- min_mx_level none
-}
-```
-
-Using 'local_policy off' is equivalent to setting both directives to 'none'.
-
-*Syntax*: min_tls_level none|encrypted|authenticated ++
-*Default*: none
-
-Set the minimal TLS security level required for all outbound messages.
-
-See [Security levels](../../seclevels) page for details.
-
-*Syntax*: min_mx_level: none|mtasts|dnssec ++
-*Default*: none
-
-Set the minimal MX security level required for all outbound messages.
-
-See [Security levels](../../seclevels) page for details.
-
-# SMTP transparent forwarding module (target.smtp)
-
-Module that implements transparent forwarding of messages over SMTP.
-
-Use in pipeline configuration:
-```
-deliver_to smtp tcp://127.0.0.1:5353
-# or
-deliver_to smtp tcp://127.0.0.1:5353 {
- # Other settings, see below.
-}
-```
-
-```
-target.smtp {
- debug no
- tls_client {
- ...
- }
- attempt_starttls yes
- require_tls no
- auth off
- targets tcp://127.0.0.1:2525
- connect_timeout 5m
- command_timeout 5m
- submission_timeout 12m
-}
-```
-
-Endpoint addresses use format described in *maddy-config*(5).
-
-## Configuration directives
-
-*Syntax*: debug _boolean_ ++
-*Default*: global directive value
-
-Enable verbose logging.
-
-*Syntax*: tls_client { ... } ++
-*Default*: not specified
-
-Advanced TLS client configuration options. See *maddy-tls*(5) for details.
-
-*Syntax*: attempt_starttls _boolean_ ++
-*Default*: yes (no for target.lmtp)
-
-Attempt to use STARTTLS if it is supported by the remote server.
-If TLS handshake fails, connection will be retried without STARTTLS
-unless 'require_tls' is also specified.
-
-*Syntax*: require_tls _boolean_ ++
-*Default*: no
-
-Refuse to pass messages over plain-text connections.
-
-*Syntax*: ++
- auth off ++
- plain _username_ _password_ ++
- forward ++
- external ++
-*Default*: off
-
-Specify the way to authenticate to the remote server.
-Valid values:
-
-- off
-
- No authentication.
-
-- plain
-
- Authenticate using specified username-password pair.
- *Don't use* this without enforced TLS ('require_tls').
-
-- forward
-
- Forward credentials specified by the client.
- *Don't use* this without enforced TLS ('require_tls').
-
-- external
-
- Request "external" SASL authentication. This is usually used for
- authentication using TLS client certificates. See *maddy-tls*(5)
- for how to specify the client certificate.
-
-*Syntax*: targets _endpoints..._ ++
-*Default:* not specified
-
-REQUIRED.
-
-List of remote server addresses to use. See Address definitions in
-*maddy-config*(5) for syntax to use. Basically, it is 'tcp://ADDRESS:PORT'
-for plain SMTP and 'tls://ADDRESS:PORT' for SMTPS (aka SMTP with Implicit
-TLS).
-
-Multiple addresses can be specified, they will be tried in order until connection to
-one succeeds (including TLS handshake if TLS is required).
-
-*Syntax*: connect_timeout _duration_ ++
-*Default*: 5m
-
-Same as for target.remote.
-
-*Syntax*: command_timeout _duration_ ++
-*Default*: 5m
-
-Same as for target.remote.
-
-*Syntax*: submission_timeout _duration_ ++
-*Default*: 12m
-
-Same as for target.remote.
-
-# LMTP transparent forwarding module (target.lmtp)
-
-The 'target.lmtp' module is similar to 'target.smtp' and supports all
-its options and syntax but speaks LMTP instead of SMTP.
diff --git a/docs/man/maddy-tls.5.scd b/docs/man/maddy-tls.5.scd
deleted file mode 100644
index 3307820f7..000000000
--- a/docs/man/maddy-tls.5.scd
+++ /dev/null
@@ -1,379 +0,0 @@
-maddy-tls(5) "maddy mail server" "maddy reference documentation"
-
-; TITLE Advanced TLS configuration
-
-# TLS server configuration
-
-TLS certificates are obtained by modules called "certificate loaders". 'tls' directive
-arguments specify name of loader to use and arguments. Due to syntax limitations
-advanced configuration for loader should be specified using 'loader' directive, see
-below.
-
-```
-tls file cert.pem key.pem {
- protocols tls1.2 tls1.3
- curve X25519
- ciphers ...
-}
-
-tls {
- loader file cert.pem key.pem {
- # Options for loader go here.
- }
- protocols tls1.2 tls1.3
- curve X25519
- ciphers ...
-}
-```
-
-## Available certificate loaders
-
-- file
-
- Accepts argument pairs specifying certificate and then key.
- E.g. 'tls file certA.pem keyA.pem certB.pem keyB.pem'
-
- If multiple certificates are listed, SNI will be used.
-
-- acme
-
- Automatically obtains a certificate using ACME protocol (Let's Encrypt)
-
- See below for details.
-
-- off
-
- Not really a loader but a special value for tls directive, explicitly disables TLS for
- endpoint(s).
-
-## Advanced TLS configuration
-
-*Note: maddy uses secure defaults and TLS handshake is resistant to active downgrade attacks.*
-*There is no need to change anything in most cases.*
-
-*Syntax*: ++
- protocols _min_version_ _max_version_ ++
- protocols _version_ ++
-*Default*: tls1.0 tls1.3
-
-Minimum/maximum accepted TLS version. If only one value is specified, it will
-be the only one usable version.
-
-Valid values are: tls1.0, tls1.1, tls1.2, tls1.3
-
-*Syntax*: ciphers _ciphers..._ ++
-*Default*: Go version-defined set of 'secure ciphers', ordered by hardware
-performance
-
-List of supported cipher suites, in preference order. Not used with TLS 1.3.
-
-Valid values:
-
-- RSA-WITH-RC4128-SHA
-- RSA-WITH-3DES-EDE-CBC-SHA
-- RSA-WITH-AES128-CBC-SHA
-- RSA-WITH-AES256-CBC-SHA
-- RSA-WITH-AES128-CBC-SHA256
-- RSA-WITH-AES128-GCM-SHA256
-- RSA-WITH-AES256-GCM-SHA384
-- ECDHE-ECDSA-WITH-RC4128-SHA
-- ECDHE-ECDSA-WITH-AES128-CBC-SHA
-- ECDHE-ECDSA-WITH-AES256-CBC-SHA
-- ECDHE-RSA-WITH-RC4128-SHA
-- ECDHE-RSA-WITH-3DES-EDE-CBC-SHA
-- ECDHE-RSA-WITH-AES128-CBC-SHA
-- ECDHE-RSA-WITH-AES256-CBC-SHA
-- ECDHE-ECDSA-WITH-AES128-CBC-SHA256
-- ECDHE-RSA-WITH-AES128-CBC-SHA256
-- ECDHE-RSA-WITH-AES128-GCM-SHA256
-- ECDHE-ECDSA-WITH-AES128-GCM-SHA256
-- ECDHE-RSA-WITH-AES256-GCM-SHA384
-- ECDHE-ECDSA-WITH-AES256-GCM-SHA384
-- ECDHE-RSA-WITH-CHACHA20-POLY1305
-- ECDHE-ECDSA-WITH-CHACHA20-POLY1305
-
-*Syntax*: curve _curves..._ ++
-*Default*: defined by Go version
-
-The elliptic curves that will be used in an ECDHE handshake, in preference
-order.
-
-Valid values: p256, p384, p521, X25519.
-
-# TLS client configuration
-
-tls_client directive allows to customize behavior of TLS client implementation,
-notably adjusting minimal and maximal TLS versions and allowed cipher suites,
-enabling TLS client authentication.
-
-```
-tls_client {
- protocols tls1.2 tls1.3
- ciphers ...
- curve X25519
- root_ca /etc/ssl/cert.pem
-
- cert /etc/ssl/private/maddy-client.pem
- key /etc/ssl/private/maddy-client.pem
-}
-```
-
-*Syntax*: ++
- protocols _min_version_ _max_version_ ++
- protocols _version_ ++
-*Default*: tls1.0 tls1.3
-
-Minimum/maximum accepted TLS version. If only one value is specified, it will
-be the only one usable version.
-
-Valid values are: tls1.0, tls1.1, tls1.2, tls1.3
-
-*Syntax*: ciphers _ciphers..._ ++
-*Default*: Go version-defined set of 'secure ciphers', ordered by hardware
-performance
-
-List of supported cipher suites, in preference order. Not used with TLS 1.3.
-
-See TLS server configuration for list of supported values.
-
-*Syntax*: curve _curves..._ ++
-*Default*: defined by Go version
-
-The elliptic curves that will be used in an ECDHE handshake, in preference
-order.
-
-Valid values: p256, p384, p521, X25519.
-
-*Syntax*: root_ca _paths..._ ++
-*Default*: system CA pool
-
-List of files with PEM-encoded CA certificates to use when verifying
-server certificates.
-
-*Syntax*: ++
- cert _cert_path_ ++
- key _key_path_ ++
-*Default*: not specified
-
-Present the specified certificate when server requests a client certificate.
-Files should use PEM format. Both directives should be specified.
-
-# Automatic certificate management via ACME
-
-```
-tls.loader.acme {
- debug off
- hostname example.maddy.invalid
- store_path /var/lib/maddy/acme
- ca https://acme-v02.api.letsencrypt.org/directory
- test_ca https://acme-staging-v02.api.letsencrypt.org/directory
- email test@maddy.invalid
- agreed off
- challenge dns-01
- dns ...
-}
-```
-
-Maddy supports obtaining certificates using ACME protocol.
-
-To use it, create a configuration name for tls.loader.acme
-and reference it from endpoints that should use automatically
-configured certificates:
-```
-tls.loader.acme local_tls {
- email put-your-email-here@example.org
- agreed # indicate your agreement with Let's Encrypt ToS
- challenge dns-01
-}
-
-smtp tcp://127.0.0.1:25 {
- tls &local_tls
- ...
-}
-```
-
-Currently the only supported challenge is dns-01 one therefore
-you also need to configure the DNS provider:
-```
-tls.loader.acme local_tls {
- email maddy-acme@example.org
- agreed
- challenge dns-01
- dns PROVIDER_NAME {
- ...
- }
-}
-```
-See below for supported providers and necessary configuration
-for each.
-
-## Configuration directives
-
-*Syntax:* debug _boolean_ ++
-*Default:* global directive value
-
-Enable debug logging.
-
-*Syntax:* hostname _str_ ++
-*Default:* global directive value
-
-Domain name to issue certificate for. Required.
-
-*Syntax:* store_path _path_ ++
-*Default:* state_dir/acme
-
-Where to store issued certificates and associated metadata.
-Currently only filesystem-based store is supported.
-
-*Syntax:* ca _url_ ++
-*Default:* Let's Encrypt production CA
-
-URL of ACME directory to use.
-
-*Syntax:* test_ca _url_ ++
-*Default:* Let's Encrypt staging CA
-
-URL of ACME directory to use for retries should
-primary CA fail.
-
-maddy will keep attempting to issues certificates
-using test_ca until it succeeds then it will switch
-back to the one configured via 'ca' option.
-
-This avoids rate limit issues with production CA.
-
-*Syntax:* email _str_ ++
-*Default:* not set
-
-Email to pass while registering an ACME account.
-
-*Syntax:* agreed _boolean_ ++
-*Default:* false
-
-Whether you agreed to ToS of the CA service you are using.
-
-*Syntax:* challenge dns-01 ++
-*Default:* not set
-
-Challenge(s) to use while performing domain verification.
-
-## DNS providers
-
-Support for some providers is not provided by standard builds.
-To be able to use these, you need to compile maddy
-with "libdns_PROVIDER" build tag.
-E.g.
-```
-./build.sh -tags 'libdns_googleclouddns'
-```
-
-- gandi
-
-```
-dns gandi {
- api_token "token"
-}
-```
-
-- digitalocean
-
-```
-dns digitalocean {
- api_token "..."
-}
-```
-
-- cloudflare
-
-See https://github.com/libdns/cloudflare#authenticating
-
-```
-dns cloudflare {
- api_token "..."
-}
-```
-
-- vultr
-
-```
-dns vultr {
- api_token "..."
-}
-```
-
-- hetzner
-
-```
-dns hetzner {
- api_token "..."
-}
-```
-
-- namecheap
-
-```
-dns namecheap {
- api_key "..."
- api_username "..."
-
- # optional: API endpoint, production one is used if not set.
- endpoint "https://api.namecheap.com/xml.response"
-
- # optional: your public IP, discovered using icanhazip.com if not set
- client_ip 1.2.3.4
-}
-```
-
-- googleclouddns (non-default)
-
-```
-dns googleclouddns {
- project "project_id"
- service_account_json "path"
-}
-```
-
-- route53 (non-default)
-
-```
-dns route53 {
- secret_access_key "..."
- access_key_id "..."
- # or use environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
-}
-```
-
-- leaseweb (non-default)
-
-```
-dns leaseweb {
- api_key "key"
-}
-```
-
-- metaname (non-default)
-
-```
-dns metaname {
- api_key "key"
- account_ref "reference"
-}
-```
-
-- alidns (non-default)
-
-```
-dns alidns {
- key_id "..."
- key_secret "..."
-}
-```
-
-- namedotcom (non-default)
-
-```
-dns namedotcom {
- user "..."
- token "..."
-}
-```
diff --git a/docs/man/maddy.5.scd b/docs/man/maddy.5.scd
deleted file mode 100644
index 06926b41e..000000000
--- a/docs/man/maddy.5.scd
+++ /dev/null
@@ -1,228 +0,0 @@
-maddy(1) "maddy mail server" "maddy reference documentation"
-
-; TITLE Introduction
-
-# Modules
-
-maddy is built of many small components called "modules". Each module does one
-certain well-defined task. Modules can be connected to each other in arbitrary
-ways to achieve wanted functionality. Default configuration file defines
-set of modules that together implement typical email server stack.
-
-To specify the module that should be used by another module for something, look
-for configuration directives with "module reference" argument. Then
-put the module name as an argument for it. Optionally, if referenced module
-needs that, put additional arguments after the name. You can also put a
-configuration block with additional directives specifing the module
-configuration.
-
-Here are some examples:
-
-```
-smtp ... {
- # Deliver messages to the 'dummy' module with the default configuration.
- deliver_to dummy
-
- # Deliver messages to the 'target.smtp' module with
- # 'tcp://127.0.0.1:1125' argument as a configuration.
- deliver_to smtp tcp://127.0.0.1:1125
-
- # Deliver messages to the 'queue' module with the specified configuration.
- deliver_to queue {
- target ...
- max_tries 10
- }
-}
-```
-
-Additionally, module configuration can be placed in a separate named block
-at the top-level and merely referenced by its name where it is needed.
-
-Here is the example:
-```
-storage.imapsql local_mailboxes {
- driver sqlite3
- dsn all.db
-}
-
-smtp ... {
- deliver_to &local_mailboxes
-}
-```
-
-It is recommended to use this syntax for modules that are 'expensive' to
-initialize such as storage backends and authentication providers.
-
-For top-level configuration block definition, syntax is as follows:
-```
-namespace.module_name config_block_name... {
- module_configuration
-}
-```
-If config_block_name is omitted, it will be the same as module_name. Multiple
-names can be specified. All names must be unique.
-
-Note the "storage." prefix. The actual module name is this and includes
-"namespace". It is a little cheating to make more concise names and can
-be omitted when you reference the module where it is used since it can
-be implied (e.g. putting module reference in "check{}" likely means you want
-something with "check." prefix)
-
-Usual module arguments can't be specified when using this syntax, however,
-modules usually provide explicit directives that allow to specify the needed
-values. For example 'sql sqlite3 all.db' is equivalent to
-```
-storage.imapsql {
- driver sqlite3
- dsn all.db
-}
-```
-
-# Reference documentation conventions
-
-## Syntax descriptions for directives
-
-Underlined values are placeholders and should be replaced by your values.
-_boolean_ is either 'yes' or 'no' string.
-
-Ellipsis (_smth..._) means that multiple values can be specified
-
-Multiple values listed with '|' (pipe) separator mean that any of them
-can be used.
-
-# Global directives
-
-These directives applied for all configuration blocks that don't override it.
-
-*Syntax*: state_dir _path_ ++
-*Default*: /var/lib/maddy
-
-The path to the state directory. This directory will be used to store all
-persistent data and should be writable.
-
-*Syntax*: runtime_dir _path_ ++
-*Default*: /run/maddy
-
-The path to the runtime directory. Used for Unix sockets and other temporary
-objects. Should be writable.
-
-*Syntax*: hostname _domain_ ++
-*Default*: not specified
-
-Internet hostname of this mail server. Typicall FQDN is used. It is recommended
-to make sure domain specified here resolved to the public IP of the server.
-
-*Syntax*: autogenerated_msg_domain _domain_ ++
-*Default*: not specified
-
-Domain that is used in From field for auto-generated messages (such as Delivery
-Status Notifications).
-
-*Syntax*: ++
- tls file _cert_file_ _pkey_file_ ++
- tls _module reference_ ++
- tls off ++
-*Default*: not specified
-
-Default TLS certificate to use for all endpoints.
-
-Must be present in either all endpoint modules configuration blocks or as
-global directive.
-
-You can also specify other configuration options such as cipher suites and TLS
-version. See maddy-tls(5) for details. maddy uses reasonable
-cipher suites and TLS versions by default so you generally don't have to worry
-about it.
-
-*Syntax*: tls_client { ... } ++
-*Default*: not specified
-
-This is optional block that specifies various TLS-related options to use when
-making outbound connections. See TLS client configuration for details on
-directives that can be used in it. maddy uses reasonable cipher suites and TLS
-versions by default so you generally don't have to worry about it.
-
-*Syntax*: ++
- log _targets..._ ++
- log off ++
-*Default*: stderr
-
-Write log to one of more "targets".
-
-The target can be one or the following:
-
-- stderr
-
- Write logs to stderr.
-
-- stderr_ts
-
- Write logs to stderr with timestamps.
-
-- syslog
-
- Send logs to the local syslog daemon.
-
-- _file path_
-
- Write (append) logs to file.
-
-Example:
-```
-log syslog /var/log/maddy.log
-```
-
-*Note:* Maddy does not perform log files rotation, this is the job of the
-logrotate daemon. Send SIGUSR1 to maddy process to make it reopen log files.
-
-*Syntax*: debug _boolean_ ++
-*Default*: no
-
-Enable verbose logging for all modules. You don't need that unless you are
-reporting a bug.
-
-# Prometheus/OpenMetrics endpoint
-
-```
-openmetrics tcp://127.0.0.1:9749 { }
-```
-
-This will enable HTTP listener that will serve telemetry in OpenMetrics format.
-(It is compatible with Prometheus).
-
-See openmetrics.md documentation page the list of metrics exposed.
-
-# Signals
-
-*SIGTERM, SIGINT, SIGHUP*
-
-Stop the server process gracefully. Send the signal second time to force
-immediate shutdown (likely unclean).
-
-*SIGUSR1*
-
-Reopen log files, if any are used.
-
-*SIGUSR2*
-
-Reload some files from disk, including alias mappings and TLS certificates.
-This does not include the main configuration, though.
-
-# Authors
-
-Maintained by Max Mazurov . Project includes contributions
-made by other people.
-
-Source code is available at https://github.com/foxcpp/maddy.
-
-# See also
-
-*maddy-config*(5) - Detailed configuration syntax description ++
-*maddy-imap*(5) - IMAP endpoint module reference ++
-*maddy-smtp*(5) - SMTP & Submission endpoint module reference ++
-*maddy-targets*(5) - Delivery targets reference ++
-*maddy-storage*(5) - Storage modules reference ++
-*maddy-auth*(5) - Authentication modules reference ++
-*maddy-filters*(5) - Message filtering modules reference ++
-*maddy-tables*(5) - Table modules reference ++
-*maddy-tls*(5) - Advanced TLS client & server configuration
diff --git a/docs/multiple-domains.md b/docs/multiple-domains.md
index a7425334a..46fabf025 100644
--- a/docs/multiple-domains.md
+++ b/docs/multiple-domains.md
@@ -1,56 +1,157 @@
# Multiple domains configuration
-## Separate account namespaces
+By default, maddy uses email addresses as account identifiers for both
+authentication and storage purposes. Therefore, account named `user@example.org`
+is completely independent from `user@example.com`. They must be created
+separately, may have different credentials and have separate IMAP mailboxes.
-Given two domains, example.org and example.com. foo@example.org and
-foo@example.com are different and completely independent accounts.
+This makes it extremely easy to setup maddy to manage multiple otherwise
+independent domains.
-All changes needed to make it work is to make sure all domains are specified in
-the `$(local_domains)` macro in the main configuration file. Note that you need
-to pick one domain as a "primary" for use in auto-generated messages.
+Default configuration file contains two macros - `$(primary_domain)` and
+`$(local_domains)`. They are used to used in several places thorough the
+file to configure message routing, security checks, etc.
+
+In general, you should just add all domains you want maddy to manage to
+`$(local_domains)`, like this:
```
$(primary_domain) = example.org
$(local_domains) = $(primary_domain) example.com
```
+Note that you need to pick one domain as a "primary" for use in
+auto-generated messages.
+
+With that done, you can create accounts using both domains in the name, send
+and receive messages and so on. Do not forget to configure corresponding SPF,
+DMARC and MTA-STS records as was recommended in
+the [introduction tutorial](tutorials/setting-up.md).
-The base configuration is done. You can create accounts using maddyctl using
-both domains in the name, send and receive messages and so on. Do not forget
-to configure corresponding SPF, DMARC and MTA-STS records as was
-recommended in the [introduction tutorial](tutorials/setting-up.md).
+Also note that you do not really need a separate TLS certificate for each
+managed domain. You can have one hostname e.g. mail.example.org set as an MX
+record for multiple domains.
-## Single account namespace
+**If you want multiple domains to share username namespace**, you should change
+several more options.
-You can configure maddy to only use local part of the email
-as an account identifier instead of the complete email.
+You can make "user@example.org" and "user@example.com" users share the same
+credentials of user "user" but have different IMAP mailboxes ("user@example.org"
+and "user@example.com" correspondingly). For that, it is enough to set `auth_map`
+globally to use `email_localpart` table:
+```
+auth_map email_localpart
+```
+This way, when user logs in as "user@example.org", "user" will be passed
+to the authentication provider, but "user@example.org" will be passed to the
+storage backend. You should create accounts like this:
+```
+maddy creds create user
+maddy imap-acct create user@example.org
+maddy imap-acct create user@example.com
+```
-This needs two changes to default configuration:
-```
+**If you want accounts to also share the same IMAP storage of account named
+"user"**, you can set `storage_map` in IMAP endpoint and `delivery_map` in
+storage backend to use `email_locapart`:
+```
storage.imapsql local_mailboxes {
- ...
- delivery_map email_localpart
- auth_normalize precis_casefold
+ ...
+ delivery_map email_localpart # deliver "user@*" to "user"
+}
+imap tls://0.0.0.0:993 {
+ ...
+ storage &local_mailboxes
+ ...
+ storage_map email_localpart # "user@*" accesses "user" mailbox
}
```
+You also might want to make it possible to log in without
+specifying a domain at all. In this case, use `email_localpart_optional` for
+both `auth_map` and `storage_map`.
+
You also need to make `authorize_sender` check (used in `submission` endpoint)
accept non-email usernames:
```
authorize_sender {
...
- auth_normalize precis_casefold
- user_to_email regexp "(.*)" "1ドル@$(primary_domain)"
+ user_to_email chain {
+ step email_localpart_optional # remove domain from username if present
+ step email_with_domain $(local_domains) # expand username with all allowed domains
+ }
}
```
-Note that is would work only if clients use only one domain as sender (`$(primary_domain)`).
-If you want to allow sending from all domains, you need to remove `authorize_sender` check
-altogether since it is not currently supported.
-After that you can create accounts without specifying the domain part:
-```
-maddyctl imap-acct create foxcpp
-maddyctl creds create foxcpp
+## TL;DR
+
+Your options:
+
+**"user@example.org" and "user@example.com" have distinct credentials and
+distinct mailboxes.**
+
+```
+$(primary_domain) = example.org
+$(local_domains) = example.org example.com
+```
+
+Create accounts as:
+
+```shell
+maddy creds create user@example.org
+maddy imap-acct create user@example.org
+maddy creds create user@example.com
+maddy imap-acct create user@example.com
+```
+
+**"user@example.org" and "user@example.com" have same credentials but
+distinct mailboxes.**
+
+```
+$(primary_domain) = example.org
+$(local_domains) = example.org example.com
+auth_map email_localpart
+```
+
+Create accounts as:
+```shell
+maddy creds create user
+maddy imap-acct create user@example.org
+maddy imap-acct create user@example.com
```
-And authenticate using "foxcpp" in email clients.
-Messages for any foxcpp@* address with a domain in `$(local_domains)`
-will be delivered to that mailbox.
+**"user@example.org", "user@example.com", "user" have same credentials and same
+mailboxes.**
+
+```
+ $(primary_domain) = example.org
+ $(local_domains) = example.org example.com
+ auth_map email_localpart_optional # authenticating as "user@*" checks credentials for "user"
+
+ storage.imapsql local_mailboxes {
+ ...
+ delivery_map email_localpart_optional # deliver "user@*" to "user" mailbox
+ }
+
+ imap tls://0.0.0.0:993 {
+ ...
+ storage_map email_localpart_optional # authenticating as "user@*" accesses "user" mailboxes
+ }
+
+ submission tls://0.0.0.0:465 {
+ check {
+ authorize_sender {
+ ...
+ user_to_email chain {
+ step email_localpart_optional # remove domain from username if present
+ step email_with_domain $(local_domains) # expand username with all allowed domains
+ }
+ }
+ }
+ ...
+ }
+```
+
+Create accounts as:
+```shell
+maddy creds create user
+maddy imap-acct create user
+```
diff --git a/docs/reference/auth/dovecot_sasl.md b/docs/reference/auth/dovecot_sasl.md
new file mode 100644
index 000000000..919d42b8d
--- /dev/null
+++ b/docs/reference/auth/dovecot_sasl.md
@@ -0,0 +1,26 @@
+# Dovecot SASL
+
+The 'auth.dovecot_sasl' module implements the client side of the Dovecot
+authentication protocol, allowing maddy to use it as a credentials source.
+
+Currently SASL mechanisms support is limited to mechanisms supported by maddy
+so you cannot get e.g. SCRAM-MD5 this way.
+
+```
+auth.dovecot_sasl {
+ endpoint unix://socket_path
+}
+
+dovecot_sasl unix://socket_path
+```
+
+## Configuration directives
+
+### endpoint _schema://address_
+Default: not set
+
+Set the address to use to contact Dovecot SASL server in the standard endpoint
+format.
+
+`tcp://10.0.0.1:2222` for TCP, `unix:///var/lib/dovecot/auth.sock` for Unix
+domain sockets.
diff --git a/docs/reference/auth/external.md b/docs/reference/auth/external.md
new file mode 100644
index 000000000..9b9659ef2
--- /dev/null
+++ b/docs/reference/auth/external.md
@@ -0,0 +1,52 @@
+# System command
+
+auth.external module for authentication using external helper binary. It looks for binary
+named `maddy-auth-helper` in $PATH and libexecdir and uses it for authentication
+using username/password pair.
+
+The protocol is very simple:
+Program is launched for each authentication. Username and password are written
+to stdin, adding \n to the end. If binary exits with 0 status code -
+authentication is considered successful. If the status code is 1 -
+authentication is failed. If the status code is 2 - another unrelated error has
+happened. Additional information should be written to stderr.
+
+```
+auth.external {
+ helper /usr/bin/ldap-helper
+ perdomain no
+ domains example.org
+}
+```
+
+## Configuration directives
+
+### helper _file_path_
+
+**Required.**
+Location of the helper binary.
+
+---
+
+### perdomain _boolean_
+Default: `no`
+
+Don't remove domain part of username when authenticating and require it to be
+present. Can be used if you want user@domain1 and user@domain2 to be different
+accounts.
+
+---
+
+### domains _domains..._
+Default: not specified
+
+Domains that should be allowed in username during authentication.
+
+For example, if 'domains' is set to "domain1 domain2", then
+username, username@domain1 and username@domain2 will be accepted as valid login
+name in addition to just username.
+
+If used without 'perdomain', domain part will be removed from login before
+check with underlying auth. mechanism. If 'perdomain' is set, then
+domains must be also set and domain part **will not** be removed before check.
+
diff --git a/docs/reference/auth/ldap.md b/docs/reference/auth/ldap.md
new file mode 100644
index 000000000..a4ced5514
--- /dev/null
+++ b/docs/reference/auth/ldap.md
@@ -0,0 +1,130 @@
+# LDAP BindDN
+
+maddy supports authentication via LDAP using DN binding. Passwords are verified
+by the LDAP server.
+
+maddy needs to know the DN to use for binding. It can be obtained either by
+directory search or template .
+
+Note that storage backends conventionally use email addresses, if you use
+non-email identifiers as usernames then you should map them onto
+emails on delivery by using `auth_map` (see documentation page for used storage backend).
+
+auth.ldap also can be a used as a table module. This way you can check
+whether the account exists. It works only if DN template is not used.
+
+```
+auth.ldap {
+ urls ldap://maddy.test:389
+
+ # Specify initial bind credentials. Not required ('bind off')
+ # if DN template is used.
+ bind plain "cn=maddy,ou=people,dc=maddy,dc=test" "123456"
+
+ # Specify DN template to skip lookup.
+ dn_template "cn={username},ou=people,dc=maddy,dc=test"
+
+ # Specify base_dn and filter to lookup DN.
+ base_dn "ou=people,dc=maddy,dc=test"
+ filter "(&(objectClass=posixAccount)(uid={username}))"
+
+ tls_client { ... }
+ starttls off
+ debug off
+ connect_timeout 1m
+}
+```
+```
+auth.ldap ldap://maddy.test.389 {
+ ...
+}
+```
+
+## Configuration directives
+
+### urls _servers..._
+
+**Required.**
+
+URLs of the directory servers to use. First available server
+is used - no load-balancing is done.
+
+URLs should use `ldap://`, `ldaps://`, `ldapi://` schemes.
+
+---
+
+### bind `off` | `unauth` | `external` | `plain` _username_ _password_
+
+Default: `off`
+
+Credentials to use for initial binding. Required if DN lookup is used.
+
+`unauth` performs unauthenticated bind. `external` performs external binding
+which is useful for Unix socket connections (`ldapi://`) or TLS client certificate
+authentication (cert. is set using tls_client directive). `plain` performs a
+simple bind using provided credentials.
+
+---
+
+### dn_template _template_
+
+DN template to use for binding. `{username}` is replaced with the
+username specified by the user.
+
+---
+
+### base_dn _dn_
+
+Base DN to use for lookup.
+
+---
+
+### filter _str_
+
+DN lookup filter. `{username}` is replaced with the username specified
+by the user.
+
+Example:
+
+```
+(&(objectClass=posixAccount)(uid={username}))
+```
+
+Example (using ActiveDirectory):
+
+```
+(&(objectCategory=Person)(memberOf=CN=user-group,OU=example,DC=example,DC=org)(sAMAccountName={username})(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))
+```
+
+Example:
+
+```
+(&(objectClass=Person)(mail={username}))
+```
+
+---
+
+### starttls _bool_
+Default: `off`
+
+Whether to upgrade connection to TLS using STARTTLS.
+
+---
+
+### tls_client { ... }
+
+Advanced TLS client configuration. See [TLS configuration / Client](/reference/tls/#client) for details.
+
+---
+
+### connect_timeout _duration_
+Default: `1m`
+
+Timeout for initial connection to the directory server.
+
+---
+
+### request_timeout _duration_
+Default: `1m`
+
+Timeout for each request (binding, lookup).
diff --git a/docs/reference/auth/netauth.md b/docs/reference/auth/netauth.md
new file mode 100644
index 000000000..4c2681085
--- /dev/null
+++ b/docs/reference/auth/netauth.md
@@ -0,0 +1,50 @@
+# Native NetAuth
+
+maddy supports authentication via NetAuth using direct entity
+authentication checks. Passwords are verified by the NetAuth server.
+
+maddy needs to know the Entity ID to use for authentication. It must
+match the string the user provides for the Local Atom part of their
+mail address.
+
+Note that storage backends conventionally use email addresses. Since NetAuth
+recommends *nix compatible usernames. You will need to either map email
+identifiers specified by user to NetAuth Entity IDs using `auth_map` in
+endpoint.smtp/imap configuration (recommended) or you would need to use
+`storage_map` in storage backend configuration to map NetAuth Entity ID
+specified by user back to appropriate storage backend account names.
+
+auth.netauth also can be used as a table module. This way you can
+check whether the account exists.
+
+Note that the configuration fragment provided below is very sparse.
+This is because NetAuth expects to read most of its common
+configuration values from the system NetAuth config file located at
+`/etc/netauth/config.toml`.
+
+```
+auth.netauth {
+ require_group "maddy-users"
+ debug off
+}
+```
+
+```
+auth.netauth {}
+```
+
+## Configuration directives
+
+### require_group _group_
+
+Optional.
+
+Group that entities must possess to be able to use maddy services.
+This can be used to provide email to just a subset of the entities
+present in NetAuth.
+
+---
+
+### debug `on` | `off`
+
+Default: `off`
diff --git a/docs/reference/auth/pam.md b/docs/reference/auth/pam.md
new file mode 100644
index 000000000..89f0f3e3d
--- /dev/null
+++ b/docs/reference/auth/pam.md
@@ -0,0 +1,48 @@
+# PAM
+
+auth.pam module implements authentication using libpam. Alternatively it can be configured to
+use helper binary like auth.external module does.
+
+maddy should be built with libpam build tag to use this module without
+'use_helper' directive.
+
+```
+go get -tags 'libpam' ...
+```
+
+```
+auth.pam {
+ debug no
+ use_helper no
+}
+```
+
+## Configuration directives
+
+### debug _boolean_
+Default: `no`
+
+Enable verbose logging for all modules. You don't need that unless you are
+reporting a bug.
+
+---
+
+### use_helper _boolean_
+Default: `no`
+
+Use `LibexecDirectory/maddy-pam-helper` instead of directly calling libpam.
+You need to use that if:
+
+1. maddy is not compiled with libpam, but `maddy-pam-helper` is built separately.
+2. maddy is running as an unprivileged user and used PAM configuration requires additional privileges (e.g. when using system accounts).
+
+For 2, you need to make `maddy-pam-helper` binary setuid, see
+README.md in source tree for details.
+
+TL;DR (assuming you have the maddy group):
+
+```
+chown root:maddy /usr/lib/maddy/maddy-pam-helper
+chmod u+xs,g+x,o-x /usr/lib/maddy/maddy-pam-helper
+```
+
diff --git a/docs/reference/auth/pass_table.md b/docs/reference/auth/pass_table.md
new file mode 100644
index 000000000..39fea6fcc
--- /dev/null
+++ b/docs/reference/auth/pass_table.md
@@ -0,0 +1,44 @@
+# Password table
+
+auth.pass_table module implements username:password authentication by looking up the
+password hash using a table module (maddy-tables(5)). It can be used
+to load user credentials from text file (via table.file module) or SQL query
+(via table.sql_table module).
+
+
+Definition:
+```
+auth.pass_table [block name] {
+ table
+
+}
+```
+Shortened variant for inline use:
+```
+pass_table
[table arguments] {
+ [additional table config]
+}
+```
+
+Example, read username:password pair from the text file:
+```
+smtp tcp://0.0.0.0:587 {
+ auth pass_table file /etc/maddy/smtp_passwd
+ ...
+}
+```
+
+## Password hashes
+
+pass_table expects the used table to contain certain structured values with
+hash algorithm name, salt and other necessary parameters.
+
+You should use `maddy hash` command to generate suitable values.
+See `maddy hash --help` for details.
+
+## maddy creds
+
+If the underlying table is a "mutable" table (see maddy-tables(5)) then
+the `maddy creds` command can be used to modify the underlying tables
+via pass_table module. It will act on a "local credentials store" and will write
+appropriate hash values to the table.
diff --git a/docs/reference/auth/plain_separate.md b/docs/reference/auth/plain_separate.md
new file mode 100644
index 000000000..f5b576675
--- /dev/null
+++ b/docs/reference/auth/plain_separate.md
@@ -0,0 +1,45 @@
+# Separate username and password lookup
+
+auth.plain_separate module implements authentication using username:password pairs but can
+use zero or more "table modules" (maddy-tables(5)) and one or more
+authentication providers to verify credentials.
+
+```
+auth.plain_separate {
+ user ...
+ user ...
+ ...
+ pass ...
+ pass ...
+ ...
+}
+```
+
+How it works:
+- Initial username input is normalized using PRECIS UsernameCaseMapped profile.
+- Each table specified with the 'user' directive looked up using normalized
+ username. If match is not found in any table, authentication fails.
+- Each authentication provider specified with the 'pass' directive is tried.
+ If authentication with all providers fails - an error is returned.
+
+## Configuration directives
+
+### user _table-module_
+
+Configuration block for any module from maddy-tables(5) can be used here.
+
+Example:
+
+```
+user file /etc/maddy/allowed_users
+```
+
+---
+
+### pass _auth-provider_
+
+Configuration block for any auth. provider module can be used here, even
+'plain_split' itself.
+
+The used auth. provider must provide username:password pair-based
+authentication.
diff --git a/docs/reference/auth/shadow.md b/docs/reference/auth/shadow.md
new file mode 100644
index 000000000..0fc3e89b3
--- /dev/null
+++ b/docs/reference/auth/shadow.md
@@ -0,0 +1,40 @@
+# /etc/shadow
+
+auth.shadow module implements authentication by reading /etc/shadow. Alternatively it can be
+configured to use helper binary like auth.external does.
+
+```
+auth.shadow {
+ debug no
+ use_helper no
+}
+```
+
+## Configuration directives
+
+### debug _boolean_
+
+Default: `no`
+
+Enable verbose logging for all modules. You don't need that unless you are
+reporting a bug.
+
+---
+
+### use_helper _boolean_
+Default: `no`
+
+Use `LibexecDirectory/maddy-shadow-helper` instead of directly reading `/etc/shadow`.
+You need to use that if maddy is running as an unprivileged user
+privileges (e.g. when using system accounts).
+
+You need to make `maddy-shadow-helper` binary setuid, see
+cmd/maddy-shadow-helper/README.md in source tree for details.
+
+TL;DR (assuming you have maddy group):
+
+```
+chown root:maddy /usr/lib/maddy/maddy-shadow-helper
+chmod u+xs,g+x,o-x /usr/lib/maddy/maddy-shadow-helper
+```
+
diff --git a/docs/reference/blob/fs.md b/docs/reference/blob/fs.md
new file mode 100644
index 000000000..ef94b54b4
--- /dev/null
+++ b/docs/reference/blob/fs.md
@@ -0,0 +1,23 @@
+# Filesystem
+
+This module stores message bodies in a file system directory.
+
+```
+storage.blob.fs {
+ root
+}
+```
+
+```
+storage.blob.fs
+```
+
+## Configuration directives
+
+### root _path_
+Default: not set
+
+Path to the FS directory. Must be readable and writable by the server process.
+If it does not exist - it will be created (parent directory should be writable
+for this). Relative paths are interpreted relatively to server state directory.
+
diff --git a/docs/reference/blob/s3.md b/docs/reference/blob/s3.md
new file mode 100644
index 000000000..54b6a4e2f
--- /dev/null
+++ b/docs/reference/blob/s3.md
@@ -0,0 +1,98 @@
+# Amazon S3
+
+storage.blob.s3 module stores messages bodies in a bucket on S3-compatible storage.
+
+```
+storage.blob.s3 {
+ endpoint play.min.io
+ secure yes
+ access_key "Q3AM3UQ867SPQQA43P2F"
+ secret_key "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"
+ bucket maddy-test
+
+ # optional
+ region eu-central-1
+ object_prefix maddy/
+ creds access_key
+}
+```
+
+Example:
+
+```
+storage.imapsql local_mailboxes {
+ ...
+ msg_store s3 {
+ endpoint s3.amazonaws.com
+ access_key "..."
+ secret_key "..."
+ bucket maddy-messages
+ region us-west-2
+ creds access_key
+ }
+}
+```
+
+## Configuration directives
+
+### endpoint _address:port_
+
+**Required**.
+
+Root S3 endpoint. e.g. `s3.amazonaws.com`
+
+---
+
+### secure _boolean_
+Default: `yes`
+
+Whether TLS should be used.
+
+---
+
+### access_key _string_ secret_key _string_
+
+**Required**.
+
+Static S3 credentials.
+
+---
+
+### bucket _name_
+
+**Required**.
+
+S3 bucket name. The bucket must exist and
+be read-writable.
+
+---
+
+### region _string_
+Default: not set
+
+S3 bucket location. May be called "endpoint" in some manuals.
+
+---
+
+### object_prefix _string_
+Default: empty string
+
+String to add to all keys stored by maddy.
+
+Can be useful when S3 is used as a file system.
+
+---
+
+### creds `access_key` | `file_minio` | `file_aws` | `iam`
+Default: `access_key`
+
+Credentials to use for accessing the S3 Bucket.
+
+Credential Types:
+
+ - `access_key`: use AWS access key and secret access key
+ - `file_minio`: use credentials for Minio present at ~/.mc/config.json
+ - `file_aws`: use credentials for AWS S3 present at ~/.aws/credentials
+ - `iam`: use AWS IAM instance profile for credentials.
+
+By default, access_key is used with the access key and secret access key present in the config.
diff --git a/docs/reference/checks/actions.md b/docs/reference/checks/actions.md
new file mode 100644
index 000000000..d9e9f9c91
--- /dev/null
+++ b/docs/reference/checks/actions.md
@@ -0,0 +1,21 @@
+# Check actions
+
+When a certain check module thinks the message is "bad", it takes some actions
+depending on its configuration. Most checks follow the same configuration
+structure and allow following actions to be taken on check failure:
+
+- Do nothing (`action ignore`)
+
+Useful for testing deployment of new checks. Check failures are still logged
+but they have no effect on message delivery.
+
+- Reject the message (`action reject`)
+
+Reject the message at connection time. No bounce is generated locally.
+
+- Quarantine the message (`action quarantine`)
+
+Mark message as 'quarantined'. If message is then delivered to the local
+storage, the storage backend can place the message in the 'Junk' mailbox.
+Another thing to keep in mind that 'target.remote' module
+will refuse to send quarantined messages.
\ No newline at end of file
diff --git a/docs/reference/checks/authorize_sender.md b/docs/reference/checks/authorize_sender.md
new file mode 100644
index 000000000..4ddd78629
--- /dev/null
+++ b/docs/reference/checks/authorize_sender.md
@@ -0,0 +1,132 @@
+# MAIL FROM and From authorization
+
+Module check.authorize_sender verifies that envelope and header sender addresses belong
+to the authenticated user. Address ownership is established via table
+that maps each user account to a email address it is allowed to use.
+There are some special cases, see `user_to_email` description below.
+
+```
+check.authorize_sender {
+ prepare_email identity
+ user_to_email identity
+ check_header yes
+
+ unauth_action reject
+ no_match_action reject
+ malformed_action reject
+ err_action reject
+
+ auth_normalize auto
+ from_normalize auto
+}
+```
+```
+check {
+ authorize_sender { ... }
+}
+```
+
+## Configuration directives
+
+### user_to_email _table_
+Default: `identity`
+
+Table that maps authorization username to the list of sender emails
+the user is allowed to use.
+
+In additional to email addresses, the table can contain domain names or
+special string "\*" as a value. If the value is a domain - user
+will be allowed to use any mailbox within it as a sender address.
+If it is "\*" - user will be allowed to use any address.
+
+By default, table.identity is used, meaning that username should
+be equal to the sender email.
+
+Before username is looked up via the table, normalization algorithm
+defined by auth_normalize is applied to it.
+
+---
+
+### prepare_email _table_
+Default: `identity`
+
+Table that is used to translate email addresses before they
+are matched against user_to_email values.
+
+Typically used to allow users to use their aliases as sender
+addresses - prepare_email in this case should translate
+aliases to "canonical" addresses. This is how it is
+done in default configuration.
+
+If table does not contain any mapping for the used sender
+address, it will be used as is.
+
+---
+
+### check_header _boolean_
+Default: `yes`
+
+Whether to verify header sender in addition to envelope.
+
+Either Sender or From field value should match the
+authorization identity.
+
+---
+
+### unauth_action _action_
+Default: `reject`
+
+What to do if the user is not authenticated at all.
+
+---
+
+### no_match_action _action_
+Default: `reject`
+
+What to do if user is not allowed to use the sender address specified.
+
+---
+
+### malformed_action _action_
+Default: `reject`
+
+What to do if From or Sender header fields contain malformed values.
+
+---
+
+### err_action _action_
+Default: `reject`
+
+What to do if error happens during prepare_email or user_to_email lookup.
+
+---
+
+### auth_normalize _action_
+Default: `auto`
+
+Normalization function to apply to authorization username before
+further processing.
+
+Available options:
+
+- `auto` `precis_casefold_email` for valid emails, `precis_casefold` otherwise.
+- `precis_casefold_email` PRECIS UsernameCaseMapped profile + U-labels form for domain
+- `precis_casefold` PRECIS UsernameCaseMapped profile for the entire string
+- `precis_email` PRECIS UsernameCasePreserved profile + U-labels form for domain
+- `precis` PRECIS UsernameCasePreserved profile for the entire string
+- `casefold` Convert to lower case
+- `noop` Nothing
+
+PRECIS profiles are defined by RFC 8265. In short, they make sure
+that Unicode strings that look the same will be compared as if they were
+the same. CaseMapped profiles also convert strings to lower case.
+
+---
+
+### from_normalize _action_
+Default: `auto`
+
+Normalization function to apply to email addresses before
+further processing.
+
+Available options are same as for `auth_normalize`.
diff --git a/docs/reference/checks/command.md b/docs/reference/checks/command.md
new file mode 100644
index 000000000..6475efcf4
--- /dev/null
+++ b/docs/reference/checks/command.md
@@ -0,0 +1,96 @@
+# System command filter
+
+This module executes an arbitrary system command during a specified stage of
+checks execution.
+
+```
+command executable_name arg0 arg1 ... {
+ run_on body
+
+ code 1 reject
+ code 2 quarantine
+}
+```
+
+## Arguments
+
+The module arguments specify the command to run. If the first argument is not
+an absolute path, it is looked up in the Libexec Directory (/usr/lib/maddy on
+Linux) and in $PATH (in that ordering). Note that no additional handling
+of arguments is done, especially, the command is executed directly, not via the
+system shell.
+
+There is a set of special strings that are replaced with the corresponding
+message-specific values:
+
+- `{source_ip}` – IPv4/IPv6 address of the sending MTA.
+- `{source_host}` – Hostname of the sending MTA, from the HELO/EHLO command.
+- `{source_rdns}` – PTR record of the sending MTA IP address.
+- `{msg_id}` – Internal message identifier. Unique for each delivery.
+- `{auth_user}` – Client username, if authenticated using SASL PLAIN
+- `{sender}` – Message sender address, as specified in the MAIL FROM SMTP command.
+- `{rcpts}` – List of accepted recipient addresses, including the currently handled
+ one.
+- `{address}` – Currently handled address. This is a recipient address if the command
+ is called during RCPT TO command handling (`run_on rcpt`) or a sender
+ address if the command is called during MAIL FROM command handling (`run_on
+ sender`).
+
+If value is undefined (e.g. `{source_ip}` for a message accepted over a Unix
+socket) or unavailable (the command is executed too early), the placeholder
+is replaced with an empty string. Note that it can not remove the argument.
+E.g. `-i {source_ip}` will not become just `-i`, it will be `-i ""`
+
+Undefined placeholders are not replaced.
+
+## Command stdout
+
+The command stdout must be either empty or contain a valid RFC 5322 header.
+If it contains a byte stream that does not look a valid header, the message
+will be rejected with a temporary error.
+
+The header from stdout will be **prepended** to the message header.
+
+## Configuration directives
+
+### run_on `conn` | `sender` | `rcpt` | `body`
+Default: `body`
+
+When to run the command. This directive also affects the information visible
+for the message.
+
+- `conn`
+ Run before the sender address (MAIL FROM) is handled.
+ **Stdin**: Empty
+ **Available placeholders**: {source_ip}, {source_host}, {msg_id}, {auth_user}.
+
+- `sender`
+ Run during sender address (MAIL FROM) handling.
+ **Stdin**: Empty
+ **Available placeholders**: conn placeholders + {sender}, {address}.
+ The {address} placeholder contains the MAIL FROM address.
+
+- `rcpt`
+ Run during recipient address (RCPT TO) handling. The command is executed
+ once for each RCPT TO command, even if the same recipient is specified
+ multiple times.
+ **Stdin**: Empty
+ **Available placeholders**: sender placeholders + {rcpts}.
+ The {address} placeholder contains the recipient address.
+
+- `body`
+ Run during message body handling.
+ **Stdin**: The message header + body
+ **Available placeholders**: all except for {address}.
+
+---
+
+### code _integer_ ignore code _integer_ quarantine code _integer_ reject _smtp-code_ _smtp-enhanced-code_ _smtp-message_
+
+This directive specifies the mapping from the command exit code _integer_ to
+the message pipeline action.
+
+Two codes are defined implicitly, exit code 1 causes the message to be rejected
+with a permanent error, exit code 2 causes the message to be quarantined. Both
+actions can be overridden using the 'code' directive.
+
diff --git a/docs/reference/checks/dkim.md b/docs/reference/checks/dkim.md
new file mode 100644
index 000000000..7ab14a6d0
--- /dev/null
+++ b/docs/reference/checks/dkim.md
@@ -0,0 +1,63 @@
+# DKIM
+
+This is the check module that performs verification of the DKIM signatures
+present on the incoming messages.
+
+## Configuration directives
+
+```
+check.dkim {
+ debug no
+ required_fields From Subject
+ allow_body_subset no
+ no_sig_action ignore
+ broken_sig_action ignore
+ fail_open no
+}
+```
+
+### debug _boolean_
+Default: global directive value
+
+Log both successful and unsuccessful check executions instead of just
+unsuccessful.
+
+---
+
+### required_fields _string..._
+Default: `From Subject`
+
+Header fields that should be included in each signature. If signature
+lacks any field listed in that directive, it will be considered invalid.
+
+Note that From is always required to be signed, even if it is not included in
+this directive.
+
+---
+
+### no_sig_action _action_
+Default: `ignore` (recommended by RFC 6376)
+
+Action to take when message without any signature is received.
+
+Note that DMARC policy of the sender domain can request more strict handling of
+missing DKIM signatures.
+
+---
+
+### broken_sig_action _action_
+Default: `ignore` (recommended by RFC 6376)
+
+Action to take when there are not valid signatures in a message.
+
+Note that DMARC policy of the sender domain can request more strict handling of
+broken DKIM signatures.
+
+---
+
+### fail_open _boolean_
+Default: `no`
+
+Whether to accept the message if a temporary error occurs during DKIM
+verification. Rejecting the message with a 4xx code will require the sender
+to resend it later in a hope that the problem will be resolved.
diff --git a/docs/reference/checks/dnsbl.md b/docs/reference/checks/dnsbl.md
new file mode 100644
index 000000000..d7bb74cfa
--- /dev/null
+++ b/docs/reference/checks/dnsbl.md
@@ -0,0 +1,258 @@
+# DNSBL lookup
+
+The check.dnsbl module implements checking of source IP and hostnames against a set
+of DNS-based Blackhole lists (DNSBLs).
+
+Its configuration consists of module configuration directives and a set
+of blocks specifying lists to use and kind of lookups to perform on them.
+
+```
+check.dnsbl {
+ debug no
+ check_early no
+
+ quarantine_threshold 1
+ reject_threshold 1
+
+ # Lists configuration example.
+ dnsbl.example.org {
+ client_ipv4 yes
+ client_ipv6 no
+ ehlo no
+ mailfrom no
+ score 1
+ }
+ hsrbl.example.org {
+ client_ipv4 no
+ client_ipv6 no
+ ehlo yes
+ mailfrom yes
+ score 1
+ }
+
+ # Example with per-response-code scoring (new in 0.8)
+ zen.spamhaus.org {
+ client_ipv4 yes
+ client_ipv6 yes
+
+ # SBL - Spamhaus Block List (known spam sources)
+ response 127.0.0.2 127.0.0.3 {
+ score 10
+ message "Listed in Spamhaus SBL. See https://check.spamhaus.org/"
+ }
+
+ # XBL - Exploits Block List (compromised hosts)
+ response 127.0.0.4 127.0.0.5 127.0.0.6 127.0.0.7 {
+ score 10
+ message "Listed in Spamhaus XBL. See https://check.spamhaus.org/"
+ }
+
+ # PBL - Policy Block List (dynamic IPs)
+ response 127.0.0.10 127.0.0.11 {
+ score 5
+ message "Listed in Spamhaus PBL. See https://check.spamhaus.org/"
+ }
+ }
+}
+```
+
+## Arguments
+
+Arguments specify the list of IP-based BLs to use.
+
+The following configurations are equivalent.
+
+```
+check {
+ dnsbl dnsbl.example.org dnsbl2.example.org
+}
+```
+
+```
+check {
+ dnsbl {
+ dnsbl.example.org dnsbl2.example.org {
+ client_ipv4 yes
+ client_ipv6 no
+ ehlo no
+ mailfrom no
+ score 1
+ }
+ }
+}
+```
+
+## Configuration directives
+
+### debug _boolean_
+Default: global directive value
+
+Enable verbose logging.
+
+---
+
+### check_early _boolean_
+Default: `no`
+
+Check BLs before mail delivery starts and silently reject blacklisted clients.
+
+For this to work correctly, check should not be used in source/destination
+pipeline block.
+
+In particular, this means:
+
+- No logging is done for rejected messages.
+- No action is taken if `quarantine_threshold` is hit, only `reject_threshold`
+ applies.
+- `defer_sender_reject` from SMTP configuration takes no effect.
+- MAIL FROM is not checked, even if specified.
+
+If you often get hit by spam attacks, it is recommended to enable this
+setting to save server resources.
+
+---
+
+### quarantine_threshold _integer_
+Default: `1`
+
+DNSBL score needed (equals-or-higher) to quarantine the message.
+
+---
+
+### reject_threshold _integer_
+Default: `9999`
+
+DNSBL score needed (equals-or-higher) to reject the message.
+
+## List configuration
+
+```
+dnsbl.example.org dnsbl.example.com {
+ client_ipv4 yes
+ client_ipv6 no
+ ehlo no
+ mailfrom no
+ responses 127.0.0.1/24
+ score 1
+}
+```
+
+Directive name and arguments specify the actual DNS zone to query when checking
+the list. Using multiple arguments is equivalent to specifying the same
+configuration separately for each list.
+
+### client_ipv4 _boolean_
+Default: `yes`
+
+Whether to check address of the IPv4 clients against the list.
+
+---
+
+### client_ipv6 _boolean_
+Default: `yes`
+
+Whether to check address of the IPv6 clients against the list.
+
+---
+
+### ehlo _boolean_
+Default: `no`
+
+Whether to check hostname specified n the HELO/EHLO command
+against the list.
+
+This works correctly only with domain-based DNSBLs.
+
+---
+
+### mailfrom _boolean_
+Default: `no`
+
+Whether to check domain part of the MAIL FROM address against the list.
+
+This works correctly only with domain-based DNSBLs.
+
+---
+
+### responses _cidr_ | _ip..._
+Default: `127.0.0.1/24`
+
+IP networks (in CIDR notation) or addresses to permit in list lookup results.
+Addresses not matching any entry in this directives will be ignored.
+
+---
+
+### score _integer_
+Default: `1`
+
+Score value to add for the message if it is listed.
+
+If sum of list scores is equals or higher than `quarantine_threshold`, the
+message will be quarantined.
+
+If sum of list scores is equals or higher than `rejected_threshold`, the message
+will be rejected.
+
+It is possible to specify a negative value to make list act like a whitelist
+and override results of other blocklists.
+
+**Note:** When using `response` blocks (see below), the score from matching response
+rules is used instead of this flat score value.
+
+---
+
+### response _ip..._
+
+Defines per-response-code rules for scoring and custom messages. This is useful
+for combined DNSBLs like Spamhaus ZEN that return different codes for different
+listing types.
+
+This works for both IP-based lookups (client_ipv4, client_ipv6) and domain-based
+lookups (ehlo, mailfrom).
+
+Each `response` block takes one or more IP addresses or CIDR ranges as arguments
+and contains the following directives:
+
+#### score _integer_
+**Required**
+
+Score to add when this response code is returned. If multiple response codes
+are returned by the DNSBL, and they match different rules, the scores from
+all matched rules are summed together. Each rule is counted only once, even
+if multiple returned IPs match networks within that rule.
+
+#### message _string_
+**Optional**
+
+Custom rejection or quarantine message to include when this response code
+matches. This message is shown to the client or logged when the threshold
+is reached.
+
+**Example:**
+
+```
+zen.spamhaus.org {
+ client_ipv4 yes
+
+ # High severity - known spam sources
+ response 127.0.0.2 127.0.0.3 {
+ score 10
+ message "Listed in Spamhaus SBL"
+ }
+
+ # Lower severity - dynamic IPs
+ response 127.0.0.10 127.0.0.11 {
+ score 5
+ message "Listed in Spamhaus PBL"
+ }
+}
+```
+
+**Scoring behavior:**
+- If DNSBL returns `127.0.0.2` only → Score: 10 (matches first rule)
+- If DNSBL returns `127.0.0.11` only → Score: 5 (matches second rule)
+- If DNSBL returns both `127.0.0.2` and `127.0.0.11` → Score: 15 (both rules match, scores sum)
+- If DNSBL returns both `127.0.0.2` and `127.0.0.3` → Score: 10 (same rule matches, counted once)
+
+**Backwards compatibility:** When `response` blocks are not used, the legacy
+`responses` and `score` directives work as before.
diff --git a/docs/reference/checks/milter.md b/docs/reference/checks/milter.md
new file mode 100644
index 000000000..8286a79b1
--- /dev/null
+++ b/docs/reference/checks/milter.md
@@ -0,0 +1,49 @@
+# Milter client
+
+The 'milter' implements subset of Sendmail's milter protocol that can be used
+to integrate external software with maddy.
+maddy implements version 6 of the protocol, older versions are
+not supported.
+
+Notable limitations of protocol implementation in maddy include:
+1. Changes of envelope sender address are not supported
+2. Removal and addition of envelope recipients is not supported
+3. Removal and replacement of header fields is not supported
+4. Headers fields can be inserted only on top
+5. Milter does not receive some "macros" provided by sendmail.
+
+Restrictions 1 and 2 are inherent to the maddy checks interface and cannot be
+removed without major changes to it. Restrictions 3, 4 and 5 are temporary due to
+incomplete implementation.
+
+```
+check.milter {
+ endpoint
+ fail_open false
+}
+
+milter
+```
+
+## Arguments
+
+When defined inline, the first argument specifies endpoint to access milter
+via. See below.
+
+## Configuration directives
+
+### endpoint _scheme://path_
+Default: not set
+
+Specifies milter protocol endpoint to use.
+The endpoit is specified in standard URL-like format:
+`tcp://127.0.0.1:6669` or `unix:///var/lib/milter/filter.sock`
+
+---
+
+### fail_open _boolean_
+Default: `false`
+
+Toggles behavior on milter I/O errors. If false ("fail closed") - message is
+rejected with temporary error code. If true ("fail open") - check is skipped.
+
diff --git a/docs/reference/checks/misc.md b/docs/reference/checks/misc.md
new file mode 100644
index 000000000..19c71ad09
--- /dev/null
+++ b/docs/reference/checks/misc.md
@@ -0,0 +1,48 @@
+# Misc checks
+
+## Configuration directives
+
+Following directives are defined for all modules listed below.
+
+### fail_action `ignore` | `reject` | `quarantine`
+Default: `quarantine`
+
+Action to take when check fails. See [Check actions](../actions/) for details.
+
+---
+
+### debug _boolean_
+Default: global directive value
+
+Log both successful and unsuccessful check executions instead of just
+unsuccessful.
+
+---
+
+### require_mx_record
+
+Check that domain in MAIL FROM command does have a MX record and none of them
+are "null" (contain a single dot as the host).
+
+By default, quarantines messages coming from servers missing MX records,
+use `fail_action` directive to change that.
+
+---
+
+### require_matching_rdns
+
+Check that source server IP does have a PTR record point to the domain
+specified in EHLO/HELO command.
+
+By default, quarantines messages coming from servers with mismatched or missing
+PTR record, use `fail_action` directive to change that.
+
+---
+
+### require_tls
+
+Check that the source server is connected via TLS; either directly, or by using
+the STARTTLS command.
+
+By default, rejects messages coming from unencrypted servers. Use the
+`fail_action` directive to change that.
\ No newline at end of file
diff --git a/docs/reference/checks/rspamd.md b/docs/reference/checks/rspamd.md
new file mode 100644
index 000000000..f37f5dcc0
--- /dev/null
+++ b/docs/reference/checks/rspamd.md
@@ -0,0 +1,113 @@
+# rspamd
+
+The 'rspamd' module implements message filtering by contacting the rspamd
+server via HTTP API.
+
+```
+check.rspamd {
+ tls_client { ... }
+ api_path http://127.0.0.1:11333
+ settings_id whatever
+ tag maddy
+ hostname mx.example.org
+ io_error_action ignore
+ error_resp_action ignore
+ add_header_action quarantine
+ rewrite_subj_action quarantine
+ reject_action reject
+ soft_reject_action reject
+ flags pass_all
+}
+
+rspamd http://127.0.0.1:11333
+```
+
+## Configuration directives
+
+### tls_client { ... }
+Default: not set
+
+Configure TLS client if HTTPS is used. See [TLS configuration / Client](/reference/tls/#client) for details.
+
+---
+
+### api_path _url_
+Default: `http://127.0.0.1:11333`
+
+URL of HTTP API endpoint. Supports both HTTP and HTTPS and can include
+path element.
+
+---
+
+### settings_id _string_
+Default: not set
+
+Settings ID to pass to the server.
+
+---
+
+### tag _string_
+Default: `maddy`
+
+Value to send in MTA-Tag header field.
+
+---
+
+### hostname _string_
+Default: value of global directive
+
+Value to send in MTA-Name header field.
+
+---
+
+### io_error_action _action_
+Default: `ignore`
+
+Action to take in case of inability to contact the rspamd server.
+
+---
+
+### error_resp_action _action_
+Default: `ignore`
+
+Action to take in case of 5xx or 4xx response received from the rspamd server.
+
+---
+
+### add_header_action _action_
+Default: `quarantine`
+
+Action to take when rspamd requests to "add header".
+
+X-Spam-Flag and X-Spam-Score are added to the header irregardless of value.
+
+---
+
+### rewrite_subj_action _action_
+Default: `quarantine`
+
+Action to take when rspamd requests to "rewrite subject".
+
+X-Spam-Flag and X-Spam-Score are added to the header irregardless of value.
+
+---
+
+### reject_action _action_
+Default: `reject`
+
+Action to take when rspamd requests to "reject".
+
+---
+
+### soft_reject_action _action_
+Default: `reject`
+
+Action to take when rspamd requests to "soft reject".
+
+---
+
+### flags _string-list..._
+Default: `pass_all`
+
+Flags to pass to the rspamd server.
+See [https://rspamd.com/doc/architecture/protocol.html](https://rspamd.com/doc/architecture/protocol.html) for details.
diff --git a/docs/reference/checks/spf.md b/docs/reference/checks/spf.md
new file mode 100644
index 000000000..f0afb3473
--- /dev/null
+++ b/docs/reference/checks/spf.md
@@ -0,0 +1,97 @@
+# SPF
+
+check.spf the check module that verifies whether IP address of the client is
+authorized to send messages for domain in MAIL FROM address.
+
+SPF statuses are mapped to maddy check actions in a way
+specified by \*_action directives. By default, SPF failure
+results in the message being quarantined and errors (both permanent and
+temporary) cause message to be rejected.
+Authentication-Results field is generated irregardless of status.
+
+## DMARC override
+
+It is recommended by the DMARC standard to don't fail delivery based solely on
+SPF policy and always check DMARC policy and take action based on it.
+
+If `enforce_early` is `no`, check.spf module will not take any action on SPF
+policy failure if sender domain does have a DMARC record with 'quarantine' or
+'reject' policy. Instead it will rely on DMARC support to take necesary
+actions using SPF results as an input.
+
+Disabling `enforce_early` without enabling DMARC support will make SPF policies
+no-op and is considered insecure.
+
+## Configuration directives
+
+```
+check.spf {
+ debug no
+ enforce_early no
+ fail_action quarantine
+ softfail_action ignore
+ permerr_action reject
+ temperr_action reject
+}
+```
+
+### debug _boolean_
+Default: global directive value
+
+Enable verbose logging for check.spf.
+
+---
+
+### enforce_early _boolean_
+Default: `no`
+
+Make policy decision on MAIL FROM stage (before the message body is received).
+This makes it impossible to apply DMARC override (see above).
+
+---
+
+### none_action `reject` | `quarantine` | `ignore`
+Default: `ignore`
+
+Action to take when SPF policy evaluates to a 'none' result.
+
+See [https://tools.ietf.org/html/rfc7208#section-2.6](https://tools.ietf.org/html/rfc7208#section-2.6) for meaning of
+SPF results.
+
+---
+
+### neutral_action `reject` | `quarantine` | `ignore`
+Default: `ignore`
+
+Action to take when SPF policy evaluates to a 'neutral' result.
+
+See [https://tools.ietf.org/html/rfc7208#section-2.6](https://tools.ietf.org/html/rfc7208#section-2.6) for meaning of
+SPF results.
+
+---
+
+### fail_action `reject` | `quarantine` | `ignore`
+Default: `quarantine`
+
+Action to take when SPF policy evaluates to a 'fail' result.
+
+---
+
+### softfail_action `reject` | `quarantine` | `ignore`
+Default: `ignore`
+
+Action to take when SPF policy evaluates to a 'softfail' result.
+
+---
+
+### permerr_action `reject` | `quarantine` | `ignore`
+Default: `reject`
+
+Action to take when SPF policy evaluates to a 'permerror' result.
+
+---
+
+### temperr_action `reject` | `quarantine` | `ignore`
+Default: `reject`
+
+Action to take when SPF policy evaluates to a 'temperror' result.
diff --git a/docs/man/maddy-config.5.scd b/docs/reference/config-syntax.md
similarity index 92%
rename from docs/man/maddy-config.5.scd
rename to docs/reference/config-syntax.md
index ba29a38a1..72e18d411 100644
--- a/docs/man/maddy-config.5.scd
+++ b/docs/reference/config-syntax.md
@@ -1,6 +1,7 @@
-maddy-config(5) "maddy mail server" "maddy reference documentation"
+# Configuration files syntax
-; TITLE Configuration files syntax
+**Note:** This file is a technical document describing how
+maddy parses configuration files.
Configuration consists of newline-delimited "directives". Each directive can
have zero or more arguments.
@@ -181,24 +182,19 @@ Also note that the following is not valid, unlike Duration values syntax:
32M5K
```
-# ADDRESS DEFINITIONS
+## Address Definitions
Maddy configuration uses URL-like syntax to specify network addresses.
-- unix://file_path
- Unix domain socket. Relative paths are relative to runtime directory
- (/run/maddy).
+- `unix://file_path` – Unix domain socket. Relative paths are relative to runtime directory (`/run/maddy`).
+- `tcp://ADDRESS:PORT` – TCP/IP socket.
+- `tls://ADDRESS:PORT` – TCP/IP socket using TLS.
-- tcp://ADDRESS:PORT
- TCP/IP socket.
-
-- tls://ADDRESS:PORT
- TCP/IP socket using TLS.
-
-# DUMMY MODULE
+## Dummy Module
No-op module. It doesn't need to be configured explicitly and can be referenced
using "dummy" name. It can act as a delivery target or auth.
provider. In the latter case, it will accept any credentials, allowing any
client to authenticate using any username and password (use with care!).
+
diff --git a/docs/reference/endpoints/imap.md b/docs/reference/endpoints/imap.md
new file mode 100644
index 000000000..ec0d2e602
--- /dev/null
+++ b/docs/reference/endpoints/imap.md
@@ -0,0 +1,164 @@
+# IMAP4rev1 endpoint
+
+Module 'imap' is a listener that implements IMAP4rev1 protocol and provides
+access to local messages storage specified by 'storage' directive.
+
+In most cases, local storage modules will auto-create accounts when they are
+accessed via IMAP. This relies on authentication provider used by IMAP endpoint
+to provide what essentially is access control. There is a caveat, however: this
+auto-creation will not happen when delivering incoming messages via SMTP as
+there is no authentication to confirm that this account should indeed be
+created.
+
+## Configuration directives
+
+```
+imap tcp://0.0.0.0:143 tls://0.0.0.0:993 {
+ tls /etc/ssl/private/cert.pem /etc/ssl/private/pkey.key
+ io_debug no
+ debug no
+ insecure_auth no
+ sasl_login no
+ auth pam
+ storage &local_mailboxes
+ auth_map identity
+ auth_map_normalize auto
+ storage_map identity
+ storage_map_normalize auto
+}
+```
+
+### tls _certificate-path_ _key-path_ { ... }
+Default: global directive value
+
+TLS certificate & key to use. Fine-tuning of other TLS properties is possible
+by specifying a configuration block and options inside it:
+
+```
+tls cert.crt key.key {
+ protocols tls1.2 tls1.3
+}
+```
+
+See [TLS configuration / Server](/reference/tls/#server-side) for details.
+
+---
+
+### proxy_protocol _trusted ips..._ { ... }
+Default: not enabled
+
+Enable use of HAProxy PROXY protocol. Supports both v1 and v2 protocols.
+If a list of trusted IP addresses or subnets is provided, only connections
+from those will be trusted.
+
+TLS for the channel between the proxies and maddy can be configured
+using a 'tls' directive:
+```
+proxy_protocol {
+ trust 127.0.0.1 ::1 192.168.0.1/24
+ tls &proxy_tls
+}
+```
+Note that the top-level 'tls' directive is not inherited here. If you
+need TLS on top of the PROXY protocol, securing the protocol header,
+you must declare TLS explicitly.
+
+---
+
+### io_debug _boolean_
+Default: `no`
+
+Write all commands and responses to stderr.
+
+---
+
+### io_errors _boolean_
+Default: `no`
+
+Log I/O errors.
+
+---
+
+### debug _boolean_
+Default: global directive value
+
+Enable verbose logging.
+
+---
+
+### insecure_auth _boolean_
+Default: `no` (`yes` if TLS is disabled)
+
+Allow plain-text authentication over unencrypted connections.
+
+---
+
+### sasl_login _boolean_
+Default: `no`
+
+Enable support for SASL LOGIN authentication mechanism used by
+some outdated clients.
+
+---
+
+### auth _module-reference_
+**Required.**
+
+Use the specified module for authentication.
+
+---
+
+### storage _module-reference_
+**Required.**
+
+Use the specified module for message storage.
+
+---
+
+### storage_map _module-reference_
+Default: `identity`
+
+Use the specified table to map SASL usernames to storage account names.
+
+Before username is looked up, it is normalized using function defined by
+`storage_map_normalize`.
+
+This directive is useful if you want users user@example.org and user@example.com
+to share the same storage account named "user". In this case, use
+
+```
+ storage_map email_localpart
+```
+
+Note that `storage_map` does not affect the username passed to the
+authentication provider.
+
+It also does not affect how message delivery is handled, you should specify
+`delivery_map` in storage module to define how to map email addresses
+to storage accounts. E.g.
+
+```
+ storage.imapsql local_mailboxes {
+ ...
+ delivery_map email_localpart # deliver "user@*" to mailbox for "user"
+ }
+```
+
+---
+
+### storage_map_normalize _function_
+Default: `auto`
+
+Same as `auth_map_normalize` but for `storage_map`.
+
+---
+
+### auth_map_normalize _function_
+Default: `auto`
+
+Overrides global `auth_map_normalize` value for this endpoint.
+
+See [Global configuration](/reference/global-config) for details.
+
+
+
diff --git a/docs/openmetrics.md b/docs/reference/endpoints/openmetrics.md
similarity index 99%
rename from docs/openmetrics.md
rename to docs/reference/endpoints/openmetrics.md
index df77665cd..f455f716a 100644
--- a/docs/openmetrics.md
+++ b/docs/reference/endpoints/openmetrics.md
@@ -4,6 +4,7 @@ Various server statistics are provided in OpenMetrics format by the
"openmetrics" module.
To enable it, add the following line to the server config:
+
```
openmetrics tcp://127.0.0.1:9749 { }
```
diff --git a/docs/reference/endpoints/smtp.md b/docs/reference/endpoints/smtp.md
new file mode 100644
index 000000000..f5fb7b4ae
--- /dev/null
+++ b/docs/reference/endpoints/smtp.md
@@ -0,0 +1,321 @@
+# SMTP/LMTP/Submission endpoint
+
+Module 'smtp' is a listener that implements ESMTP protocol with optional
+authentication, LMTP and Submission support. Incoming messages are processed in
+accordance with pipeline rules (explained in Message pipeline section below).
+
+```
+smtp tcp://0.0.0.0:25 {
+ hostname example.org
+ tls /etc/ssl/private/cert.pem /etc/ssl/private/pkey.key
+ io_debug no
+ debug no
+ insecure_auth no
+ sasl_login no
+ read_timeout 10m
+ write_timeout 1m
+ shutdown_timeout 3m
+ max_message_size 32M
+ max_header_size 1M
+ auth pam
+ defer_sender_reject yes
+ dmarc yes
+ smtp_max_line_length 4000
+ limits {
+ endpoint rate 10
+ endpoint concurrency 500
+ }
+
+ # Example pipeline configuration.
+ destination example.org {
+ deliver_to &local_mailboxes
+ }
+ default_destination {
+ reject
+ }
+}
+```
+
+## Configuration directives
+
+### hostname _string_
+Default: global directive value
+
+Server name to use in SMTP banner.
+
+```
+220 example.org ESMTP Service Ready
+```
+
+---
+
+### tls _certificate-path_ _key-path_ { ... }
+Default: global directive value
+
+TLS certificate & key to use. Fine-tuning of other TLS properties is possible
+by specifying a configuration block and options inside it:
+
+```
+tls cert.crt key.key {
+ protocols tls1.2 tls1.3
+}
+```
+
+See [TLS configuration / Server](/reference/tls/#server-side) for details.
+
+---
+
+### proxy_protocol _trusted ips..._ { ... }
+Default: not enabled
+
+Enable use of HAProxy PROXY protocol. Supports both v1 and v2 protocols.
+If a list of trusted IP addresses or subnets is provided, only connections
+from those will be trusted.
+
+TLS for the channel between the proxies and maddy can be configured
+using a 'tls' directive:
+```
+proxy_protocol {
+ trust 127.0.0.1 ::1 192.168.0.1/24
+ tls &proxy_tls
+}
+```
+
+---
+
+### io_debug _boolean_
+Default: `no`
+
+Write all commands and responses to stderr.
+
+---
+
+### debug _boolean_
+Default: global directive value
+
+Enable verbose logging.
+
+---
+
+### insecure_auth _boolean_
+Default: `no` (`yes` if TLS is disabled)
+
+Allow plain-text authentication over unencrypted connections. Not recommended!
+
+---
+
+### sasl_login _boolean_
+Default: `no`
+
+Enable support for SASL LOGIN authentication mechanism used by
+some outdated clients.
+
+---
+
+### read_timeout _duration_
+Default: `10m`
+
+I/O read timeout.
+
+---
+
+### write_timeout _duration_
+Default: `1m`
+
+I/O write timeout.
+
+---
+
+### shutdown_timeout _duration_
+Default: `3m`
+
+Time to wait until forcibly closing connections on server shutdown
+or configuration reload.
+
+---
+
+### max_message_size _size_
+Default: `32M`
+
+Limit the size of incoming messages to 'size'.
+
+---
+
+### max_header_size _size_
+Default: `1M`
+
+Limit the size of incoming message headers to 'size'.
+
+---
+
+### auth _module-reference_
+Default: not specified
+
+Use the specified module for authentication.
+
+---
+
+### defer_sender_reject _boolean_
+Default: `yes`
+
+Apply sender-based checks and routing logic when first RCPT TO command
+is received. This allows maddy to log recipient address of the rejected
+message and also improves interoperability with (improperly implemented)
+clients that don't expect an error early in session.
+
+---
+
+### max_logged_rcpt_errors _integer_
+Default: `5`
+
+Amount of RCPT-time errors that should be logged. Further errors will be
+handled silently. This is to prevent log flooding during email dictionary
+attacks (address probing).
+
+---
+
+### max_received _integer_
+Default: `50`
+
+Max. amount of Received header fields in the message header. If the incoming
+message has more fields than this number, it will be rejected with the permanent error
+5.4.6 ("Routing loop detected").
+
+---
+
+### buffer `ram` buffer `fs` _path_ buffer `auto` _max-size_ _path_
+Default: `auto 1M StateDirectory/buffer`
+
+Temporary storage to use for the body of accepted messages.
+
+- `ram` – Store the body in RAM.
+- `fs` – Write out the message to the FS and read it back as needed.
+_path_ can be omitted and defaults to StateDirectory/buffer.
+- `auto` – Store message bodies smaller than `_max_size_` entirely in RAM,
+otherwise write them out to the FS. _path_ can be omitted and defaults to `StateDirectory/buffer`.
+
+---
+
+### smtp_max_line_length _integer_
+Default: `4000`
+
+The maximum line length allowed in the SMTP input stream. If client sends a
+longer line - connection will be closed and message (if any) will be rejected
+with a permanent error.
+
+RFC 5321 has the recommended limit of 998 bytes. Servers are not required
+to handle longer lines correctly but some senders may produce them.
+
+Unless BDAT extension is used by the sender, this limitation also applies to
+the message body.
+
+---
+
+### dmarc _boolean_
+Default: `yes`
+
+Enforce sender's DMARC policy. Due to implementation limitations, it is not a
+check module.
+
+**Note**: Report generation is not implemented now.
+
+**Note**: DMARC needs SPF and DKIM checks to function correctly.
+Without these, DMARC check will not run.
+
+---
+
+## Rate & concurrency limiting
+
+### limits { ... }
+Default: no limits
+
+This allows configuring a set of message flow restrictions including
+max. concurrency and rate per-endpoint, per-source, per-destination.
+
+Limits are specified as directives inside the block:
+
+```
+limits {
+ all rate 20
+ destination concurrency 5
+}
+```
+
+Supported limits:
+
+### _scope_ rate _burst_ _period_
+
+Rate limit. Restrict the amount of messages processed in _period_ to
+_burst_ messages. If period is not specified, 1 second is used.
+
+### _scope_ concurrency _max_
+Concurrency limit. Restrict the amount of messages processed in parallel
+to _max_.
+
+For each supported limitation, _scope_ determines whether it should be applied
+for all messages ("all"), per-sender IP ("ip"), per-sender domain ("source") or
+per-recipient domain ("destination"). Having a scope other than "all" means
+that the restriction will be enforced independently for each group determined
+by scope. E.g. "ip rate 20" means that the same IP cannot send more than 20
+messages per second. "destination concurrency 5" means that no more than 5
+messages can be sent in parallel to a single domain.
+
+**Note**: At the moment, SMTP endpoint on its own does not support per-recipient
+limits. They will be no-op. If you want to enforce a per-recipient restriction
+on outbound messages, do so using 'limits' directive for the 'table.remote' module
+
+It is possible to share limit counters between multiple endpoints (or any other
+modules). To do so define a top-level configuration block for module "limits"
+and reference it where needed using standard & syntax. E.g.
+
+```
+limits inbound_limits {
+ all rate 20
+}
+
+smtp smtp://0.0.0.0:25 {
+ limits &inbound_limits
+ ...
+}
+
+submission tls://0.0.0.0:465 {
+ limits &inbound_limits
+ ...
+}
+```
+
+Using an "all rate" restriction in such way means that no more than 20
+messages can enter the server through both endpoints in one second.
+
+# Submission module (submission)
+
+Module 'submission' implements all functionality of the 'smtp' module and adds
+certain message preprocessing on top of it, additionally authentication is
+always required.
+
+'submission' module checks whether addresses in header fields From, Sender, To,
+Cc, Bcc, Reply-To are correct and adds Message-ID and Date if it is missing.
+
+```
+submission tcp://0.0.0.0:587 tls://0.0.0.0:465 {
+ # ... same as smtp ...
+}
+```
+
+# LMTP module (lmtp)
+
+Module 'lmtp' implements all functionality of the 'smtp' module but uses
+LMTP (RFC 2033) protocol.
+
+```
+lmtp unix://lmtp.sock {
+ # ... same as smtp ...
+}
+```
+
+## Limitations of LMTP implementation
+
+- Can't be used with TCP.
+- Delivery to 'sql' module storage is always atomic, either all recipients will
+ succeed or none of them will.
+
diff --git a/docs/reference/global-config.md b/docs/reference/global-config.md
new file mode 100644
index 000000000..db0ec1a34
--- /dev/null
+++ b/docs/reference/global-config.md
@@ -0,0 +1,153 @@
+# Global configuration directives
+
+These directives can be specified outside of any
+configuration blocks and they are applied to all modules.
+
+Some directives can be overridden on per-module basis (e.g. hostname).
+
+### state_dir _path_
+Default: `/var/lib/maddy`
+
+The path to the state directory. This directory will be used to store all
+persistent data and should be writable.
+
+---
+
+### runtime_dir _path_
+Default: `/run/maddy`
+
+The path to the runtime directory. Used for Unix sockets and other temporary
+objects. Should be writable.
+
+---
+
+### hostname _domain_
+Default: not specified
+
+Internet hostname of this mail server. Typicall FQDN is used. It is recommended
+to make sure domain specified here resolved to the public IP of the server.
+
+---
+
+### auth_map _module-reference_
+Default: `identity`
+
+Use the specified table to translate SASL usernames before passing it to the
+authentication provider.
+
+Before username is looked up, it is normalized using function defined by
+`auth_map_normalize`.
+
+Note that `auth_map` does not affect the storage account name used. You probably
+should also use `storage_map` in IMAP config block to handle this.
+
+This directive is useful if used authentication provider does not support
+using emails as usernames but you still want users to have separate mailboxes
+on separate domains. In this case, use it with `email_localpart` table:
+
+```
+ auth_map email_localpart
+```
+
+With this configuration, `user@example.org` and `user@example.com` will use
+`user` credentials when authenticating, but will access `user@example.org` and
+`user@example.com` mailboxes correspondingly. If you want to also accept
+`user` as a username, use `auth_map email_localpart_optional`.
+
+If you want `user@example.org` and `user@example.com` to have the same mailbox,
+also set `storage_map` in IMAP config block to use `email_localpart`
+(or `email_localpart_optional` if you want to also accept just "user"):
+
+```
+ storage_map email_localpart
+```
+
+In this case you will need to create storage accounts without domain part in
+the name:
+
+```
+maddy imap-acct create user # instead of user@example.org
+```
+
+---
+
+### auth_map_normalize _function_
+Default: `auto`
+
+Normalization function to apply to SASL usernames before mapping
+them to storage accounts.
+
+Available options:
+
+- `auto` `precis_casefold_email` for valid emails, `precis_casefold` otherwise.
+- `precis_casefold_email` PRECIS UsernameCaseMapped profile + U-labels form for domain
+- `precis_casefold` PRECIS UsernameCaseMapped profile for the entire string
+- `precis_email` PRECIS UsernameCasePreserved profile + U-labels form for domain
+- `precis` PRECIS UsernameCasePreserved profile for the entire string
+- `casefold` Convert to lower case
+- `noop` Nothing
+
+---
+
+### autogenerated_msg_domain _domain_
+Default: not specified
+
+Domain that is used in From field for auto-generated messages (such as Delivery
+Status Notifications).
+
+---
+
+### tls `file` _cert-file_ _pkey-file_ | _module-reference_ | `off`
+Default: not specified
+
+Default TLS certificate to use for all endpoints.
+
+Must be present in either all endpoint modules configuration blocks or as
+global directive.
+
+You can also specify other configuration options such as cipher suites and TLS
+version. See maddy-tls(5) for details. maddy uses reasonable
+cipher suites and TLS versions by default so you generally don't have to worry
+about it.
+
+---
+
+### tls_client { ... }
+Default: not specified
+
+This is optional block that specifies various TLS-related options to use when
+making outbound connections. See TLS client configuration for details on
+directives that can be used in it. maddy uses reasonable cipher suites and TLS
+versions by default so you generally don't have to worry about it.
+
+---
+
+### log _targets..._ | `off`
+Default: `stderr`
+
+Write log to one of more "targets".
+
+The target can be one or the following:
+
+- `stderr` – Write logs to stderr.
+- `stderr_ts` – Write logs to stderr with timestamps.
+- `syslog` – Send logs to the local syslog daemon.
+- _file path_ – Write (append) logs to file.
+
+Example:
+
+```
+log syslog /var/log/maddy.log
+```
+
+**Note:** Maddy does not perform log files rotation, this is the job of the
+logrotate daemon. Send SIGUSR1 to maddy process to make it reopen log files.
+
+---
+
+### debug _boolean_
+Default: `no`
+
+Enable verbose logging for all modules. You don't need that unless you are
+reporting a bug.
+
diff --git a/docs/reference/modifiers/dkim.md b/docs/reference/modifiers/dkim.md
new file mode 100644
index 000000000..36fffe275
--- /dev/null
+++ b/docs/reference/modifiers/dkim.md
@@ -0,0 +1,225 @@
+# DKIM signing
+
+modify.dkim module is a modifier that signs messages using DKIM
+protocol (RFC 6376).
+
+Each configuration block specifies a single selector
+and one or more domains.
+
+A key will be generated or read for each domain, the key to use
+for each message will be selected based on the SMTP envelope sender. Exception
+for that is that for domain-less postmaster address and null address, the
+key for the first domain will be used. If domain in envelope sender
+does not match any of loaded keys, message will not be signed.
+Additionally, for each messages From header is checked to
+match MAIL FROM and authorization identity (username sender is logged in as).
+This can be controlled using require_sender_match directive.
+
+Generated private keys are stored in unencrypted PKCS#8 format
+in state_directory/dkim_keys (`/var/lib/maddy/dkim_keys`).
+In the same directory .dns files are generated that contain
+public key for each domain formatted in the form of a DNS record.
+
+## Arguments
+
+domains and selector can be specified in arguments, so actual modify.dkim use can
+be shortened to the following:
+
+```
+modify {
+ dkim example.org selector
+}
+```
+
+## Configuration directives
+
+```
+modify.dkim {
+ debug no
+ domains example.org example.com
+ selector default
+ key_path dkim-keys/{domain}-{selector}.key
+ oversign_fields ...
+ sign_fields ...
+ header_canon relaxed
+ body_canon relaxed
+ sig_expiry 120h # 5 days
+ hash sha256
+ newkey_algo rsa2048
+}
+```
+
+### debug _boolean_
+Default: global directive value
+
+Enable verbose logging.
+
+---
+
+### domains _string-list_
+**Required**.
+Default: not specified
+
+
+ADministrative Management Domains (ADMDs) taking responsibility for messages.
+
+Should be specified either as a directive or as an argument.
+
+---
+
+### selector _string_
+**Required**.
+Default: not specified
+
+Identifier of used key within the ADMD.
+Should be specified either as a directive or as an argument.
+
+---
+
+### key_path _string_
+Default: `dkim_keys/{domain}_{selector}.key`
+
+Path to private key. It should be in PKCS#8 format wrapped in PAM encoding.
+If key does not exist, it will be generated using algorithm specified
+in newkey_algo.
+
+Placeholders '{domain}' and '{selector}' will be replaced with corresponding
+values from domain and selector directives.
+
+Additionally, keys in PKCS#1 ("RSA PRIVATE KEY") and
+RFC 5915 ("EC PRIVATE KEY") can be read by modify.dkim. Note, however that
+newly generated keys are always in PKCS#8.
+
+---
+
+### oversign_fields _list..._
+Default: see below
+
+Header fields that should be signed n+1 times where n is times they are
+present in the message. This makes it impossible to replace field
+value by prepending another field with the same name to the message.
+
+Fields specified here don't have to be also specified in `sign_fields`.
+
+Default set of oversigned fields:
+
+- Subject
+- To
+- From
+- Date
+- MIME-Version
+- Content-Type
+- Content-Transfer-Encoding
+- Reply-To
+- Message-Id
+- References
+- Autocrypt
+- Openpgp
+
+---
+
+### sign_fields _list..._
+Default: see below
+
+Header fields that should be signed n times where n is times they are
+present in the message. For these fields, additional values can be prepended
+by intermediate relays, but existing values can't be changed.
+
+Default set of signed fields:
+
+- List-Id
+- List-Help
+- List-Unsubscribe
+- List-Post
+- List-Owner
+- List-Archive
+- Resent-To
+- Resent-Sender
+- Resent-Message-Id
+- Resent-Date
+- Resent-From
+- Resent-Cc
+
+---
+
+### header_canon `relaxed` | `simple`
+Default: `relaxed`
+
+Canonicalization algorithm to use for header fields. With `relaxed`, whitespace within
+fields can be modified without breaking the signature, with `simple` no
+modifications are allowed.
+
+---
+
+### body_canon `relaxed` | `simple`
+Default: `relaxed`
+
+Canonicalization algorithm to use for message body. With `relaxed`, whitespace within
+can be modified without breaking the signature, with `simple` no
+modifications are allowed.
+
+---
+
+### sig_expiry _duration_
+Default: `120h`
+
+Time for which signature should be considered valid. Mainly used to prevent
+unauthorized resending of old messages.
+
+---
+
+### hash _hash_
+Default: `sha256`
+
+Hash algorithm to use when computing body hash.
+
+sha256 is the only supported algorithm now.
+
+---
+
+### newkey_algo `rsa4096` | `rsa2048` | `ed25519`
+Default: `rsa2048`
+
+Algorithm to use when generating a new key.
+
+Currently ed25519 is **not** supported by most platforms.
+
+---
+
+### require_sender_match _ids..._
+Default: `envelope auth`
+
+Require specified identifiers to match From header field and key domain,
+otherwise - don't sign the message.
+
+If From field contains multiple addresses, message will not be
+signed unless `allow_multiple_from` is also specified. In that
+case only first address will be compared.
+
+Matching is done in a case-insensitive way.
+
+Valid values:
+
+- `off` – Disable check, always sign.
+- `envelope` – Require MAIL FROM address to match From header.
+- `auth` – If authorization identity contains @ - then require it to
+ fully match From header. Otherwise, check only local-part
+ (username).
+
+---
+
+### allow_multiple_from _boolean_
+Default: `no`
+
+Allow multiple addresses in From header field for purposes of
+`require_sender_match` checks. Only first address will be checked, however.
+
+---
+
+### sign_subdomains _boolean_
+Default: `no`
+
+Sign emails from subdomains using a top domain key.
+
+Allows only one domain to be specified (can be worked around by using `modify.dkim`
+multiple times).
diff --git a/docs/reference/modifiers/envelope.md b/docs/reference/modifiers/envelope.md
new file mode 100644
index 000000000..0e101cf62
--- /dev/null
+++ b/docs/reference/modifiers/envelope.md
@@ -0,0 +1,63 @@
+# Envelope sender / recipient rewriting
+
+`replace_sender` and `replace_rcpt` modules replace SMTP envelope addresses
+based on the mapping defined by the table module (maddy-tables(5)). It is possible
+to specify 1:N mappings. This allows, for example, implementing mailing lists.
+
+The address is normalized before lookup (Punycode in domain-part is decoded,
+Unicode is normalized to NFC, the whole string is case-folded).
+
+First, the whole address is looked up. If there is no replacement, local-part
+of the address is looked up separately and is replaced in the address while
+keeping the domain part intact. Replacements are not applied recursively, that
+is, lookup is not repeated for the replacement.
+
+Recipients are not deduplicated after expansion, so message may be delivered
+multiple times to a single recipient. However, used delivery target can apply
+such deduplication (imapsql storage does it).
+
+Definition:
+
+```
+replace_rcpt