Arquitetura

O mxout recebe por dois canais, autentica quem envia, assina com DKIM e entrega direto no MX do destinatário. Desde a v0.9.0 ele guarda estado: a mensagem aceita sobrevive a restart e a crash.

Diagrama de fluxo

  aplicação                          aplicação
      │ SMTP AUTH (2525)                 │ POST /send-raw (Basic)
      ▼                                  ▼
┌──────────────────────────────────────────────────────────────┐
│                           mxout                              │
│                                                              │
│  1. Autentica o CLIENTE (bcrypt)                             │
│  2. Autoriza o domínio do envelope-from                      │
│  3. Garante Date e Message-ID                                │
│  4. Verifica alinhamento do header From                      │
│  5. Cota do domínio (hora e dia)                             │
│                                                              │
│  ── síncrono ──────────  ou  ── assíncrono ────────────────  │
│  entrega na requisição       grava estado + corpo, responde  │
│                              202 e sai no tick do cron       │
│                                                              │
│  Por destinatário:                                           │
│  6. Assina DKIM (RSA-SHA256)              ← ledger           │
│  7. Resolve MX                                               │
│  8. Entrega: EHLO → STARTTLS? → MAIL/RCPT/DATA               │
│     5xx → falha permanente · 4xx → reagenda                  │
│     backoff 1m → 5m → 15m → 1h → 4h       ← ledger           │
└──────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    MX do domínio destino

Onde o estado mora

banco embarcado  →  clientes, estado da entrega, tentativas, último erro
arquivo (spool)  →  o MIME cru, lido só na hora de entregar
arquivo (ledger) →  o histórico permanente, uma linha JSON por evento

O banco é SQL embarcado em Rust puro, sem dependência externa: sobe o binário e funciona. Ele é residente em memória, e é por isso que o corpo da mensagem fica fora dele — com teto de 5 MiB por mensagem, uma fila cheia consumiria RAM demais.

A ordem de gravação é a garantia de corretude: o corpo vai para o disco e é sincronizado antes da linha de estado. Um crash entre as duas coisas deixa um arquivo órfão, que a varredura recolhe; a ordem inversa deixaria estado sem corpo, ou seja, mensagem impossível de entregar.

Quem drena a fila

O cron, chamando `mxout tick`. O tick é uma chamada HTTP no loopback — quem trabalha é o próprio servidor, porque o banco embarcado aceita um processo por vez.

Ciclo por destinatário

Uma única requisição POST /send pode conter múltiplos destinatários. Para cada um, o mxout executa o ciclo abaixo de forma independente.

1. Montagem MIME

O mxout constrói o e-mail com os dados recebidos. Gera o Message-ID no formato <unixts.token@dominio-do-from> e o Date antes de qualquer assinatura. Isso é necessário porque esses campos são assinados — gerá-los depois invalidaria a assinatura.

2. Assinatura DKIM

A chave privada RSA-2048 do domínio (indicada pelo kit configurado) assina os headers From, To, Subject, Date e Message-ID. O header DKIM-Signature resultante é inserido no e-mail.

3. Resolução de MX

O mxout consulta o DNS pelo registro MX do domínio do destinatário. Os servidores são ordenados por prioridade (menor valor = maior prioridade). Se não houver registro MX, o RFC 5321 permite usar o registro A como MX implícito — o mxout segue essa regra.

4. Loop de retry

Tentativas: imediata, +5 s, +10 s, +30 s (4 ao total). As tentativas ficam em memória; não há persistência.

  • Resposta 5xx do servidor remoto indica erro permanente. O destinatário é descartado imediatamente.
  • Resposta 4xx ou falha de conexão indica erro temporário. O mxout retenta até esgotar as tentativas.
  • Esgotadas as tentativas sem sucesso, o erro é registrado no ledger e o destinatário é descartado.

Stateless e ledger

O mxout não mantém banco de dados nem fila em disco. O ledger gravado em stdout (JSON por linha) é a fonte de verdade de cada envio: contém o message_id, o destinatário, o resultado e os detalhes de cada tentativa.

  • Ledger — formato completo dos eventos registrados.
  • Entrega MX direta — detalha resolução de MX, STARTTLS e retry.
By Borlot.com.br on 01/06/2026