Skip to content

Mail Poller Z2 Deployment Checklist

Phase 1: Prepare (Credential audit + state export)

Credentials

  • [ ] Verify Gmail app password format

    bash
    cat /opt/itachi/credentials/mail_imap_credential.json 2>/dev/null || echo "File missing"
  • [ ] Verify Outlook Graph credentials

    bash
    cat /opt/itachi/credentials/outlook-graph-credentials.json 2>/dev/null || echo "File missing"
  • [ ] Verify all 3 Outlook token files exist

    bash
    ls -1 /opt/itachi/credentials/outlook-tokens-*.json | wc -l
    # Should be 3

State Export (from current Mac poller)

  • [ ] Export current IMAP state from Mac

    bash
    # On Mac:
    cd ~/Sandpit/hinata/data/mail
    cp imap-state-*.json /tmp/mail-poller-state-backup-$(date +%Y%m%d).json
  • [ ] Document state cursor positions

    bash
    # Check last UID per folder (example):
    cat ~/Sandpit/hinata/data/mail/imap-state-gmail.json | python3 -m json.tool | head -30

Archive Verification

  • [ ] Count existing emails in archive

    bash
    find ~/Sandpit/hinata/resources/email-poller -name "*.json" | wc -l
  • [ ] Check archive size

    bash
    du -sh ~/Sandpit/hinata/resources/email-poller
  • [ ] Verify no corrupted JSON files

    bash
    find ~/Sandpit/hinata/resources/email-poller -name "*.json" -exec python3 -m json.tool {} \; > /dev/null 2>&1 && echo "OK" || echo "Found corrupt JSON"

Z2 Preflight

  • [ ] Z2 hardware online and SSH accessible

    bash
    ssh z2 'pct list' | grep -E "^100|^101|^102|^103"
  • [ ] ct102 running (Debian 12)

    bash
    ssh z2 'pct status 102'
  • [ ] ct103 running with credentials

    bash
    ssh z2 'pct exec 103 -- ls -l /opt/itachi/credentials/'
  • [ ] ct102 can see ct103 (ping test via Proxmox network)

    bash
    ssh z2 'pct exec 102 -- timeout 2 ping 10.0.3.103 || echo "no direct route"'

Phase 2: Deploy to Z2

Run Deployment Script

  • [ ] Deploy with dry-run first

    bash
    cd projects/brain/mail-poller-z2/
    ./deploy.sh --dry-run
  • [ ] Review dry-run output for any issues

  • [ ] Run actual deployment

    bash
    ./deploy.sh

Verify Deployment

  • [ ] Check mail-poller.py exists on ct102

    bash
    ssh z2 'pct exec 102 -- ls -l /opt/hinata/mail-poller/mail-poller.py'
  • [ ] Check directory structure

    bash
    ssh z2 'pct exec 102 -- tree /opt/hinata/mail-poller/ 2>/dev/null || find /opt/hinata/mail-poller -type f'
  • [ ] Check systemd timer is enabled and active

    bash
    ssh z2 'pct exec 102 -- systemctl status hinata-mail-poller.timer'
  • [ ] Check service unit is present

    bash
    ssh z2 'pct exec 102 -- systemctl status hinata-mail-poller.service'

First Test Run

  • [ ] Run mail-poller manually (verbose)

    bash
    ssh z2 'pct exec 102 -- /opt/hinata/mail-poller/mail-poller.py --verbose'
  • [ ] Check output — should show messages fetched or "No new messages"

  • [ ] Verify state.json was created

    bash
    ssh z2 'pct exec 102 -- cat /opt/hinata/mail-poller/state.json | python3 -m json.tool'
  • [ ] Verify emails were archived

    bash
    ssh z2 'pct exec 102 -- find /opt/hinata/mail-poller/archive -name "*.json" | head -5'

Troubleshooting First Run

If first run fails, check:

  • [ ] Credentials are accessible

    bash
    ssh z2 'pct exec 102 -- ls -l /opt/itachi/credentials/'
  • [ ] Python 3.11 available

    bash
    ssh z2 'pct exec 102 -- python3 --version'
  • [ ] Check service logs

    bash
    ssh z2 'pct exec 102 -- journalctl -u hinata-mail-poller.service -n 100'
  • [ ] Check if IMAP/Graph API is reachable

    bash
    ssh z2 'pct exec 102 -- timeout 3 nc -zv imap.gmail.com 993'
    ssh z2 'pct exec 102 -- timeout 3 nc -zv graph.microsoft.com 443'

Phase 3: Cutover (Mac → Z2)

Pre-Cutover Testing

  • [ ] Run Z2 poller for 24–48 hours, monitor results

    bash
    ssh z2 'pct exec 102 -- journalctl -u hinata-mail-poller.service --since "24 hours ago"'
  • [ ] Compare archive counts: Mac vs Z2

    bash
    # Mac:
    find ~/Sandpit/hinata/resources/email-poller -name "*.json" | wc -l
    
    # Z2:
    ssh z2 'pct exec 102 -- find /opt/hinata/mail-poller/archive -name "*.json" | wc -l'
  • [ ] Verify no duplicate messages in Z2 archive

  • [ ] Check state.json cursors — should be advancing with each poll

Disable Mac Poller

  • [ ] Disable Mac LaunchAgent

    bash
    launchctl unload ~/Library/LaunchAgents/com.hinata.mail-poller.plist
  • [ ] Verify it's disabled

    bash
    launchctl list | grep mail-poller || echo "OK (not listed)"
  • [ ] Keep mail-poller.py on Mac as backup (do NOT delete)

Final Verification

  • [ ] Z2 poller runs on schedule (every 15 min)

    bash
    ssh z2 'pct exec 102 -- systemctl list-timers hinata-mail-poller.timer'
  • [ ] Check logs for last 6 hours

    bash
    ssh z2 'pct exec 102 -- journalctl -u hinata-mail-poller.service --since "6 hours ago"'
  • [ ] Count new emails since cutover (should be non-zero)

    bash
    ssh z2 'pct exec 102 -- find /opt/hinata/mail-poller/archive -newermt "6 hours ago" -name "*.json" | wc -l'
  • [ ] Run smoketest: 7-day stability check

    • [ ] Zero errors in logs
    • [ ] Archive grows consistently (expect ~5–50 new emails per day)
    • [ ] State.json cursors advance
    • [ ] No duplicate message hashes

Update Cron (if using Pi orchestration)

If Pi cron was triggering Mac poller, update to Z2:

  • [ ] Update Pi cron to point to Z2

    bash
    # On Pi:
    # Old: 0 6,12,20,21 * * * /usr/bin/python3 ~/mail-poller.py
    # New: 0 6,12,20,21 * * * ssh hinata-poller@ct102-ip "/opt/hinata/mail-poller/mail-poller.py"
  • [ ] Verify Pi can SSH to ct102 (Tailscale or direct network)


Troubleshooting Reference

Issue: "Credential file not found"

Check:

bash
ssh z2 'pct exec 102 -- ls -l /opt/itachi/credentials/'
ssh z2 'pct exec 103 -- ls -l /opt/itachi/credentials/'

Fix:

  • If ct103 has them, copy via deploy.sh: ./deploy.sh
  • If ct103 doesn't have them, copy manually from Mac via SCP

Issue: "IMAP connection failed"

Check:

bash
ssh z2 'pct exec 102 -- python3 << 'EOF'
import imaplib
conn = imaplib.IMAP4_SSL("imap.gmail.com", 993)
conn.login("michael.asolo1@gmail.com", "app-password-here")
print("OK")
EOF

Fix:

Issue: "Graph API 401 Unauthorized"

Check:

bash
ssh z2 'pct exec 102 -- cat /opt/itachi/credentials/outlook-tokens-hotmail-michael-asolo.json'

Fix:

  • Token may be expired. Mail-poller should refresh it automatically.
  • If refresh fails, regenerate tokens on Mac using Azure CLI: az account get-access-token
  • Copy new tokens to ct103 via SCP

Issue: "Timer not running"

Check:

bash
ssh z2 'pct exec 102 -- systemctl status hinata-mail-poller.timer'
ssh z2 'pct exec 102 -- systemctl list-timers'

Fix:

bash
ssh z2 'pct exec 102 -- systemctl start hinata-mail-poller.timer'
ssh z2 'pct exec 102 -- systemctl enable hinata-mail-poller.timer'

Success Criteria

Mail poller migration is COMPLETE when:

  • [ ] Z2 ct102 poller runs without errors for 7 consecutive days
  • [ ] Archive grows at expected rate (all 4 accounts polling successfully)
  • [ ] State cursors advance with each poll
  • [ ] Mac poller disabled
  • [ ] Zero duplicate messages in Z2 archive
  • [ ] Logs clear of warnings/errors (except expected rate limits)
  • [ ] Systemd timer runs on schedule (every 15 min)
  • [ ] All 4 accounts successfully polled in each run