Appearance
Z2 ↔ Sandpit Sync & Migration Strategy
Owner: Jimmy Neutron (Captain) + Trunks (architecture)
Date: 2026-06-04
Status: Executable plan — ready for Phase 1 kickoff
Executive Summary
Goal: Move mail-poller + Heim classifier from Mac (local) → Z2 (networked), with vault + data split cleanly.
Architecture:
┌──────────────────────────────────┐
│ iCloud (Obsidian vault) │
│ hinata-v2 (canonical) │
└──────────────┬───────────────────┘
│ (git clone/pull)
▼
┌──────────────────────────────────┐
│ Z2 (Proxmox VE 9.2) │
│ ├─ hinata-v2/ (vault clone) │
│ ├─ Container: hinata-poller │
│ └─ Container: hinata-ml (Heim) │
└──────────────┬───────────────────┘
│ (NFS mount)
▼
┌──────────────────────────────────┐
│ Sandpit (~/hinata/resources) │
│ ├─ email-poller/ (47K emails) │
│ ├─ data/mail/ (imap state) │
│ └─ (cold + backup storage) │
└──────────────────────────────────┘Key principles:
- Vault source: iCloud (Obsidian) → authoritative
- Vault on Z2: Working clone (git pull daily)
- Data source: Sandpit (NFS mount, read-write for poller, read-only for classifier)
- Infrastructure code: hinata-z2 GitHub repo (docker/, scripts/ only — NOT vault, NOT data)
Phase 1 — Prepare (NOW → 1 week)
1.1 Credential Audit + Sandpit Export
[ ] Confirm mail_imap_credential.json has ALL 4 accounts:
- michael.asolo1@gmail.com
- n.nnamah@outlook.com
- michael.nnamah@outlook.com
- michael.asolo@hotmail.co.uk
- Currently MISSING: Outlook accounts (only Gmail present)
- Action: Find where Outlook creds are stored; consolidate to single JSON or document source
[ ] Export imap-state-*.json from Mac → project folder:
z2-sandpit-sync/state-export-2026-06-04.json- Validate: last UID + modseq per account match current server state
- Test: dry-run mail-poller against state → should fetch 0 new emails
[ ] Generate archive manifest (size, file count, date range):
- Total: ~47K emails
- Per account breakdown
- Oldest—newest dates
- Corrupted files detected (if any)
1.2 Z2 GitHub Repo Restructure
Old structure (contaminated): vault copy + Sandpit data duplication + credentials in git
New structure (clean):
hinata-z2/ (GitHub)
├── docker/
│ ├── compose.yml # LXC service definitions
│ └── Dockerfile.mail-poller # mail-poller container image
├── scripts/
│ ├── proxmox-install.sh # BIOS → Proxmox VE 9.2 flow
│ ├── mount-sandpit-nfs.sh # NFS mount setup
│ ├── mail-poller.py # (copy from vault, updated for Z2 paths)
│ └── email-classifier.py # Heim layers 1–4 (stub, Phase 3 build)
├── docs/
│ └── z2-setup-guide.md # Step-by-step for Michael
├── .gitignore # STRICT: no data/, resources/, creds
└── README.md # Infrastructure pointer
NEVER in repo:
- Sandpit data (email archives, embeddings, models)
- Credentials, tokens, settings
- Vault copy (federation/, supreme-court/, the-government/)Action:
- [ ] Delete old hinata-z2 GitHub repo
- [ ] Create fresh repo with clean structure (ready locally:
/tmp/hinata-z2-clean/) - [ ] Push to GitHub
1.3 Sandpit NFS Export Configuration (Mac)
Mail-poller + Heim will read from Sandpit via NFS. Requires:
- [ ] Confirm Mac has
nfsdenabled (check System Settings → Sharing → File Sharing) - [ ] Add NFS export to
/etc/exports:(allows Z2 subnet to mount)/Users/nnamdi/Sandpit/hinata/resources -alldirs -mapall=nnamdi:staff 192.168.1.0/24 - [ ] Restart nfsd:
sudo nfsd restart - [ ] Test from Z2 (ping from Proxmox):
showmount -e [mac-ip]
Phase 2 — Install & Sync (Z2 hardware online → 2 weeks)
2.1 Z2 Hardware Pre-flight
- [ ] Verify RAM: expected 16GB (factory sticker says 8GB — CRITICAL MISMATCH)
- [ ] BIOS update + VT-x/VT-d enabled
- [ ] MAC address noted for static DHCP
2.2 Proxmox VE Install
- [ ] Boot Z2 from USB (ISO at
~/Sandpit/hinata/installers/proxmox-ve_9.2-1.iso) - [ ] Install to NVMe, static IP
.50, Tailscale join - [ ] Verify: Web UI at
https://jimmy-z2.${TAILNET}.ts.net:8006
2.3 Itachi Credentials Sync (special case — plaintext only)
ONE-TIME: Sync ONLY credential .json files from Itachi to Z2
bash
# On Mac: sync ONLY *.json credential files (NOT docs/doctrine)
rsync -av --include='*.json' --exclude='*.html' --exclude='*.md' \
/Users/nnamdi/hinata-v2/federation/ \
hinata-poller@hinata-poller.ts.net:/etc/hinata/credentials/- [ ] Credential .json files synced to Z2 at
/etc/hinata/credentials/(0600 perms) - [ ] TEMPORARY: Plaintext credentials on Z2 until Vaultwarden (840058) live
- [ ] NOT synced: Doctrine files (no-rotate-credentials.html, itachi-credential-storage.html)
- [ ] Once Vaultwarden deployed: credentials rotated → Vaultwarden vault, plaintext .json files deleted from Z2
- [ ] Mail-poller reads from
/etc/hinata/credentials/mail_imap_credential.json(and other creds as needed)
2.4 LXC Container Setup
hinata-poller (mail-poller service):
- [ ] Create Debian 12 LXC
- [ ] Install Python 3.11, openssh-server, git
- [ ] Copy mail-poller.py →
/opt/hinata/mail-poller.py - [ ] Mount Sandpit NFS at
/mnt/archive(read-write) - [ ] Copy credentials to
/etc/hinata/mail_imap_cred.json(0600 perms) - [ ] Test:
python3 mail-poller.py --account michael.asolo1@gmail.com --dry-run- Should fetch 0 new (state matches server)
- [ ] First live run: archive new emails to
/mnt/archive/email-poller/2026-06/ - [ ] Verify no duplicates
hinata-ml (Heim classifier, scaffolding):
- [ ] Create Debian 12 LXC
- [ ] Install Python 3.11 + pip3 (PyTorch, BGE, BERTopic, spaCy, FastAPI)
- [ ] Mount Sandpit NFS at
/mnt/archive(read-only) - [ ] Create
/data/mllocal storage (models, FAISS index) - [ ] Start FastAPI mock at 0.0.0.0:8000 →
200 OK(placeholder for Phase 3)
2.5 State Sync from Mac
- [ ] On Z2 poller:
mount /mnt/archive(verify Sandpit visible) - [ ] SCP imap-state-*.json from Mac
~/Sandpit/hinata/data/mail/→ Z2/mnt/archive/data/mail/ - [ ] Run:
mail-poller.py --account michael.asolo1@gmail.com --dry-run- Should detect 0 new emails (UIDvalidity + modseq match)
- [ ] If matched: state sync successful
2.6 Cron Integration (Pi → Z2)
Remove Mac cron:
bash
launchctl unload ~/Library/LaunchAgents/com.hinata.mail-poller.plistAdd Pi cron (calls Z2):
bash
# Pi crontab
0 6,12,20,21 * * * ssh hinata-poller@hinata-poller.ts.net "/opt/hinata/mail-poller.py" >> /var/log/hinata-poller.log 2>&1Verify:
- [ ] Cron executes successfully
- [ ] New emails appear in
/mnt/archive/email-poller/2026-06/ - [ ] 7-day smoke test: no missed emails, no duplicates
2.7 GitHub Repo Alignment
- [ ] Z2 repo (GitHub) reflects the clean infrastructure structure
- [ ]
docker-compose.ymlin repo matches running containers - [ ] All scripts are version-controlled (but config/paths are environment-specific)
- [ ]
.gitignoreis airtight (no data leaks)
Phase 3 — Z2 HDD Installation + Data Replication
When: After Phase 2 smoke test passes (Z2 poller stable, Heim scaffolding live)
Deliverables:
- [ ] Order + install HDD on Z2 (recommend: 4TB SSD for durability, or 8TB HDD for capacity)
- [ ] Format + mount at
/mnt/data(primary storage, not Sandpit NFS) - [ ] Replicate Sandpit data to Z2 HDD:bash
rsync -av --delete /mnt/archive/ /mnt/data/ # email-poller/, data/mail/, resources/ → Z2 HDD # One-time sync; ~30 minutes (4.2GB) - [ ] Verify: File count + checksums match between Sandpit + Z2 HDD
- [ ] Update containers to read from
/mnt/data(Z2 HDD) instead of/mnt/archive(Sandpit NFS) - [ ] Test 7 days: poller + classifier running against Z2 HDD (not NFS)
- [ ] Delete vault clone from Z2:bash
rm -rf /home/hinata/vault # Vault lives on iCloud/GitHub; Z2 no longer needs a local copy - [ ] Update Z2 cron: daily
git pullremoved (vault no longer synced locally)
New architecture (post-HDD):
iCloud (vault source)
↓ (no longer synced to Z2)
Z2 (containers only, no vault clone)
├─ /home/hinata/z2-infra/ (docker/, scripts/, config/)
└─ /mnt/data/ (email-poller/, data/mail/, models, embeddings)
Sandpit (backup-only)
└─ Mirrors /mnt/data/ (rsync backup, not primary)Success criteria:
- [ ] Z2 HDD populated + verified (file count, checksums)
- [ ] Containers reading from Z2 HDD (not NFS)
- [ ] 7-day stability test passed
- [ ] Vault clone deleted from Z2
- [ ] Sandpit demoted to backup-only status
Phase 4 — Heim Classifier (Z2 ML pipeline → 2 weeks)
3.1 Layer 1 — Structured Classifier
- [ ] Build email-classifier.py Layer 1 (deterministic rules)
- [ ] Test on existing corpus: >90% coverage
- [ ] Output: JSON with
3.2 Layer 2 — BERTopic + Unstructured
- [ ] Fit BERTopic on full 47K email corpus (one-time, overnight)
- [ ] Topic → commander domain mapping (Meruem reviews)
- [ ] Train LogReg classifier (embedding + topic_id → category)
- [ ] Test on held-out set: >80% accuracy
3.3 Layer 3 — Recommendation Engine
- [ ] Similarity scorer: email embedding similarity to acted-on emails
- [ ] Persist as FAISS index + pickle on Z2
/data/ml/ - [ ] Priority scoring: 1–5 per email
3.4 Layer 4 — FastAPI Endpoints (live)
- [ ]
GET /api/email/feed— latest classified emails - [ ]
GET /api/email/leads— career + side-hustle inbound signals - [ ]
GET /api/email/missed-opportunities— cold leads >14d - [ ]
POST /api/email/{message_id}/action— log feedback
3.5 Backfill (optional)
- [ ] Process all 47K existing emails through Layers 1–4
- [ ] Store classifications to
/mnt/archive/resources/ml/classifications/
Risk Register
| Risk | Mitigation |
|---|---|
| NFS mount latency slows classification | Monitor via /proc/self/mountstats; alert if >100ms |
| Sandpit offline → Z2 poller stalls | Implement circuit breaker; fall back to local queue |
| State cursor desync (Mac vs Z2) | Dry-run all fetches before cutover; validate UIDs against server |
| Credential plaintext on Z2 until Vaultwarden (CT103, 192.168.1.250) ready | 0600 file perms, isolated LXC, Tailscale-only SSH |
| Z2 hardware RAM is 8GB (not 16GB) | Reduce container limits, defer GPU, or upgrade RAM kit (~£35) |
Success Criteria
Phase 1 complete:
- [ ] All 4 IMAP credentials located + validated
- [ ] State export matches server state
- [ ] Archive manifest generated
- [ ] Z2 GitHub repo clean + pushable
Phase 2 complete:
- [ ] Mail-poller runs on Z2, fetches correctly via NFS
- [ ] 7-day smoke test: no errors, no missing emails
- [ ] Pi cron → Z2 working
- [ ] Mac mail-poller disabled
- [ ] Heim scaffolding live (FastAPI 200 OK)
Phase 3 complete (HDD + migration):
- [ ] Z2 HDD installed + formatted at
/mnt/data - [ ] Sandpit data replicated to Z2 HDD (verified)
- [ ] Containers switched to read from
/mnt/data(not NFS) - [ ] 7-day stability test on Z2 HDD passed
- [ ] Vault clone deleted from Z2:
rm -rf /home/hinata/vault - [ ] Sandpit demoted to backup-only (rsync pull from Z2 weekly)
Phase 4 complete:
- [ ] Heim classifier live (Layers 1–4)
- [ ] FastAPI endpoints tested
- [ ] Optional: 47K emails backfilled + classified
Open Questions for Michael
- Outlook credentials: Where are the 3 Outlook accounts stored? (currently only Gmail in mail_imap_credential.json)
- RAM contingency: Z2 shows 8GB on sticker, listing claimed 16GB. Proceed with 8GB or upgrade?
- Backfill: Classify all 47K existing emails in Phase 3, or defer?
- Mail.Send: Investigate send capability (Phase 5) in parallel with Phase 1–3, or defer?