ALAI Mail Topology — Migadu Domains, Mailbox Inventory, John's 17-Account Ingest Loop (2026-06-08)
ALAI Mail Topology & John's Email Ingest Loop
Last updated: 2026-06-08 | MC: #103182 | Built by: FlowForge | Validated by: Proveo (Angie Jones) — PASS
1. Mail Infrastructure — Migadu (Single Account)
All ALAI product domains are hosted on one Migadu account. MX records for every domain point to the same two servers:
aspmx1.migadu.com(priority 10)aspmx2.migadu.com(priority 20)
Domains on this account: alai.no, bilko.io, bilko.cloud, bilko.company, snowit.ba, basicconsulting.no, basicfakta.no, lumiscare.com
Migadu Admin Access
| Item | Value / Location |
|---|---|
| Admin login | [email protected] |
| API key | Vaultwarden item "migadu keyy" (86-char token — do NOT print) |
| IMAP host | imap.migadu.com |
| SMTP host | smtp.migadu.com |
| Web UI | https://admin.migadu.com |
Migadu API Quirks (DO NOT FORGET)
- GET aliases — response key is
address_aliases, notaliases. - Create alias — must send JSON body
{"local_part": "...", "destinations": ["..."]}with headerAccept: application/json(omitting Accept = HTML response, silent fail). - Alias destinations MUST be same-domain. Cross-domain targets (e.g.
[email protected] → [email protected]) return HTTP 400. Route to a real mailbox on the same domain instead. - No catch-all rewrites — verified via
/rewritesendpoint (empty on all domains). Any email to a non-existent local-part that has no alias bounces. - App-passwords for new mailboxes are created via
PUT /v1/domains/{domain}/mailboxes/{local_part}and stored as Vaultwarden items (never in logs).
2. Real Mailbox Inventory
These are the real mailboxes that exist in Migadu (verified 2026-06-08 via admin API). Only real mailboxes can be used as alias destinations.
| Domain | Real mailboxes (local parts) |
|---|---|
alai.no | john, alem, dev, post, admin |
bilko.io | admin, sales, privacy |
bilko.cloud | admin, sales |
bilko.company | admin, sales |
snowit.ba | admin, info, asmir, enis |
basicconsulting.no | john, info |
Note: lumiscare.com and basicfakta.no are on this Migadu account but are not actively polled in John's loop. [email protected] is CareSafety-gated (see Section 6).
3. John's Email Ingest — All 17 Monitored Accounts
John's email ingest is managed by ~/system/tools/email-inbox.js and polled by ~/system/daemons/email-agent.js. As of MC #103182 (2026-06-08), 17 accounts are registered in email-inbox.db → email_accounts.
Original 6 Accounts (pre-MC #103182)
| Account name (DB key) | Email address | Vault item |
|---|---|---|
john | [email protected] | existing |
info | [email protected] | existing |
alai | [email protected] | existing |
dev | [email protected] | existing |
alem | [email protected] | existing |
gmail | [email protected] | existing |
11 New Product/Role Accounts (added MC #103182, 2026-06-08)
| Account name (DB key) | Email address | Vault item name |
|---|---|---|
post-alai | [email protected] | Migadu — [email protected] |
admin-alai | [email protected] | Migadu — [email protected] |
sales-bilko-io | [email protected] | Migadu — [email protected] |
privacy-bilko-io | [email protected] | Migadu — [email protected] |
admin-bilko-io | [email protected] | Migadu — [email protected] |
sales-bilko-cloud | [email protected] | Migadu — [email protected] |
admin-bilko-cloud | [email protected] | Migadu — [email protected] |
sales-bilko-company | [email protected] | Migadu — [email protected] |
admin-bilko-company | [email protected] | Migadu — [email protected] |
info-snowit | [email protected] | [email protected] IMAP |
admin-snowit | [email protected] | Migadu — [email protected] |
App-passwords for the 5 newly created admin@* mailboxes ([email protected], [email protected], [email protected], [email protected], [email protected]) were generated via the Migadu API and stored as Vaultwarden items. Vault IDs: 558181ec, 8dfe8d2d, 2f38a16a, 7d0f9216, 2fb07c20.
4. Alias Map — Dead-Address Fixes (2026-06-08)
The following addresses were previously advertised (on websites, legal pages, landing pages) but did not correspond to any real mailbox — all mail to them was silently bouncing. Migadu aliases were created to route them to the nearest real same-domain mailbox.
| Dead address (was bouncing) | Now routes to | Why |
|---|---|---|
[email protected] | [email protected] | alai.no contact form was sending to this dead address — all website contact submissions were lost |
[email protected] | [email protected] | bilko.io landing mailto link |
[email protected] | [email protected] | bilko.io Bosnian support address on legal/terms pages |
[email protected] | [email protected] | bilko.io legal/terms page |
[email protected] | [email protected] | bilko.io security disclosure address |
[email protected] | [email protected] | bilko.cloud landing mailto |
[email protected] | [email protected] | bilko.company landing mailto |
Pre-fix state: Only postmaster@{domain} → admin@{domain} aliases existed. No rewrites, no catch-all. All other non-existent local-parts bounced.
Post-fix: All advertised addresses now deliver to a real monitored mailbox. Nothing bounces.
5. Contact-Form Routing
| Product | Contact form path | Where mail ends up |
|---|---|---|
| alai.no website | Vercel serverless: ~/business/ALAI-Holding-AS/web/api/contact.js (nodemailer) |
Sends to [email protected] (which now aliases to [email protected] — monitored). Was dead before 2026-06-08 fix. |
| Bilko landing pages | Cloudflare Pages function: apps/landing-*/functions/api/lead.js |
Posts to Slack #ceo channel (C0AFJDP9V6U) + writes to Cloudflare KV (BILKO_LEADS). No email path — separate from IMAP polling. |
6. Boundary Accounts — NOT Polled (intentional)
| Address | Reason not polled |
|---|---|
[email protected] | Personal mailbox belonging to Asmir (SnowIT partner). He reads his own mail. |
[email protected] | Personal mailbox belonging to Enis. Same reason. |
[email protected] | CareSafety boundary — health/patient-adjacent service. NOT polled per explicit boundary decision. See CareSafety memo in MEMORY. |
7. Components Changed (MC #103182)
All 5 files were modified additively (no existing rows or accounts removed).
| File | What changed |
|---|---|
~/system/tools/email-inbox.js |
(a) Added 11 INSERT OR IGNORE INTO email_accounts rows (lines ~159–170).(b) Added DB schema migration (lines ~141–208): rebuilds emails table with extended CHECK(account IN (...)) constraint covering all 17 account names. Guard is idempotent — runs once only. Preserves all 4697+ existing rows and all 25 columns including late additions (delegated_to, delegated_at, deadline, body, triaged_at, auto_forwarded).
|
~/system/tools/mail-native.js |
Added 11 account-name → Vaultwarden item-name entries in VAULT_NAMES map (lines ~76–88). Existing 7 entries untouched. |
~/system/tools/email-imap-db-audit.js |
Expanded ACCOUNTS constant from 5 to 16 accounts (line 51). |
~/system/tools/email-action-hard-check.js |
Added ALL_MONITORED_ACCOUNTS constant with all 17 account names (lines ~14–22). Default accounts arg now uses it instead of hardcoded john,alai. |
~/system/daemons/email-agent.js |
Expanded fetch-loop accounts array from 6 to 17 (line ~1853) and mirrored expansion in last_checked_at update loop (line ~1880). |
Known Minor Issue (pre-existing, non-blocking)
After SMTP send via mail-native.js, the IMAP post-send copy to Sent folder times out with ETIMEOUT. Delivery succeeds (Message-ID is logged). This is a cosmetic issue in the IMAP cleanup code — pre-existing, unrelated to MC #103182. Separate MC recommended for fix.
8. Runbook — How to Add a New Mailbox to John's Loop
-
Verify the mailbox exists in Migadu.
curl -s -H "Authorization: Token <api-key>" "https://admin.migadu.com/v1/domains/{domain}/mailboxes"
If it does not exist, create it via the admin UI or API first. -
Create an app-password for the mailbox.
Use Migadu admin UI (Mailbox settings > App Passwords) orPUT /v1/domains/{domain}/mailboxes/{local_part}API.
Store the password as a new Vaultwarden item namedMigadu — {email}. -
Add the account to
mail-native.jsVAULT_NAMES map.
Key = your chosen account name (e.g.sales-newdomain), value = the Vaultwarden item name. -
Add the account seed to
email-inbox.js.
Append anINSERT OR IGNORE INTO email_accounts (name, email) VALUES ('<name>', '<email>')in the accounts-seed block.
Also extend theemailstable CHECK constraint migration to include the new account name. Add the name to thenewAccountsarray in the migration block. The guard condition must also be updated to include a unique string from the new account. -
Add the account to
email-imap-db-audit.jsACCOUNTS array. -
Add the account to
email-action-hard-check.jsALL_MONITORED_ACCOUNTS array. -
Add the account to
email-agent.jsfetch-loop array andlast_checked_atupdate loop (both must be mirrored). -
Run syntax checks.
node --check ~/system/tools/email-inbox.js && node --check ~/system/tools/mail-native.js && node --check ~/system/daemons/email-agent.js -
Test connectivity.
node ~/system/tools/mail-native.js test --account <name>— expect IMAP OK + SMTP OK. -
Restart the email-agent daemon (LaunchAgent:
com.john.email-agent) so the updated accounts array takes effect. -
Proveo ingest probe. Send a test email with subject token
INGEST-PROBE-<account>-<timestamp>to the new address. Trigger one daemon cycle. Confirm the row appears inemail_accountsandemailstables with the correct account name vianode ~/system/tools/email-inbox.js search "INGEST-PROBE". -
If it is a new alias (not a real mailbox) — create the alias first in Migadu. Remember: alias destination MUST be a real mailbox on the same domain. Use the Migadu API with
Accept: application/jsonheader and JSON body{"local_part": "...", "destinations": ["..."]}.
9. Validation Evidence (MC #103182)
| Check | Result |
|---|---|
| Code changes (5 files) verified by Proveo | PASS |
| DB registry — 17 rows in email_accounts | PASS |
| IMAP/SMTP connectivity — 11/11 new accounts | PASS |
| emails table CHECK migration (emails_new rebuild) | PASS — DDL confirmed, 4697 rows preserved |
| Ingest probes — 4/4 probe accounts persist to DB | PASS (round 2, after schema fix) |
| Regression — original 6 accounts | PASS — counts growing, timestamps advancing |
| No-loop / alias dedup (UNIQUE on message_id) | PASS — 0 duplicate message_ids |
| email-action-hard-check.js exit code | PASS — exit 0, all 17 accounts in scope |
Blocker found and fixed during validation: The emails table had a hardcoded CHECK(account IN ('john','info','alai','dev','alem','gmail')). This caused INSERT OR IGNORE to silently discard all rows from the 11 new accounts — 27 real emails were dropped before the fix was applied. The schema migration was added to email-inbox.js and re-validated. The 27 dropped emails remain in the IMAP mailboxes and can be recovered via a full-history re-fetch if needed.
Full evidence: /tmp/evidence-103182/flowforge-build.md + /tmp/evidence-103182/proveo-validation.md