Migrate tool for imap via protonmail bridge
  • Python 70%
  • Shell 30%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-07 12:07:28 +00:00
.gitignore Update readme 2026-08-07 08:06:26 -04:00
currentpwd-to-proton.sh Update readme 2026-08-07 08:06:26 -04:00
LICENSE Update readme 2026-08-07 08:06:26 -04:00
README.md Update README.md 2026-08-07 12:07:28 +00:00
sanitise_mbox.py updates to formatting 2026-08-07 07:39:22 -04:00

imap-sanitiser

Migration tool for pushing legacy mbox archives into Proton Mail via the Proton Mail Bridge IMAP interface.

Proton Bridge enforces strict RFC 5322 compliance. Real-world email archives spanning decades do not. This tool sanitises mbox files to bridge's standards before upload, handling malformed address headers, legacy character encodings, and missing envelope lines.

What It Does

  • Rebuilds address headers (From, To, Cc, Bcc, Reply-To, Return-Path, Sender) to ensure every address is wrapped in angle brackets. Bare names like MAILER-DAEMON or a.friend get a fallback <unknown@localhost>.
  • Strips the Sender header entirely, which causes parse failures in Bridge.
  • Converts legacy character encodings (windows-1252, cp1252, iso-8859-1, latin-1, and others) to UTF-8.
  • Preserves mbox envelope lines by parsing raw bytes line-by-line instead of using Python's email.message_from_bytes(), which silently strips them.
  • Injects a Date header if one is missing.
  • Concatenates all mbox files in a .sbd directory tree into a single temp file for batch upload.
  • Diverts failures to a separate failed_<folder>.mbox for manual review.

The Trade-Off

Display names are stripped from address headers. A message that originally showed From: "Alex" <alex@example.com> will show From: <alex@example.com> after sanitisation. The email address, subject, body, and attachments are all preserved. The display name is lost. Given the alternative (zero messages imported), that is an acceptable trade.

Prerequisites

  • Proton Mail Bridge installed and running (local IMAP at 127.0.0.1:1143)
  • Python 3 (standard library only, no pip packages required)
  • imap-upload (imap_upload.py) installed and on your PATH or referenced by full path in the script
  • Your Bridge IMAP credentials, sourced from Bridge's internal info tool

Setup

1. Get your Bridge credentials

Proton Bridge does not use your email address as the IMAP username. Retrieve the generated credentials from Bridge:

protonmail-bridge --cli

Inside the CLI, use the info command to display your IMAP username and password.

2. Store credentials securely

Create ~/.proton_bridge_creds:

cat > ~/.proton_bridge_creds << 'EOF'
export IMAP_USER="your_bridge_username"
export IMAP_PASS="your_bridge_password"
EOF

chmod 600 ~/.proton_bridge_creds

Source it before running the migration: source ~/.proton_bridge_creds

3. Configure the script

Edit currentpwd-to-proton.sh and update:

- TMP_DIR — temporary working directory for concatenated mbox files
- IMAP_HOST / IMAP_PORT — if your Bridge instance runs elsewhere
- The path to imap_upload.py if it is not on your PATH

Usage

./currentpwd-to-proton.sh <path-to-.sbd-directory> <IMAP-target-folder>

Examples

Migrate a Yahoo archive

./currentpwd-to-proton.sh "Yahoo!.sbd" Folders/alt/heim/yahoo

The script will:

Create the target IMAP folder hierarchy if it does not exist
Concatenate all mbox files in the .sbd directory into one temp file
Count and report messages per folder
Run the Python sanitiser on the combined mbox
Upload the sanitised file via imap-upload
Report total, successful, and failed message counts
Save failed messages to failed_<folder>.mbox for manual review
Clean up temporary files

Known Issues

  • Folders created via IMAP do not auto-subscribe. After migration, subscribe to each folder in Proton's web UI or add m.subscribe() to the inline Python in the script.
  • imaplib has a hardcoded 1 MB line-length limit. For folders with hundreds of thousands of messages, override imaplib._MAXLINE before running SEARCH commands.
  • OK: 0 from imap-upload is a silent failure. If imap-upload cannot parse the mbox file, it reports zero messages uploaded successfully, not an error. Always check the OK count, not just the error count.

Misc

Background

Full narrative of the migration, including all seven failure modes encountered and the lessons learned, is documented in the blog post: Migrating 30,000+ Personal Emails to Proton Mail: A Field Report

LICENSE

MIT. See LICENSE

Contributing

This was built for a specific migration. If you hit a case it does not handle, fork it and adapt. Pull requests welcome if they fix genuine bugs rather than edge cases specific to one archive.