your-mail-mcp

Acesso MCP somente leitura e auto-hospedado a e-mails IMAP, espelhado em um maildir local e indexado por notmuch.

Documentação

your-mail-mcp

MCP registry Glama score

Seu e-mail já contém as respostas: referências de reserva, códigos de portão, faturas, períodos de garantia, promessas que as pessoas fizeram por escrito. Este servidor permite que seu assistente de IA as encontre.

Pergunte coisas como:

  • "Encontre a referência de reserva da balsa de junho."
  • "Qual era a senha do Wi-Fi que o hotel enviou no verão passado?"
  • "O que o contador respondeu sobre o IVA, e quando?"
  • "Reúna tudo entre mim e o construtor sobre o telhado, em ordem, e resuma quem prometeu o quê."
  • "O que chegou esta manhã, em todas as minhas contas, que realmente precisa de mim?"

Use para:

  • Busca que entende perguntas. Busca de texto completo em todo o seu histórico, todas as contas em um único índice, formulada do jeito que você pensa, em vez do jeito que a sintaxe de busca funciona.
  • Triagem pelo celular. Um resumo matinal do que chegou durante a noite, com o lixo já filtrado, de onde quer que você esteja.
  • E-mail como contexto para outros trabalhos. Extraia os requisitos do cliente da conversa e leve-os para sua sessão de codificação ou escrita, em vez de redigitá-los.
  • Agentes que você pode deixar rodando. O servidor só pode ler. Um e-mail malicioso que chega ao seu assistente é lido e nada mais, porque enviar, excluir e mover não existem aqui. Isso torna resumos agendados e agentes sempre ativos algo tranquilo de executar.

A configuração são dois arquivos e docker compose up -d — veja Executando.

Um servidor MCP auto-hospedado que dá a um cliente MCP (Claude, ou qualquer outro cliente que fale HTTP MCP com streamable e OAuth) acesso de leitura ao seu e-mail. Ele espelha uma ou mais contas IMAP em um maildir local com mbsync, indexa-os com notmuch e responde a chamadas de ferramentas a partir desse índice.

How your-mail-mcp works: mail is pulled from IMAP providers into a local mirror, indexed by notmuch, and served to an MCP client through an OAuth gate, with no write path back to the providers

O e-mail só se move da esquerda para a direita nesse diagrama. A única seta que o servidor faz de volta para um provedor é uma única LIST IMAP na inicialização, para descobrir como esse servidor chama suas pastas de lixo e spam; ele nunca seleciona uma caixa de correio e nunca busca uma mensagem. A fonte do diagrama é docs/diagrams/how-it-works.html.

O que ele não pode fazer

A propriedade somente leitura está embutida na arquitetura.

O espelho é somente de pull. A configuração mbsync gerada para cada conta carrega Sync Pull, Create Near, Remove None, Expunge None — nada nessa configuração pode enviar uma alteração de volta ao servidor, excluir uma mensagem ou expurgar uma.

A única operação IMAP em qualquer lugar do código Go é LIST, emitida uma vez por conta na inicialização para encontrar as pastas de lixo e spam de cada conta (veja Notas do provedor e Solução de problemas). Essa conexão faz login, lista caixas de correio e faz logout. Ela nunca seleciona uma caixa de correio e nunca busca uma mensagem.

Não há envio, exclusão, movimentação ou marcação. Os anexos são listados em show e thread e servidos somente leitura pela ferramenta attachment, uma parte por vez, com limite de 5MB. Partes maiores são servidas cruas em GET /attachment/{id}/{part}, autenticadas por um token bearer ou pelo link assinado de curta duração que a ferramenta retorna quando recusa uma parte superdimensionada. Nada no processo tem acesso de escrita a qualquer conta.

Onze ferramentas, todas somente leitura:

FerramentaO que faz
searchBusca e-mail. Retorna resumos de conversas como JSON.
idsRetorna os IDs de mensagem que correspondem a uma consulta.
filesRetorna os caminhos de arquivo maildir que correspondem a uma consulta.
countConta as mensagens que correspondem a uma consulta.
showMostra uma mensagem: cabeçalhos e corpo decodificado, como JSON.
threadMostra a conversa inteira que contém uma mensagem. Exclui respostas de lixo/spam por padrão; defina include_excluded para incluí-las.
textRetorna o corpo em texto simples de uma mensagem, convertendo HTML.
foldersLista contas, suas pastas, tags de índice e a última sincronização e último erro de cada conta.
refreshSincroniza a caixa de entrada agora e relata quantas mensagens chegaram.
statusSaúde da sincronização por conta: conclusão da primeira sincronização, última sincronização, mensagens indexadas, erros e backoff.
attachmentUm anexo ou parte MIME de uma mensagem, pelo número da parte de show. Imagens e binários como conteúdo tipado, texto como bloco marcado. Partes acima de 5MB recebem um link de download assinado.

search, ids, files e count aceitam uma consulta notmuch (from:, to:, subject:, tag:, folder:, date:2026-01-01..2026-06-30, combinadas com e/ou/não), um account opcional para limitar a uma conta, e podem incluir lixo/spam com include_excluded.

Executando

Três maneiras de executar isso. Elas diferem em uma coisa: quem pode acessar o servidor. Comece no caso 1 e suba apenas quando precisar. Nenhum deles é endurecido além dos padrões — isso é Endurecimento, mais abaixo, e é deliberadamente separado para que você possa fazer funcionar primeiro.

Onde rodaQuem pode acessarSeu e-mail é armazenado em
1sua máquinaapenas essa máquinasua máquina
2sua máquinavocê, de qualquer lugarsua máquina
3um VPSvocê, de qualquer lugarum disco alugado

O servidor é distribuído como imagem de contêiner em ghcr.io/wildsurfer/your-mail-mcp, construída e publicada por CI para amd64 e arm64. Nada precisa ser compilado, e todos os casos começam da mesma forma — dois arquivos em um diretório vazio:

mkdir your-mail && cd your-mail
curl -fsSLO https://raw.githubusercontent.com/wildsurfer/your-mail-mcp/main/compose.yaml
curl -fsSL https://raw.githubusercontent.com/wildsurfer/your-mail-mcp/main/accounts.example.json -o accounts.json

Edite accounts.json com suas contas (veja O arquivo de contas), depois coloque os segredos que ele referencia em um arquivo .env ao lado de compose.yaml:

# .env
OAUTH_PASSPHRASE=pick-a-long-one-you-can-type-on-a-smartphone
WORK_PASS=your-gmail-app-password
PERSONAL_PASS=your-icloud-app-specific-password

OAUTH_PASSPHRASE é a única credencial entre a internet e seu e-mail nos casos 2 e 3. Trate-a de acordo.

Esses dois arquivos contêm as senhas do seu e-mail. Se você colocar este diretório sob controle de versão ou em um backup que saia da máquina, trate-os de acordo.


Caso 1 — na sua máquina, apenas para sua máquina

O servidor vincula ao loopback. Nada fora da sua máquina pode acessá-lo, então não há TLS para configurar nem hostname para possuir. Suas ferramentas de CLI podem usá-lo. Seu smartphone não pode.

Adicione uma linha ao .env:

PUBLIC_URL=http://127.0.0.1:8080

Depois inicie:

docker compose up -d
docker compose logs -f          # watch the first sync

A primeira sincronização popula o maildir e leva um tempo em uma caixa de correio grande. É mais lenta do que poderia ser de propósito, um comando IMAP por vez, porque os provedores limitam. Não há etapa separada de inicialização.

Claude Code

claude mcp add --transport http your-mail http://127.0.0.1:8080/mcp

Depois execute /mcp dentro do Claude Code, escolha your-mail e autentique. Um navegador abre a página de consentimento, que pede uma coisa: seu OAUTH_PASSPHRASE. Até você fazer isso, claude mcp list mostra Needs authentication.

Codex

codex mcp add your-mail --url http://127.0.0.1:8080/mcp
codex mcp login your-mail

codex mcp list mostra o status de autenticação. Se as ferramentas ainda não aparecerem em uma sessão após um login bem-sucedido, isso é um bug conhecido do Codex em que as credenciais OAuth são obtidas e nunca usadas (openai/codex#20009). Use a ponte abaixo até que seja corrigido.

Fallback para qualquer cliente cujo suporte a OAuth esteja quebrado

mcp-remote faz o fluxo OAuth sozinho e reexpõe o servidor via stdio, que todo cliente MCP suporta:

# ~/.codex/config.toml
[mcp_servers.your-mail]
command = "npx"
args = ["-y", "mcp-remote", "http://127.0.0.1:8080/mcp"]

Ele abre a mesma página de consentimento na primeira execução e armazena os tokens em cache.


Caso 2 — na sua máquina, acessível de qualquer lugar

Mesmo servidor, mais algo que lhe dê um endereço HTTPS público. Seu e-mail permanece na sua máquina, e nada escuta na sua rede doméstica, porque o túnel faz a conexão de saída. Você precisa disso para os aplicativos de smartphone e desktop: um conector personalizado é buscado pelos servidores do fornecedor, então ele não pode acessar um endereço privado.

Com Tailscale (sem necessidade de domínio)

Um comando, igual no macOS e no Linux, e você obtém um hostname HTTPS sem possuir um domínio.

tailscale funnel --bg 8080

--bg mantém isso rodando entre reinicializações. Ele imprime a URL pública, que parece com https://your-machine.your-tailnet.ts.net. Esse é o hostname a usar:

# .env
PUBLIC_URL=https://your-machine.your-tailnet.ts.net
docker compose up -d

O Funnel precisa de certificados HTTPS e do atributo de nó Funnel habilitado para sua tailnet; a CLI oferece adicionar a linha de política na primeira vez, e o resto está no seu console de administração. tailscale funnel status mostra o que está exposto, e tailscale funnel --https=443 off derruba.

Com Cloudflare (você possui um domínio, e ele está no Cloudflare)

Use isso se quiser um hostname no seu próprio domínio em vez de um .ts.net. mail.example.com abaixo é seu domínio, já adicionado à sua conta Cloudflare — o Cloudflare não fornece um hostname para um túnel nomeado.

cloudflared tunnel login
cloudflared tunnel create your-mail

create imprime o UUID do túnel e o arquivo de credenciais que acabou de escrever:

Tunnel credentials written to /Users/you/.cloudflared/f9e2…-… .json
Created tunnel your-mail with id f9e2…-…

Use esse caminho exato abaixo; cloudflared tunnel list imprime o UUID novamente se você o perder. Roteie o hostname, depois escreva ~/.cloudflared/config.yml:

cloudflared tunnel route dns your-mail mail.example.com
tunnel: your-mail
credentials-file: /Users/you/.cloudflared/f9e2….json   # the path create printed
url: http://localhost:8080
cloudflared tunnel run your-mail

Para manter rodando: no Linux, sudo cloudflared service install. No macOS, instale via Homebrew e use brew services start cloudflared, porque o caminho de instalação do sudo procura seu certificado no diretório home do usuário root e não encontrará o que cloudflared tunnel login escreveu no seu.

Depois defina PUBLIC_URL=https://mail.example.com em .env e docker compose up -d.

De qualquer forma

PUBLIC_URL precisa corresponder exatamente ao que você digita no cliente. O servidor publica PUBLIC_URL + /mcp como o resource em seus metadados OAuth, e uma incompatibilidade aí é o motivo mais comum para um conector recusar a adição.

Uma coisa para saber antes de começar no smartphone: nem o Claude nem o ChatGPT permitem adicionar um conector pelo aplicativo do smartphone. Você o adiciona uma vez na web (ou no aplicativo desktop do Claude), e ele então aparece no seu smartphone. Tentar fazer a configuração no próprio smartphone vai desperdiçar seu tempo.

Claude — adicione na web ou no desktop, depois use no seu smartphone

  1. Em claude.ai ou no Claude Desktop, vá em Configurações → Conectores e clique em + ao lado de Conectores, ou Adicionar conector personalizado.
  2. Dê um nome e a URL <PUBLIC_URL>/mcp. Deixe os campos avançados de OAuth vazios: este servidor registra clientes dinamicamente.
  3. O Claude abre a página de consentimento. Insira seu OAUTH_PASSPHRASE.
  4. Abra o aplicativo Claude no seu smartphone. O conector já está lá, e as ferramentas estão disponíveis em um chat. Ative-o para uma conversa pelo menu de ferramentas ou conectores no compositor.

ChatGPT — adicione na web, depois use no seu smartphone

Conectores MCP personalizados ficam atrás do modo de desenvolvedor, que exige uma conta Pro, Plus, Business, Enterprise ou Education e está disponível apenas na web.

  1. No ChatGPT na web, abra Configurações → Segurança e login e ative o Modo de desenvolvedor. Em espaços de trabalho Business e Enterprise, um administrador pode precisar permitir primeiro.
  2. Adicione um conector para um servidor MCP remoto e dê a URL <PUBLIC_URL>/mcp, com OAuth como autenticação. O ChatGPT suporta registro dinâmico de clientes, então não há nada para colar.
  3. Aprove a página de consentimento com seu OAUTH_PASSPHRASE.
  4. Abra o ChatGPT no seu smartphone e ative o conector em um chat.

Esses menus mudam. Se os nomes acima não corresponderem ao que você vê, procure pelo modo de desenvolvedor nas configurações e depois pelo lugar que adiciona um conector por URL.

O ChatGPT desativa algumas ações de escrita do MCP no celular. Isso não tem efeito aqui, porque este servidor não tem nenhuma ação de escrita.

Claude Code

claude mcp add --transport http your-mail https://your-host/mcp

Codex

codex mcp add your-mail --url https://your-host/mcp
codex mcp login your-mail

Caso 3 — em um VPS, acessível de qualquer lugar

Escolha isso quando quiser que o espelho permaneça ativo independentemente de sua máquina estar ligada. Custa alguns dólares por mês e uma troca real: uma cópia completa em texto simples do seu e-mail vai para um disco alugado, com as senhas de aplicativo no mesmo ambiente. Leia Segurança antes de escolher. A instalação é o caso 1 mais um túnel, no computador de outra pessoa. Sem portas para abrir, sem DNS para configurar, sem certificados para gerenciar.

Em uma máquina nova com Debian ou Ubuntu:

# 1. Docker
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER && newgrp docker

# 2. The two files, and your accounts
mkdir your-mail && cd your-mail
curl -fsSLO https://raw.githubusercontent.com/wildsurfer/your-mail-mcp/main/compose.yaml
curl -fsSL https://raw.githubusercontent.com/wildsurfer/your-mail-mcp/main/accounts.example.json -o accounts.json
$EDITOR accounts.json             # your accounts
$EDITOR .env                      # OAUTH_PASSPHRASE and the account passwords

# 3. A public address, exactly as in case 2
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
tailscale funnel --bg 8080        # prints your https://….ts.net hostname

# 4. Put that hostname in .env, then start
echo "PUBLIC_URL=https://your-machine.your-tailnet.ts.net" >> .env
docker compose up -d
docker compose logs -f

PUBLIC_URL vem por último porque você não sabe o hostname até o passo 3 imprimi-lo.

Conectar um cliente é idêntico ao caso 2.

restart: unless-stopped no compose.yaml traz os contêineres de volta após uma reinicialização. Verifique com a ferramenta folders, que relata a última sincronização de cada conta e seu último erro, ou com docker compose logs --tail=50.

Agora vá ler Hardening. Um VPS no qual você consegue entrar via SSH com senha, contendo uma cópia do seu e-mail, é pior do que não executar isso de forma alguma.


Hardening

Nada disso é necessário para fazer o servidor funcionar, por isso não está nos passos de instalação. Está ordenado pelo quanto ele te beneficia. O caso 1 não precisa de nada disso.

Escolha uma frase-senha de verdade. OAUTH_PASSPHRASE é a porta inteira. Um chute errado custa ao atacante um segundo, e os chutes são serializados, então executá-los em paralelo não ajuda, mas nenhuma dessas coisas salva uma frase-senha curta. Use uma longa que você ainda consiga digitar em um smartphone.

Bloqueie o SSH (caso 3). Uma máquina alugada com login por senha e uma cópia do seu e-mail é a pior combinação neste documento. Como root, antes de qualquer outra coisa:

adduser mail && usermod -aG sudo mail
rsync --archive --chown=mail:mail ~/.ssh /home/mail
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/; s/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh

Depois faça a instalação como mail, não como root.

Feche as portas que você não está usando (caso 3). Com um túnel, você não precisa de nenhuma porta de entrada, então:

sudo ufw allow OpenSSH && sudo ufw --force enable

Restrinja quem pode alcançar o conector. Se a única coisa que fala com o seu servidor é um conector personalizado em um app Claude, esse tráfego chega a partir da faixa de saída publicada da Anthropic, 160.79.104.0/21, e você pode recusar todo o resto no túnel ou no firewall. Não faça isso se você também usa Claude Code ou Codex de um laptop, pois eles se conectam de onde você estiver.

Faça backup dos volumes ou aceite uma ressincronização. compose.yaml mantém o maildir e o índice em volumes nomeados. Nada neles é único — tudo ainda está no seu servidor de e-mail — mas baixar novamente uma caixa de entrada grande leva um tempo e incomoda provedores que limitam a largura de banda.

Saiba o que a frase-senha não protege. Ela protege a superfície MCP. Ela não criptografa nada em repouso. Veja Security.

Seu próprio domínio e certificado em vez de um túnel

Se você preferir encerrar o TLS você mesmo em um domínio que possui, aponte um registro A para a máquina e coloque o Caddy na frente. Adicione compose.override.yaml:

services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports: ["80:80", "443:443"]
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
volumes:
  caddy_data:
# Caddyfile
mail.example.com {
    reverse_proxy your-mail-mcp:8080
}

Abra ambas as portas — a 80 não é opcional, o Caddy a usa para o desafio de certificado e o redirecionamento HTTPS:

sudo ufw allow 80/tcp && sudo ufw allow 443/tcp

O Caddy obtém e renova o certificado sozinho. Defina PUBLIC_URL para o hostname e docker compose up -d.

O arquivo de contas

Montado somente leitura em /config/accounts.json (veja compose.yaml). JSON, analisado com encoding/json, expandido contra o ambiente do processo antes da análise, então ${VAR} em qualquer valor de string é substituído pela variável de ambiente com esse nome. É assim que os segredos ficam fora do arquivo:

{
  "accounts": [
    {
      "name": "work",
      "host": "imap.gmail.com",
      "user": "you@example.com",
      "password": "${WORK_PASS}"
    }
  ]
}

Chaves por conta:

ChavePadrãoObservações
nameObrigatório. Sem espaços, aspas ou barras (normais ou invertidas). Torna-se o diretório maildir de nível superior para a conta e o argumento account nas chamadas de ferramenta.
hostObrigatório. Hostname do servidor IMAP.
port993 (imaps) ou 143 (caso contrário)
userObrigatório. Veja Provider notes: o iCloud quer o nome curto, não o endereço de e-mail completo.
passwordObrigatório. ${VAR} é expandido a partir do ambiente; uma senha literal também funciona, mas não é recomendada.
tlsimapsimaps, starttls ou none.
patterns["*"]Padrões de pastas do mbsync — quais pastas espelhar.
exclude_foldersdescoberto automaticamenteNomes de pastas a excluir da busca por padrão (veja SPECIAL-USE discovery). Definir isso substitui completamente a descoberta para aquela conta.

Um nome de conta deve ser único. Pelo menos uma conta é obrigatória; um array accounts vazio é um erro de inicialização.

Variáveis de ambiente

VariávelObrigatóriaPadrãoSignificado
CONFIGsimCaminho para o arquivo de contas.
MAILDIRsimRaiz do maildir; cada conta recebe um subdiretório.
INDEXsimDiretório do índice notmuch/Xapian.
PUBLIC_URLsimA URL externa pela qual o servidor é acessado, exatamente como um cliente a usará (uma barra final, se houver, é removida). Usada nos metadados OAuth e deve corresponder ao que você digita no cliente.
OAUTH_PASSPHRASEsimA única frase-senha que protege a tela de consentimento.
SYNC_INTERVALnão10mPeríodo de sincronização completa, como uma duração Go (5m, 1h). O padrão segue a cadência recomendada de clientes IMAP do Google, de 10 minutos. Uma conta que falha repetidamente é tentada novamente no dobro desse intervalo, depois no quádruplo, com limite de uma hora, para que uma queda do provedor ou bloqueio de cota não seja martelado.
SYNC_TIMEOUTnão1hPrazo por conta para uma execução do mbsync, como uma duração Go. Uma execução interrompida pelo prazo retoma de onde parou na próxima passada, então um primeiro espelhamento grande é concluído em partes. Pense antes de aumentar isso em uma configuração com várias contas: as contas sincronizam uma de cada vez, então uma conta travada em uma conexão limitada bloqueia as outras durante todo o prazo.
LISTEN_ADDRnão:8080Endereço ao qual o servidor HTTP faz bind.
INIT_MIRRORnãonão definidoDefina como 1 para sincronizar em um diretório vazio que não é um ponto de montagem. Não é necessário com o compose, onde /mail é um volume.

CONFIG, MAILDIR e INDEX são obrigatórias; o processo se recusa a iniciar sem elas. PUBLIC_URL e OAUTH_PASSPHRASE são obrigatórias pela camada OAuth e o processo também falha ao iniciar sem elas.

A imagem do contêiner já define quatro delas (Dockerfile): MAILDIR=/mail, INDEX=/index, CONFIG=/config/accounts.json, LISTEN_ADDR=:8080. compose.yaml não substitui nenhuma delas. Deixe-as em paz, a menos que você também esteja alterando a montagem de volume ou de configuração correspondente em compose.yaml — uma substituição que não move a montagem junto aponta o servidor para um caminho vazio ou inexistente.

Sem Docker

Binários de lançamento para Linux e macOS, amd64 e arm64, estão na página de lançamentos, com checksums. O binário chama mbsync, notmuch e w3m, então instale esses primeiro — brew install isync notmuch w3m no macOS, apt install isync notmuch w3m no Debian e Ubuntu. isync 1.4.4 ou mais novo funciona.

Depois, a mesma configuração do contêiner, com caminhos de sua escolha. Os volumes do contêiner começam como pontos de montagem, que a proteção de maildir vazio lê como uma primeira execução genuína; um diretório comum que você cria parece exatamente um volume ausente para essa mesma proteção, então ela precisa de INIT_MIRROR=1 para dizer que realmente é uma primeira execução aqui:

mkdir -p mail index
CONFIG=./accounts.json MAILDIR=./mail INDEX=./index INIT_MIRROR=1 \
PUBLIC_URL=http://127.0.0.1:8080 OAUTH_PASSPHRASE=... \
WORK_PASS=... ./your-mail-mcp

O Windows não é suportado: o tratamento do maildir depende de semânticas de sistema de arquivos Unix, e não há mbsync para chamar.

Compilando você mesmo

O CI compila, testa e publica cada imagem, então ninguém precisa — mas é um comando se você quiser: docker build -t your-mail-mcp . para o contêiner, ou go build para o binário (Go 1.27, com as três ferramentas acima no PATH para os testes).

Notas do provedor

As notas do iCloud vêm da operação de longo prazo de um espelho iCloud real que antecede este servidor. As notas do Gmail e do Dovecot vêm da documentação do provedor e da pesquisa do projeto, e nem todas foram reverificadas através deste servidor ainda.

  • iCloud (imap.mail.me.com): o user IMAP é o nome curto — a parte antes de @icloud.com — não o endereço de e-mail completo. O iCloud limita conexões IMAP simultâneas; é por isso que a configuração mbsync gerada fixa PipelineDepth 1 para cada conta, e isso não é configurável.
  • Gmail (imap.gmail.com): exige uma Senha de App, que exige verificação em duas etapas habilitada na conta primeiro — o Gmail não aceita a senha da conta diretamente via IMAP. O Gmail também mantém uma cópia de praticamente tudo em [Gmail]/All Mail, então o espelho de uma conta Gmail tem aproximadamente o dobro do tamanho que a lista de pastas sugere, já que a maioria das mensagens existe tanto na pasta delas quanto em All Mail. O primeiro espelhamento de uma conta Gmail grande leva horas, e o Google também impõe uma cota diária de download IMAP (cerca de 2,5 GB por dia), então uma caixa de entrada com vários gigabytes distribui o primeiro espelhamento por vários dias. Isso é normal: o servidor continua tentando em seu cronograma e o mbsync retoma de onde parou. Defina SYNC_TIMEOUT para algo como 8h para o primeiro espelhamento para que uma execução longa não seja interrompida pelo prazo padrão de uma hora.
  • Servidores Dovecot (muitos provedores auto-hospedados e menores) comumente prefixam nomes de pastas com INBOX. (ex.: INBOX.Sent). Se folders mostrar nomes de pastas que você não esperava, geralmente é por isso.

Segurança

As senhas das contas são fornecidas através do ambiente do processo (${VAR} em accounts.json, ou valores literais). Na inicialização, o servidor as escreve em um arquivo de configuração mbsync gerado no disco dentro do contêiner, com modo de arquivo 0600. Esse arquivo não é criptografado. Qualquer coisa que possa ler o ambiente do contêiner, ou esse arquivo, pode ler as senhas em texto puro.

Proteção em repouso — criptografia de disco, restringir quem pode executar comandos no contêiner, acesso ao host — é responsabilidade do operador. Este servidor não afirma criptografar credenciais em repouso e não tenta fazê-lo.

A frase-senha OAuth é verificada em tempo constante e protege todo o servidor com um único segredo compartilhado; não é um sistema de credenciais por usuário. Trate OAUTH_PASSPHRASE e as senhas das contas de e-mail com o mesmo cuidado.

Os resumos de tópicos do search incluem um nome de exibição para cada mensagem em um tópico correspondente, que um remetente controla. Uma mensagem em uma pasta excluída por padrão (lixo, lixeira) ainda pode colocar seu próprio nome escolhido pelo atacante na sua frente dessa forma, mesmo que o corpo dela nunca apareça — search não busca nem mostra o corpo de uma mensagem excluída. thread e show são caminhos de leitura, não sujeitos a isso: thread exclui respostas de lixo/lixeira por padrão (veja a tabela de ferramentas acima), e show lê uma única mensagem para a qual você já tem o id. Esse vazamento de nome de exibição no search não é corrigido nesta versão.

Solução de problemas

"maildir ... is an empty plain directory, not a mount point: refusing to sync" — o servidor verifica se o seu maildir é um sistema de arquivos montado. Um volume montado que por acaso está vazio é uma primeira execução e sincroniza sem opt-in, por isso o compose não precisa de passo extra. Um diretório vazio comum é ambíguo: um maildir novo parece exatamente um caminho cujo volume nunca foi montado, e sincronizar no segundo rebaixa todas as contas para um diretório que desaparece no momento em que você corrige a montagem. Monte o armazenamento onde MAILDIR aponta, ou defina INIT_MIRROR=1 se realmente for para ser um diretório comum neste sistema de arquivos.

"maildir ...: no such file or directory" — o caminho não existe de forma alguma. Com o compose, isso significa que o volume ou bind mount está faltando em compose.yaml; executando o binário diretamente, significa que MAILDIR está errado. Verifique o status de sincronização por conta com a ferramenta folders. Ela lista cada conta configurada, o horário da última sincronização bem-sucedida, o último erro, se houver, suas pastas e as tags no índice. Uma única conta com senha incorreta ou senha específica de aplicativo expirada não impede as outras — falhas de sincronização são isoladas por conta — mas ela aparecerá aqui como uma linha last error, não como silêncio.

Exclusão de lixo eletrônico/lixeira, dois formatos diferentes de falha:

  • "special-use discovery: account NOME: ..." nos logs do contêiner significa que a conexão inicial, o login ou o LIST para essa conta falhou completamente. Nessa falha, não há nomes de pastas para recorrer à correspondência, então essa conta fica sem nenhuma exclusão — nem mesmo pela lista de nomes em inglês embutida — até que o problema de conexão seja corrigido ou exclude_folders seja definido manualmente para ela.
  • Nenhuma linha de erro, mas folders ainda mostra nada excluído significa que o LIST foi bem-sucedido — o servidor simplesmente não anuncia atributos \Junk/\Trash (sem suporte a SPECIAL-USE RFC 6154) e os nomes das pastas não correspondem à lista em inglês embutida (junk, spam, trash, deleted messages, deleted items, bulk mail). Este é o caso de caixa de correio localizada — uma caixa de correio alemã ou francesa, por exemplo — e a correção é a mesma: defina exclude_folders manualmente.

exclude_folders em accounts.json, por exemplo, "exclude_folders": ["Papierkorb"], tem prioridade sobre SPECIAL-USE e a lista embutida em todos os casos.