# 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 from `INBOX.Sent` to flat `Sent`
    - `~/system/tools/mail-native.js` — VAULT\_NAMES updated to `Migadu — <addr>` prefix; default imap\_host/smtp\_host changed to Migadu servers
    - `~/Library/LaunchAgents/com.john.email-agent.plist` — added `HIMALAYA_DISABLED=1` environment variable
- **Bitwarden items:** 5 items created/updated with naming convention `Migadu — <email address>`
    - Migadu — alem@alai.no
    - Migadu — john@alai.no
    - Migadu — john@basicconsulting.no
    - Migadu — info@basicconsulting.no
    - Migadu — dev@alai.no
- **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 diff` against 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

```bash
# 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 "admin@alai.no:${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 "admin@alai.no:${API_KEY}"

```

### Store credentials in Bitwarden

```bash
# 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:

```javascript
const VAULT_NAMES = {
  john: 'Migadu — john@basicconsulting.no',
  info: 'Migadu — info@basicconsulting.no',
  alai: 'Migadu — john@alai.no',
  alem: 'Migadu — alem@alai.no',
  dev: 'Migadu — dev@alai.no',
  gmail: 'Gmail — alembasic@gmail.com'
};

```

**List all Migadu credentials:**

```bash
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:

```python
#!/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 = [
        'alem@alai.no',
        'john@alai.no',
        'john@basicconsulting.no',
        'info@basicconsulting.no',
        'dev@alai.no'
    ]
    for acc in accounts:
        test_imap(acc)

```

**Expected output:**

```
✓ alem@alai.no: INBOX OK, 881 messages
✓ john@alai.no: INBOX OK, 0 messages
✓ john@basicconsulting.no: INBOX OK, 13 messages
✓ info@basicconsulting.no: INBOX OK, 0 messages
✓ dev@alai.no: 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:

1. **Stop email-agent daemon:**```bash
    launchctl unload ~/Library/LaunchAgents/com.john.email-agent.plist
    
    ```
2. **Restore himalaya config to one.com:**```bash
    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
    
    ```
3. **Revert mail-native.js (verify before running):**```bash
    cd ~/system/tools
    git diff mail-native.js  # Review changes
    git checkout HEAD -- mail-native.js  # Revert to pre-migration state
    
    ```
4. **Remove HIMALAYA\_DISABLED from LaunchAgent:**```bash
    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
    
    ```
5. **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.
6. **Restart daemon:**```bash
    launchctl load ~/Library/LaunchAgents/com.john.email-agent.plist
    
    ```
7. **Verify connection to one.com:**```bash
    tail -f ~/system/logs/email-agent.log | grep -E "Connected|ERROR"
    
    ```
    
    Expect lines like: `Connected to john (john@basicconsulting.no)` 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:

```xml
<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

<table id="bkmrk-timestamp-%28utc%29event"><thead><tr><th>Timestamp (UTC)</th><th>Event</th></tr></thead><tbody><tr><td>2026-05-10 20:20</td><td>CEO registered Migadu, MX records switched</td></tr><tr><td>2026-05-12 08:36</td><td>4 mailboxes provisioned via Migadu API (post, dev, info, john@basicconsulting)</td></tr><tr><td>2026-05-12 14:37</td><td>john@alai.no mailbox created</td></tr><tr><td>2026-05-12 14:48</td><td>email-agent cutover: himalaya disabled, ImapFlow connected to 5 Migadu + 1 Gmail</td></tr><tr><td>2026-05-12 14:51</td><td>First mail cycle after cutover: 3 new emails classified via Ollama</td></tr><tr><td>2026-05-12 15:05</td><td>Verifier subagent: 12/12 claims PASS</td></tr></tbody></table>

## 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 PASS
    - `mc-100395-migration-complete-20260512T145224Z.log` — Builder summary
    - `migadu-mailbox-create-20260512T083653Z.log` — API provisioning logs
    - `email-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/](https://api.migadu.com/v1/)