Ledger — mecanismo de controle

O ledger é o registro auditável de tudo que acontece com cada e-mail no mxout. Como o mxout é stateless (sem banco de dados, sem fila persistida), o ledger é a única fonte de verdade sobre o destino de cada mensagem.

Por que o ledger existe

O mxout não armazena estado entre requisições. Para saber se um e-mail foi entregue, adiado ou falhou, é preciso observar os eventos emitidos em tempo real. O ledger fornece essa trilha de auditoria sem introduzir dependências externas.

Formato

O mxout emite uma linha de log JSON por evento no stdout, no tracing target mxout::ledger.

Cada linha segue o envelope do tracing-subscriber em formato JSON:

{
  "timestamp": "2026-06-01T14:23:05.412Z",
  "level": "INFO",
  "target": "mxout::ledger",
  "fields": {
    "ledger": "{\"event\":\"delivered\",\"message_id\":\"abc123@mail.exemplo.com\",\"rcpt\":\"destino@outro.com\",\"host\":\"mx.outro.com\",\"attempt\":1,\"tls\":true,\"smtp_response\":\"250 OK\"}"
  }
}

O campo fields.ledger é uma string JSON contendo o evento. Um log shipper filtra por target == "mxout::ledger" e faz parse do campo ledger.

Importante: o mxout nunca lê o ledger de volta. Ele é write-only por design.

Os 8 tipos de evento

event Quando é emitido Campos presentes
received Requisição aceita na borda HTTP message_id, from, from_domain, rcpt_count, subject_len, body_bytes
rejected Requisição recusada (auth, validação, domínio sem kit) message_id, reason, http_status
signed Assinatura DKIM aplicada à mensagem message_id, domain, selector
mx_resolved MX do destinatário resolvido via DNS message_id, rcpt, mx_hosts (lista)
attempt Início de uma tentativa de entrega SMTP message_id, rcpt, host, attempt (base 1)
delivered Mensagem aceita pelo servidor remoto message_id, rcpt, host, attempt, tls, smtp_response
deferred Falha temporária; haverá nova tentativa message_id, rcpt, host, tls, error, next_wait_s
failed Desistiu após esgotar tentativas ou 5xx permanente message_id, rcpt, permanent, error

Nos eventos rejected, quando ainda não existe um envio em andamento, message_id é "-".

Sequência típica de um envio bem-sucedido

Os eventos abaixo usam o mesmo message_id, permitindo correlacionar todo o ciclo:

{"event":"received","message_id":"abc123@mail.exemplo.com","from":"remetente@exemplo.com","from_domain":"exemplo.com","rcpt_count":1,"subject_len":32,"body_bytes":512}
{"event":"signed","message_id":"abc123@mail.exemplo.com","domain":"exemplo.com","selector":"mail"}
{"event":"mx_resolved","message_id":"abc123@mail.exemplo.com","rcpt":"destino@outro.com","mx_hosts":["mx1.outro.com","mx2.outro.com"]}
{"event":"attempt","message_id":"abc123@mail.exemplo.com","rcpt":"destino@outro.com","host":"mx1.outro.com","attempt":1}
{"event":"delivered","message_id":"abc123@mail.exemplo.com","rcpt":"destino@outro.com","host":"mx1.outro.com","attempt":1,"tls":true,"smtp_response":"250 OK"}

Cada linha acima é o valor do campo fields.ledger após parse. O campo timestamp e demais campos do envelope ficam na linha externa.

Como consumir

Acompanhar em tempo real com Docker

docker logs -f mxout | grep 'mxout::ledger'

Extrair e parsear o campo ledger com jq

docker logs mxout \
  | jq -c 'select(.target == "mxout::ledger") | .fields.ledger | fromjson'

Filtrar só falhas

docker logs mxout \
  | jq -c 'select(.target == "mxout::ledger") | .fields.ledger | fromjson | select(.event == "failed")'

Integrar com log shipper

Configure o shipper (Fluent Bit, Vector, Promtail, etc.) para:

  1. Coletar stdout do container mxout.
  2. Filtrar linhas onde target == "mxout::ledger".
  3. Fazer parse do campo fields.ledger como JSON aninhado.
  4. Indexar os campos do evento (message_id, event, rcpt, etc.) para busca.

Correlação por message_id

O message_id é gerado pelo mxout no momento em que a requisição é aceita. Ele aparece:

  • No header Message-ID do e-mail entregue.
  • No corpo da resposta HTTP do POST /send.
  • Em todos os eventos do ledger referentes a esse envio.

Use message_id para rastrear o ciclo completo de uma mensagem, do received ao delivered ou failed.

Verbosidade com RUST_LOG

A variável de ambiente RUST_LOG controla o nível de log do mxout. O valor padrão é info.

Os eventos do ledger são emitidos no nível info. Para ver apenas o ledger sem outros logs:

RUST_LOG=mxout::ledger=info

Para mais detalhes internos do mxout (debug de DNS, TLS, SMTP):

RUST_LOG=mxout=debug
By Borlot.com.br on 01/06/2026