Configuration

Gateway

Example: gateway.yaml

global: executor/thread limits

rest_listener: host/port/certs for http

rest_output: http endpoint of router and host for output chain

smtp_output: rest host endpoints for the router to send to

smtp_listener: host/port/certs, specify which rest_output host to use

logging: logging.dictConfig

Router

Example: router.yaml

global: executor/thread limits

rest_listener: host/port/certs for http

sender: principals/roles for sending messages, cf below

endpoint: output chains, cf below

rest_endpoint: referenced from recipient routing destination

modules: user plugins: output chain filters, recipient routing policies

storage: sqlalchemy db url

logging: logging.dictConfig

Senders

name: rest url path /senders/<name>/transactions

output_chain: how to handle messages for this sender

retry: output_chain to use the parameters from the output chain or null to disable

notification: cf retry

upstream_sender/upstream_tag: exploder rewrites sender/tag to this

tag: per-tag parameter overrides

Output Chains

name: referenced by senders

msa: true if this is terminating smtp msa port 587 from clients injecting messages for the first time, enables store&forward in more circumstances, further details in internals/exploder

output_handler:

retry_params:

max_attempts: maximum number of retries

min_attempt_time: minimum time from completion of previous attempt to start of next attempt, seconds

max_attempt_time: maximum time from completion of previous attempt to start of next attempt, seconds

backoff_factor: exponential backoff factor

deadline: maximum wall clock time to retry message

notification:

sender/tag to inject notifications/DSNs

chain: list of filters

The output chain is linear. recipient_router_filter routes on recipient by setting fields in the transaction to influence the http endpoint that rest_output sends to and if that is the smtp gateway, what destination the gateway sends to after that.

A typical ingress config would accept local domains and reject everything else. Whereas an egress config might short-circuit local domains back to ingress and then send everything else to the RHS of the address.

Output Chain Filters

remote_host: resolves tx.remote_host ip to name

message_builder: renders/serializes tx.body message builder request json to rfc822 (for sending)

message_parser: parses rfc822 tx.body to message builder json (for receiving)

received_header: prepends Received: header to tx.body

dkim: signing

dns_resolution: replaces tx.resolution containing a hostname with one containing a list of IP addresses for the gateway to attempt in order

router: RecipientRouterFilter populates tx.rest_upstream_sender which controls where RestEndpoint sends it and if that is the gateway, tx.resolution controls where the gateway sends it

relay_auth: fails the tx if it doesn’t contain smtp auth info in smtp_meta or remote_host in allowlist

exploder: Exploder, output_chain configures the upstream chain, msa: true allows store&forward in a few more situations that clients expect vs ingress where there is a previous hop to retry.

rest_output: RestEndpoint actually sends the message somewhere via http/rest

Cluster/k8s

All replicas share the same underlying database.

The router implementation currently buffers data through the local filesystem but this does not need to be durable across restarts; emptyDir is fine. This is local to each router process; the router and gateway do not share data through the filesystem.

Koukan may return http redirects if the transaction is leased/active on a different replica; native rest clients must be prepared to follow these.

For both router and gateway, configure rest_listener.session_uri to point to the dns alias or ip of the individual pod/replica. For router, configure rest_listener.service_uri to the router service dns alias.