your-mail-mcp

Acceso MCP de solo lectura y autoalojado al correo IMAP, reflejado en un maildir local e indexado por notmuch.

Documentación

your-mail-mcp

MCP registry Glama score

Tu correo ya contiene las respuestas: referencias de reservas, códigos de puerta, facturas, períodos de garantía, promesas que la gente hizo por escrito. Este servidor permite que tu asistente de IA las encuentre.

Pregúntale cosas como:

  • "Encuentra la referencia de reserva del ferry de junio."
  • "¿Cuál era la contraseña del wifi que el hotel envió el verano pasado?"
  • "¿Qué respondió el contador sobre el IVA, y cuándo?"
  • "Recopila todo lo que hay entre yo y el constructor sobre el techo, en orden, y resume quién prometió qué."
  • "¿Qué llegó esta mañana, en todas mis cuentas, que realmente requiera mi atención?"

Úsalo para:

  • Búsqueda que entiende preguntas. Búsqueda de texto completo en todo tu historial, todas las cuentas en un solo índice, formulada como piensas en lugar de como funciona la sintaxis de búsqueda.
  • Triaje desde tu teléfono. Un resumen matutino de lo que llegó durante la noche, con el spam ya filtrado, desde donde estés.
  • Correo como contexto para otro trabajo. Extrae los requisitos del cliente del hilo y llévalos a tu sesión de codificación o escritura, en lugar de volver a escribirlos.
  • Agentes que puedes dejar ejecutándose. El servidor solo puede leer. Un correo malicioso que llegue a tu asistente se lee y nada más, porque enviar, eliminar y mover no existen aquí. Eso hace que los resúmenes programados y los agentes siempre activos sean algo tranquilo de ejecutar.

La configuración son dos archivos y docker compose up -d — consulta Ejecutarlo.

Un servidor MCP autoalojado que da a un cliente MCP (Claude, o cualquier otro cliente que hable MCP HTTP streamable con OAuth) acceso de lectura a tu correo. Refleja una o más cuentas IMAP en un maildir local con mbsync, las indexa con notmuch, y responde a llamadas de herramientas desde ese í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

El correo solo se mueve de izquierda a derecha en esa imagen. La única flecha que el servidor hace de vuelta hacia un proveedor es un único IMAP LIST al inicio, para descubrir cómo llama ese servidor a sus carpetas de spam y papelera; nunca selecciona una carpeta y nunca obtiene un mensaje. La fuente del diagrama es docs/diagrams/how-it-works.html.

Lo que no puede hacer

La propiedad de solo lectura está integrada en la arquitectura.

El espejo es solo de extracción. La configuración mbsync generada para cada cuenta lleva Sync Pull, Create Near, Remove None, Expunge None — nada en esa configuración puede enviar un cambio de vuelta al servidor, eliminar un mensaje o purgar uno.

La única operación IMAP en cualquier parte del código Go es LIST, emitida una vez por cuenta al inicio para encontrar las carpetas de spam y papelera de cada cuenta (consulta Notas del proveedor y Solución de problemas). Esa conexión inicia sesión, lista las carpetas y cierra sesión. Nunca selecciona una carpeta y nunca obtiene un mensaje.

No hay envío, ni eliminación, ni movimiento, ni etiquetas. Los adjuntos se listan en show y thread y se sirven de solo lectura mediante la herramienta attachment, una parte a la vez, con un límite de 5MB. Las partes más grandes se sirven en bruto en GET /attachment/{id}/{part}, autenticadas con un token de portador o con el enlace firmado de corta duración que la herramienta devuelve cuando rechaza una parte sobredimensionada. Nada en el proceso tiene acceso de escritura a ninguna cuenta.

Once herramientas, todas de solo lectura:

HerramientaQué hace
searchBusca en el correo. Devuelve resúmenes de hilos como JSON.
idsDevuelve los ids de mensaje que coinciden con una consulta.
filesDevuelve las rutas de archivo maildir que coinciden con una consulta.
countCuenta los mensajes que coinciden con una consulta.
showMuestra un mensaje: cabeceras y cuerpo decodificado, como JSON.
threadMuestra el hilo completo que contiene un mensaje. Excluye respuestas de spam/papelera por defecto; establece include_excluded para incluirlas.
textDevuelve el cuerpo de texto plano de un mensaje, convirtiendo HTML.
foldersLista cuentas, sus carpetas, etiquetas de índice, y la última sincronización y último error de cada cuenta.
refreshSincroniza INBOX ahora e informa cuántos mensajes llegaron.
statusSalud de sincronización por cuenta: finalización de la primera sincronización, última sincronización, mensajes indexados, errores y retroceso.
attachmentUn adjunto o parte MIME de un mensaje, por número de parte de show. Imágenes y binarios como contenido tipado, texto como bloque marcado. Las partes de más de 5MB reciben un enlace de descarga firmado en su lugar.

search, ids, files y count toman una consulta notmuch (from:, to:, subject:, tag:, folder:, date:2026-01-01..2026-06-30, combinadas con y/o/no), un account opcional para limitar a una cuenta, y pueden incluir spam/papelera con include_excluded.

Ejecutarlo

Tres formas de ejecutarlo. Se diferencian en una cosa: quién puede alcanzar el servidor. Empieza en el caso 1 y sube solo cuando lo necesites. Ninguno está endurecido más allá de los valores predeterminados — eso es Endurecimiento, más abajo, y está deliberadamente separado para que puedas hacer que funcione primero.

Dónde se ejecutaQuién puede alcanzarloTu correo se almacena en
1tu máquinasolo esa máquinatu máquina
2tu máquinatú, desde cualquier lugartu máquina
3un VPStú, desde cualquier lugarun disco alquilado

El servidor se distribuye como imagen de contenedor en ghcr.io/wildsurfer/your-mail-mcp, construida y publicada por CI para amd64 y arm64. Nada necesita compilarse, y cada caso comienza de la misma manera — dos archivos en un directorio vacío:

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

Edita accounts.json con tus cuentas (consulta El archivo de cuentas), luego coloca los secretos que referencia en un archivo .env junto a 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 es la única credencial entre internet y tu correo en los casos 2 y 3. Trátalo en consecuencia.

Estos dos archivos contienen las contraseñas de tu correo. Si alguna vez pones este directorio bajo control de versiones o en una copia de seguridad que salga de la máquina, trátalos en consecuencia.


Caso 1 — en tu máquina, solo para tu máquina

El servidor se vincula al loopback. Nada fuera de tu máquina puede alcanzarlo, así que no hay TLS que organizar ni nombre de host que poseer. Tus herramientas CLI pueden usarlo. Tu teléfono inteligente no.

Añade una línea a .env:

PUBLIC_URL=http://127.0.0.1:8080

Luego inícialo:

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

La primera sincronización llena el maildir y tarda un tiempo en una carpeta grande. Es más lento de lo que podría ser a propósito, un comando IMAP a la vez, porque los proveedores limitan. No hay un paso de inicialización separado.

Claude Code

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

Luego ejecuta /mcp dentro de Claude Code, elige your-mail y autentícate. Un navegador abre la página de consentimiento, que pide una sola cosa: tu OAUTH_PASSPHRASE. Hasta que hagas esto, claude mcp list muestra Needs authentication.

Codex

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

codex mcp list muestra el estado de autenticación. Si las herramientas aún no aparecen en una sesión después de un inicio de sesión exitoso, es un error conocido de Codex donde las credenciales OAuth se obtienen y luego nunca se usan (openai/codex#20009). Usa el puente a continuación hasta que se solucione.

Respaldo para cualquier cliente cuyo soporte OAuth esté roto

mcp-remote hace el baile OAuth por sí mismo y re-expone el servidor a través de stdio, que todos los clientes MCP soportan:

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

Abre la misma página de consentimiento en la primera ejecución y almacena en caché los tokens.


Caso 2 — en tu máquina, accesible desde cualquier lugar

El mismo servidor, más algo que le dé una dirección HTTPS pública. Tu correo permanece en tu máquina, y nada escucha en tu red doméstica, porque el túnel marca hacia afuera. Necesitas esto para las aplicaciones de teléfono inteligente y escritorio: un conector personalizado es obtenido por los servidores del proveedor, por lo que no puede alcanzar una dirección privada.

Con Tailscale (sin necesidad de dominio)

Un comando, igual en macOS y Linux, y obtienes un nombre de host HTTPS sin poseer un dominio.

tailscale funnel --bg 8080

--bg lo mantiene ejecutándose entre reinicios. Imprime la URL pública, que se ve como https://your-machine.your-tailnet.ts.net. Ese es el nombre de host a usar:

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

Funnel necesita certificados HTTPS y el atributo de nodo Funnel habilitado para tu tailnet; el CLI ofrece añadir la línea de política la primera vez, y el resto está en tu consola de administración. tailscale funnel status muestra qué está expuesto, y tailscale funnel --https=443 off lo desactiva.

Con Cloudflare (posees un dominio, y está en Cloudflare)

Usa esto si quieres un nombre de host en tu propio dominio en lugar de uno .ts.net. mail.example.com a continuación es tu dominio, ya añadido a tu cuenta de Cloudflare — Cloudflare no te entrega un nombre de host para un túnel con nombre.

cloudflared tunnel login
cloudflared tunnel create your-mail

create imprime el UUID del túnel y el archivo de credenciales que acaba de escribir:

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

Usa esa ruta exacta a continuación; cloudflared tunnel list imprime el UUID de nuevo si lo pierdes. Enruta el nombre de host, luego escribe ~/.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 mantenerlo ejecutándose: en Linux, sudo cloudflared service install. En macOS, instálalo a través de Homebrew y usa brew services start cloudflared, porque la ruta de instalación de sudo busca su certificado bajo el directorio del usuario root y no encontrará el que cloudflared tunnel login escribió en el tuyo.

Luego establece PUBLIC_URL=https://mail.example.com en .env y docker compose up -d.

De cualquier manera

PUBLIC_URL tiene que coincidir exactamente con lo que escribes en el cliente. El servidor publica PUBLIC_URL + /mcp como el resource en sus metadatos OAuth, y una discrepancia allí es la razón más común por la que un conector se niega a añadirse.

Una cosa que debes saber antes de empezar en un teléfono inteligente: ni Claude ni ChatGPT te permiten añadir un conector desde la aplicación del teléfono inteligente. Lo añades una vez en la web (o en la aplicación de escritorio de Claude), y luego aparece en tu teléfono inteligente. Intentar hacer la configuración en el propio teléfono inteligente hará perder tu tiempo.

Claude — añade en web o escritorio, luego usa en tu teléfono inteligente

  1. En claude.ai o en Claude Desktop, ve a Configuración → Conectores, y haz clic en + junto a Conectores, o Añadir conector personalizado.
  2. Dale un nombre y la URL <PUBLIC_URL>/mcp. Deja los campos avanzados de OAuth vacíos: este servidor registra clientes dinámicamente.
  3. Claude abre la página de consentimiento. Introduce tu OAUTH_PASSPHRASE.
  4. Abre la aplicación de Claude en tu teléfono inteligente. El conector ya está allí, y las herramientas están disponibles en un chat. Actívalo para una conversación desde el menú de herramientas o conectores en el compositor.

ChatGPT — añade en web, luego usa en tu teléfono inteligente

Los conectores MCP personalizados están detrás del modo desarrollador, que necesita una cuenta Pro, Plus, Business, Enterprise o Education y solo está disponible en la web.

  1. En ChatGPT en la web, abre Configuración → Seguridad e inicio de sesión y activa Modo desarrollador. En espacios de trabajo Business y Enterprise, un administrador puede tener que permitirlo primero.
  2. Añade un conector para un servidor MCP remoto y dale la URL <PUBLIC_URL>/mcp, con OAuth como autenticación. ChatGPT soporta registro dinámico de clientes, así que no hay nada que pegar.
  3. Aprueba la página de consentimiento con tu OAUTH_PASSPHRASE.
  4. Abre ChatGPT en tu teléfono inteligente y habilita el conector en un chat.

Estos menús se mueven. Si los nombres anteriores no coinciden con lo que ves, busca el modo desarrollador en la configuración, luego el lugar que añade un conector por URL.

ChatGPT desactiva algunas acciones de escritura de MCP en móvil. Eso no tiene efecto aquí, porque este servidor no tiene acciones de escritura en absoluto.

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 — en un VPS, accesible desde cualquier lugar

Elige esto cuando quieras que el espejo permanezca activo esté o no encendida tu máquina. Cuesta unos pocos dólares al mes y una compensación real: una copia completa en texto plano de tu correo se mueve a un disco alquilado, con las contraseñas de aplicación en el mismo entorno. Lee Seguridad antes de elegirlo. La instalación es el caso 1 más un túnel, en la computadora de otra persona. No hay puertos que abrir, ni DNS que configurar, ni certificados que gestionar.

En una máquina nueva con Debian o 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 va al final porque no sabes el nombre de host hasta que el paso 3 lo imprime.

Conectar un cliente es idéntico al caso 2.

restart: unless-stopped en compose.yaml trae los contenedores de vuelta después de un reinicio. Verifícalo con la herramienta folders, que informa la última sincronización de cada cuenta y su último error, o con docker compose logs --tail=50.

Ahora ve y lee Hardening. Una VPS a la que puedas acceder por SSH con contraseña, que tenga una copia de tu correo, es peor que no ejecutar esto en absoluto.


Hardening

Nada de esto es necesario para que el servidor funcione, por eso no está en los pasos de instalación. Está ordenado por cuánto te beneficia. El caso 1 no necesita nada de esto.

Elige una frase de contraseña real. OAUTH_PASSPHRASE es toda la puerta. Una suposición incorrecta le cuesta al atacante un segundo, y las suposiciones se serializan, por lo que ejecutarlas en paralelo no ayuda, pero ninguna de esas cosas salva una frase de contraseña corta. Usa una larga que aún puedas escribir en un teléfono inteligente.

Bloquea SSH (caso 3). Una máquina alquilada con inicio de sesión por contraseña y una copia de tu correo es la peor combinación en este documento. Como root, antes de cualquier otra cosa:

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

Luego haz la instalación como mail, no como root.

Cierra los puertos que no estás usando (caso 3). Con un túnel no necesitas ningún puerto de entrada, así que:

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

Restringe quién puede llegar al conector. Si lo único que habla con tu servidor es un conector personalizado en una aplicación de Claude, ese tráfico llega desde el rango de salida publicado de Anthropic, 160.79.104.0/21, y puedes rechazar todo lo demás en el túnel o el firewall. No hagas esto si también usas Claude Code o Codex desde una laptop, ya que esos se conectan desde dondequiera que estés.

Haz copias de seguridad de los volúmenes, o acepta una resincronización. compose.yaml mantiene el maildir y el índice en volúmenes con nombre. Nada en ellos es único — todo sigue estando en tu servidor de correo — pero volver a descargar un buzón grande lleva tiempo y molesta a los proveedores que limitan el ancho de banda.

Sabe lo que la frase de contraseña no protege. Protege la superficie de MCP. No cifra nada en reposo. Consulta Security.

Tu propio dominio y certificado en lugar de un túnel

Si prefieres terminar TLS tú mismo en un dominio que posees, apunta un registro A a la máquina y pon Caddy al frente. Agrega 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
}

Abre ambos puertos — el 80 no es opcional, Caddy lo usa para el desafío de certificado y la redirección HTTPS:

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

Caddy obtiene y renueva el certificado por sí mismo. Establece PUBLIC_URL al nombre de host y docker compose up -d.

El archivo de cuentas

Montado de solo lectura en /config/accounts.json (consulta compose.yaml). JSON, analizado con encoding/json, expandido contra el entorno del proceso antes del análisis, de modo que ${VAR} en cualquier valor de cadena se reemplace con la variable de entorno de ese nombre. Así es como los secretos se mantienen fuera del archivo:

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

Claves por cuenta:

ClavePredeterminadoNotas
nameObligatorio. Sin espacios, comillas ni barras (hacia adelante o hacia atrás). Se convierte en el directorio maildir de nivel superior para la cuenta y en el argumento account en las llamadas a herramientas.
hostObligatorio. Nombre de host del servidor IMAP.
port993 (imaps) o 143 (de lo contrario)
userObligatorio. Consulta Provider notes: iCloud quiere el nombre corto, no la dirección de correo completa.
passwordObligatorio. ${VAR} se expande desde el entorno; una contraseña literal también funciona pero no se recomienda.
tlsimapsimaps, starttls o none.
patterns["*"]Patrones de carpetas de mbsync — qué carpetas reflejar.
exclude_foldersdescubierto automáticamenteNombres de carpetas a excluir de la búsqueda por defecto (consulta SPECIAL-USE discovery). Establecer esto anula el descubrimiento por completo para esa cuenta.

Un nombre de cuenta debe ser único. Se requiere al menos una cuenta; un array accounts vacío es un error de inicio.

Variables de entorno

VariableObligatoriaPredeterminadoSignificado
CONFIGRuta al archivo de cuentas.
MAILDIRRaíz del maildir; cada cuenta recibe un subdirectorio.
INDEXDirectorio del índice notmuch/Xapian.
PUBLIC_URLLa URL externa a la que se accede al servidor, exactamente como la usará un cliente (una barra final, si la hay, se elimina). Se usa en los metadatos de OAuth y debe coincidir con lo que escribes en el cliente.
OAUTH_PASSPHRASELa única frase de contraseña que protege la pantalla de consentimiento.
SYNC_INTERVALno10mPeríodo de sincronización completa, como duración de Go (5m, 1h). El valor predeterminado sigue la cadencia recomendada por Google para clientes IMAP de 10 minutos. Una cuenta que sigue fallando se reintenta al doble de este intervalo, luego cuatro veces, con un tope de una hora, para que una interrupción del proveedor o un bloqueo por cuota no se golpee repetidamente.
SYNC_TIMEOUTno1hPlazo por cuenta para una ejecución de mbsync, como duración de Go. Una ejecución cortada por el plazo se reanuda donde se detuvo en la siguiente pasada, por lo que un primer espejo grande se completa en partes. Piensa antes de aumentarlo en una configuración de múltiples cuentas: las cuentas se sincronizan una a la vez, por lo que una cuenta estancada en una conexión limitada bloquea a las demás durante todo el plazo.
LISTEN_ADDRno:8080Dirección a la que se vincula el servidor HTTP.
INIT_MIRRORnosin establecerEstablécelo a 1 para sincronizar en un directorio vacío que no sea un punto de montaje. No es necesario con compose, donde /mail es un volumen.

CONFIG, MAILDIR y INDEX son obligatorias; el proceso se niega a iniciar sin ellas. PUBLIC_URL y OAUTH_PASSPHRASE son obligatorias para la capa de OAuth y el proceso también falla al iniciar sin ellas.

La imagen del contenedor ya establece cuatro de estas (Dockerfile): MAILDIR=/mail, INDEX=/index, CONFIG=/config/accounts.json, LISTEN_ADDR=:8080. compose.yaml no anula ninguna de ellas. Déjalas en paz a menos que también estés cambiando el montaje de volumen o el montaje de configuración correspondiente en compose.yaml — una anulación que no mueve el montaje con ella apunta el servidor a una ruta vacía o inexistente.

Sin Docker

Los binarios de lanzamiento para Linux y macOS, amd64 y arm64, están en la página de lanzamientos, con sumas de verificación. El binario invoca a mbsync, notmuch y w3m, así que instala esos primero — brew install isync notmuch w3m en macOS, apt install isync notmuch w3m en Debian y Ubuntu. isync 1.4.4 o más reciente funciona.

Luego la misma configuración que el contenedor, con rutas de tu elección. Los volúmenes del contenedor comienzan como puntos de montaje, que la protección de maildir vacío lee como una primera ejecución genuina; un directorio normal que creas tú mismo se ve exactamente como un volumen que nunca se montó para esa misma protección, por lo que necesita INIT_MIRROR=1 para decir que realmente se pretende que sea una primera ejecución aquí:

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

Windows no es compatible: el manejo del maildir se apoya en semánticas del sistema de archivos Unix, y no hay mbsync al que invocar.

Compilándolo tú mismo

CI compila, prueba y publica cada imagen, así que nadie tiene que hacerlo — pero es un comando si quieres: docker build -t your-mail-mcp . para el contenedor, o go build para el binario (Go 1.27, con las tres herramientas anteriores en PATH para las pruebas).

Notas del proveedor

Las notas de iCloud provienen de la operación a largo plazo de un espejo real de iCloud que precede a este servidor. Las notas de Gmail y Dovecot provienen de la documentación del proveedor y de la investigación del proyecto, y no todas se han vuelto a verificar a través de este servidor todavía.

  • iCloud (imap.mail.me.com): el user de IMAP es el nombre corto — la parte antes de @icloud.com — no la dirección de correo completa. iCloud limita las conexiones IMAP concurrentes; por eso la configuración de mbsync generada fija PipelineDepth 1 para cada cuenta, y no es configurable.
  • Gmail (imap.gmail.com): requiere una contraseña de aplicación, que requiere que la verificación en dos pasos esté habilitada en la cuenta primero — Gmail no acepta la contraseña de la cuenta directamente a través de IMAP. Gmail también mantiene una copia de esencialmente todo en [Gmail]/All Mail, por lo que el espejo de una cuenta de Gmail es aproximadamente el doble del tamaño que sugiere la lista de carpetas, ya que la mayoría de los mensajes existen tanto en su carpeta como en All Mail. El primer espejo de una cuenta grande de Gmail lleva horas, y Google también impone una cuota diaria de descarga IMAP (alrededor de 2.5 GB por día), por lo que un buzón de varios gigabytes extiende su primer espejo a lo largo de varios días. Esto es normal: el servidor sigue reintentando en su horario y mbsync se reanuda donde se detuvo. Establece SYNC_TIMEOUT a algo como 8h para el primer espejo para que una ejecución larga no se corte por el plazo predeterminado de una hora.
  • Servidores Dovecot (muchos proveedores autohospedados y más pequeños) comúnmente prefijan los nombres de carpeta con INBOX. (por ejemplo, INBOX.Sent). Si folders muestra nombres de carpeta que no esperabas, esta suele ser la razón.

Seguridad

Las contraseñas de las cuentas se suministran a través del entorno del proceso (${VAR} en accounts.json, o valores literales). Al inicio, el servidor las escribe en un archivo de configuración de mbsync generado en el disco dentro del contenedor, con modo de archivo 0600. Ese archivo no está cifrado. Cualquier cosa que pueda leer el entorno del contenedor, o ese archivo, puede leer las contraseñas en texto plano.

La protección en reposo — cifrado de disco, restringir quién puede ejecutar comandos dentro del contenedor, acceso al host — es responsabilidad del operador. Este servidor no afirma cifrar las credenciales en reposo, y no lo intenta.

La frase de contraseña de OAuth se verifica en tiempo constante y protege todo el servidor con un único secreto compartido; no es un sistema de credenciales por usuario. Trata OAUTH_PASSPHRASE y las contraseñas de las cuentas de correo con el mismo cuidado.

Los resúmenes de hilos de search incluyen un nombre para mostrar para cada mensaje en un hilo coincidente, que un remitente controla. Un mensaje en una carpeta excluida por defecto (junk, trash) aún puede poner su propio nombre elegido por el atacante frente a ti de esta manera, aunque su cuerpo nunca lo haga — search no obtiene ni muestra el cuerpo de un mensaje excluido. thread y show son rutas de lectura, no sujetas a esto: thread excluye las respuestas de junk/trash por defecto (consulta la tabla de herramientas anterior), y show lee un solo mensaje del que ya tienes el id. Esta fuga de nombre para mostrar en search no se corrige en esta versión.

Solución de problemas

"maildir ... es un directorio plano vacío, no un punto de montaje: se niega a sincronizar" — el servidor verifica si tu maildir es un sistema de archivos montado. Un volumen montado que resulta estar vacío es una primera ejecución y se sincroniza sin ningún opt-in, por eso compose no necesita un paso adicional. Un directorio plano vacío es ambiguo: un maildir nuevo se ve exactamente como una ruta cuyo volumen nunca se montó, y sincronizar en el segundo vuelve a descargar cada cuenta en un directorio que desaparece en el momento en que arreglas el montaje. O bien monta el almacenamiento donde apunta MAILDIR, o establece INIT_MIRROR=1 si realmente se pretende que sea un directorio ordinario en este sistema de archivos.

"maildir ...: no such file or directory" — la ruta no existe en absoluto. Con compose eso significa que el volumen o el montaje de enlace falta en compose.yaml; ejecutando el binario directamente, significa que MAILDIR es incorrecto. Comprueba el estado de sincronización por cuenta con la herramienta folders. Enumera cada cuenta configurada, su última hora de sincronización exitosa, su último error si lo hay, sus carpetas y las etiquetas en el índice. Una sola cuenta con una contraseña incorrecta o una contraseña específica de aplicación caducada no detiene a las demás — los fallos de sincronización están aislados por cuenta — pero aparecerá aquí como una línea last error, no como silencio.

Exclusión de correo no deseado/paperera, dos formas de fallo diferentes:

  • "special-use discovery: account NAME: ..." en los registros del contenedor significa que la conexión de inicio, el inicio de sesión o el LIST para esa cuenta fallaron por completo. En ese fallo no hay nombres de carpetas a los que recurrir para comparar, por lo que esa cuenta no tiene nada excluido en absoluto — ni siquiera por la lista de nombres en inglés integrada — hasta que se solucione el problema de conexión o se establezca exclude_folders para ella manualmente.
  • Sin línea de error, pero folders aún muestra nada excluido significa que el LIST tuvo éxito — el servidor simplemente no anuncia atributos \Junk/\Trash (sin soporte RFC 6154 SPECIAL-USE) y sus nombres de carpetas no coinciden con la lista integrada en inglés (junk, spam, trash, deleted messages, deleted items, bulk mail). Este es el caso de buzones localizados — un buzón alemán o francés, por ejemplo — y la solución es la misma: establece exclude_folders manualmente.

exclude_folders en accounts.json, p. ej. "exclude_folders": ["Papierkorb"], tiene prioridad sobre SPECIAL-USE y la lista integrada en todos los casos.