Email fetcher: one.com → Migadu migration
Email Fetcher Migration: one.com → Migadu
MC Task: #100395
Migration Date: 2026-05-12 14:30-15:00 UTC
Verification: 12/12 atomic claims PASS (15:05 UTC)
Status: Production cutover complete
Overview
On 2026-05-10 20:20 UTC, the CEO registered Migadu email hosting and switched MX records for alai.no and basicconsulting.no to Migadu (priority 10/20), retaining one.com as fallback (priority 100). The email-agent daemon (~/system/tools/email-agent.js) and mail-native.js IMAP client were still configured for one.com IMAP, making mail routed to Migadu invisible to John's email processing pipeline. On 2026-05-12, John executed the migration cutover: provisioned 4 new Migadu mailboxes via Admin API, rotated Bitwarden credentials, reconfigured himalaya CLI and mail-native.js to use imap.migadu.com:993 + smtp.migadu.com:465, and deployed a workaround for himalaya 1.1.0 PLAIN SASL incompatibility by forcing ImapFlow fallback (HIMALAYA_DISABLED=1 env var).
Affected Components
- Config files:
~/.config/himalaya/config.toml— 5 accounts (john, info, alai, alem, dev) migrated from imap.one.com to imap.migadu.com; folder aliases changed fromINBOX.Sentto flatSent~/system/tools/mail-native.js— VAULT_NAMES updated toMigadu — <addr>prefix; default imap_host/smtp_host changed to Migadu servers~/Library/LaunchAgents/com.john.email-agent.plist— addedHIMALAYA_DISABLED=1environment variable
- Bitwarden items: 5 items created/updated with naming convention
Migadu — <email address>- Migadu — [email protected]
- Migadu — [email protected]
- Migadu — [email protected]
- Migadu — [email protected]
- Migadu — [email protected]
- Daemons:
com.john.email-agent— restarted after config changes; now connects to 6 mailboxes (5 Migadu + 1 Gmail)
- Backups:
~/.config/himalaya/config.toml.one-com-backup-20260512-163802~/Library/LaunchAgents/com.john.email-agent.plist.bak-20260512-164711- mail-native.js: revert via
git diffagainst pre-migration commit
Migadu Mailbox Provisioning
Migadu mailboxes are created via Admin API using the API key from Bitwarden item migadu keyy.
Create mailbox via API
# Get Migadu API credentials
API_KEY=$(bw get password 'migadu keyy' --session $(cat /tmp/bw-session))
DOMAIN="alai.no" # or basicconsulting.no
LOCAL_PART="john"
PASSWORD=$(openssl rand -base64 24)
# Create mailbox
curl -X POST "https://api.migadu.com/v1/domains/${DOMAIN}/mailboxes" \
-u "[email protected]:${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"local_part": "'"${LOCAL_PART}"'",
"password": "'"${PASSWORD}"'",
"may_send": true,
"may_receive": true,
"may_access_imap": true,
"may_access_pop3": true,
"may_access_managesieve": true
}'
# Verify creation
curl "https://api.migadu.com/v1/domains/${DOMAIN}/mailboxes/${LOCAL_PART}" \
-u "[email protected]:${API_KEY}"
Store credentials in Bitwarden
# Create Bitwarden item with naming convention
echo "{
\"organizationId\": null,
\"folderId\": null,
\"type\": 1,
\"name\": \"Migadu — ${LOCAL_PART}@${DOMAIN}\",
\"login\": {
\"username\": \"${LOCAL_PART}@${DOMAIN}\",
\"password\": \"${PASSWORD}\",
\"uris\": [
{ \"match\": null, \"uri\": \"imap.migadu.com\" },
{ \"match\": null, \"uri\": \"smtp.migadu.com\" }
]
}
}" | bw encode | bw create item --session $(cat /tmp/bw-session)
Evidence: See /Users/makinja/system/state/evidence/migadu-mailbox-create-20260512T083653Z.log for actual execution output of 4 mailboxes created on 2026-05-12.
Bitwarden Credential Structure
Naming convention: Migadu — <email address>
This convention is hardcoded in ~/system/tools/mail-native.js VAULT_NAMES mapping:
const VAULT_NAMES = {
john: 'Migadu — [email protected]',
info: 'Migadu — [email protected]',
alai: 'Migadu — [email protected]',
alem: 'Migadu — [email protected]',
dev: 'Migadu — [email protected]',
gmail: 'Gmail — [email protected]'
};
List all Migadu credentials:
bw list items --search 'Migadu —' --session $(cat /tmp/bw-session) | jq -r '.[] | "\(.name) (\(.id))"'
End-to-End Verification
Use Python imaplib to verify IMAP login and mailbox access:
#!/usr/bin/env python3
import imaplib
import json
import subprocess
def test_imap(email):
# Fetch password from Bitwarden
vault_name = f"Migadu — {email}"
bw_session = open('/tmp/bw-session').read().strip()
password = subprocess.check_output(
['bw', 'get', 'password', vault_name, '--session', bw_session],
text=True
).strip()
# Connect to Migadu IMAP
imap = imaplib.IMAP4_SSL('imap.migadu.com', 993)
imap.login(email, password)
status, messages = imap.select('INBOX')
if status == 'OK':
msg_count = int(messages[0])
print(f"✓ {email}: INBOX OK, {msg_count} messages")
else:
print(f"✗ {email}: SELECT INBOX failed")
imap.logout()
if __name__ == '__main__':
accounts = [
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]'
]
for acc in accounts:
test_imap(acc)
Expected output:
✓ [email protected]: INBOX OK, 881 messages
✓ [email protected]: INBOX OK, 0 messages
✓ [email protected]: INBOX OK, 13 messages
✓ [email protected]: INBOX OK, 0 messages
✓ [email protected]: INBOX OK, 0 messages
Evidence: Verifier executed equivalent IMAP4_SSL login probes on 2026-05-12 15:05 UTC — claim C3 PASS (see /Users/makinja/system/state/evidence/mc-100395-verifier-verdict-20260512.md).
Rollback Procedure
If Migadu migration must be reverted (e.g., service outage, credential issues), follow these steps:
- Stop email-agent daemon:
launchctl unload ~/Library/LaunchAgents/com.john.email-agent.plist - Restore himalaya config to one.com:
cp ~/.config/himalaya/config.toml ~/.config/himalaya/config.toml.migadu-backup-$(date +%Y%m%d-%H%M%S) cp ~/.config/himalaya/config.toml.one-com-backup-20260512-163802 ~/.config/himalaya/config.toml - Revert mail-native.js (verify before running):
cd ~/system/tools git diff mail-native.js # Review changes git checkout HEAD -- mail-native.js # Revert to pre-migration state - Remove HIMALAYA_DISABLED from LaunchAgent:
cp ~/Library/LaunchAgents/com.john.email-agent.plist ~/Library/LaunchAgents/com.john.email-agent.plist.bak-$(date +%Y%m%d-%H%M%S) # Edit plist to remove HIMALAYA_DISABLED env var: plutil -replace EnvironmentVariables.HIMALAYA_DISABLED -string "" ~/Library/LaunchAgents/com.john.email-agent.plist # Or manually edit and remove the key/value pair - Update Bitwarden vault names in code (if needed):
If mail-native.js git revert doesn't restore old BW item names, manually edit VAULT_NAMES back to
Email - <addr>pattern. - Restart daemon:
launchctl load ~/Library/LaunchAgents/com.john.email-agent.plist - Verify connection to one.com:
Expect lines like:tail -f ~/system/logs/email-agent.log | grep -E "Connected|ERROR"Connected to john ([email protected])within 60 seconds.
Rollback time estimate: 5-10 minutes (assuming backups are intact).
Known Issue: himalaya 1.1.0 PLAIN SASL vs Migadu
Symptom: himalaya CLI 1.1.0 fails IMAP login to imap.migadu.com:993 with error:
Error: cannot parse envelope at line 1 near column 1
Kind: MalformedMessage
Root cause: himalaya 1.1.0 attempts PLAIN SASL authentication, which Migadu's IMAP server rejects or mishandles (verify before re-running). This is a known incompatibility between himalaya's IMAP library and Migadu's Dovecot configuration.
Workaround: Force email-agent.js to skip himalaya and use ImapFlow (native Node.js IMAP client) by setting environment variable:
<key>EnvironmentVariables</key>
<dict>
<key>HIMALAYA_DISABLED</key>
<string>1</string>
</dict>
in ~/Library/LaunchAgents/com.john.email-agent.plist.
Evidence: Email-agent.js log on 2026-05-12 14:48:12 shows:
{"timestamp":"2026-05-12T14:48:12.896Z","service":"email-agent","level":"info","message":"[WARN] himalaya disabled for john, falling back to legacy unseen fetch"}
All 6 mailboxes connected successfully via ImapFlow (claims C9/C10 PASS).
Long-term fix: File upstream issue with himalaya maintainers or test downgrade to himalaya 1.0.x. Track in separate MC task.
Migration Timeline
| Timestamp (UTC) | Event |
|---|---|
| 2026-05-10 20:20 | CEO registered Migadu, MX records switched |
| 2026-05-12 08:36 | 4 mailboxes provisioned via Migadu API (post, dev, info, john@basicconsulting) |
| 2026-05-12 14:37 | [email protected] mailbox created |
| 2026-05-12 14:48 | email-agent cutover: himalaya disabled, ImapFlow connected to 5 Migadu + 1 Gmail |
| 2026-05-12 14:51 | First mail cycle after cutover: 3 new emails classified via Ollama |
| 2026-05-12 15:05 | Verifier subagent: 12/12 claims PASS |
Post-Migration State
- MX records: aspmx1/2.migadu.com (priority 10/20) PRIMARY, mx01.one.com (priority 100) FALLBACK
- Active mailboxes: 5 Migadu (alem, john×2, info, dev) + 1 Gmail (alembasic)
- Daemon status: com.john.email-agent running, last exit 0, connects every 3 minutes
- Mail processing: End-to-end verified — GitHub PR notification, GCP billing, undeliverable bounce all classified via Ollama (claim C11 PASS)
- one.com mailboxes: Still receiving via fallback MX prio 100 — backlog drain deferred to future MC task
References
- MC Task: #100395
- Evidence directory:
/Users/makinja/system/state/evidence/mc-100395-verifier-verdict-20260512.md— 12/12 atomic claims PASSmc-100395-migration-complete-20260512T145224Z.log— Builder summarymigadu-mailbox-create-20260512T083653Z.log— API provisioning logsemail-agent-migadu-cutover-20260512T145138Z.log— Daemon cutover logs
- Bitwarden API key:
migadu keyy(search in BW vault) - Migadu Admin API: https://api.migadu.com/v1/