@mulmobridge/email
v1.1.2
Published
Email bridge for MulmoBridge — checks an IMAP mailbox and replies via SMTP
Readme
@mulmobridge/email
Experimental — please test and report issues.
Email bridge for MulmoClaude. Polls an IMAP mailbox for unread mail and replies via SMTP preserving threading (In-Reply-To + References). Outbound-only — no public URL needed.
Good for async / slow workflows where you don't need instant replies: delegate tasks by email, get structured results back in your inbox.
Setup
1. Get a mailbox
Any IMAP+SMTP-capable mailbox works. A dedicated mailbox is strongly recommended — the bridge marks messages as \Seen after processing.
- Gmail — create an app password (requires 2FA). Use
imap.gmail.com:993/smtp.gmail.com:465(TLS) or587(STARTTLS). - Fastmail / iCloud / Outlook — similarly, prefer app passwords.
- Self-hosted (Postfix + Dovecot, mailcow, Stalwart) — use your regular credentials.
2. Run the bridge
EMAIL_IMAP_HOST=imap.gmail.com \
[email protected] \
EMAIL_IMAP_PASSWORD=your-app-password \
EMAIL_SMTP_HOST=smtp.gmail.com \
[email protected] \
EMAIL_SMTP_PASSWORD=your-app-password \
[email protected] \
npx @mulmobridge/emailSend an email to the bot address — you'll get a reply threaded under your original message.
Environment variables
| Variable | Required | Default | Description |
|----------------------------|----------|---------|-------------|
| EMAIL_IMAP_HOST | yes | — | IMAP server hostname |
| EMAIL_IMAP_USER | yes | — | IMAP username |
| EMAIL_IMAP_PASSWORD | yes | — | IMAP password (use an app password for Gmail / Fastmail) |
| EMAIL_IMAP_PORT | no | 993 | IMAP port |
| EMAIL_IMAP_TLS | no | true | Use implicit TLS (false for plain + STARTTLS) |
| EMAIL_SMTP_HOST | yes | — | SMTP server hostname |
| EMAIL_SMTP_USER | yes | — | SMTP username |
| EMAIL_SMTP_PASSWORD | yes | — | SMTP password |
| EMAIL_SMTP_PORT | no | 587 | SMTP port |
| EMAIL_SMTP_TLS | no | auto | true for implicit TLS (465), false for STARTTLS (587). Auto-detected from port if unset |
| EMAIL_FROM | yes | — | Bot's email address (usually equal to EMAIL_SMTP_USER) |
| EMAIL_ALLOWED_SENDERS | no | (all) | CSV of allowed sender addresses (case-insensitive) |
| EMAIL_POLL_INTERVAL_SEC | no | 30 | Poll interval in seconds (min 10) |
| MULMOCLAUDE_AUTH_TOKEN | no | auto | MulmoClaude bearer token override |
| MULMOCLAUDE_API_URL | no | auto (.server-port; waits if nothing is published) | MulmoClaude server URL |
Auth token persistence across server restarts
The MulmoClaude server regenerates a fresh bearer token on every startup and writes it to <workspace>/.session-token ($MULMOCLAUDE_WORKSPACE_PATH, or ~/mulmoclaude when unset), alongside the port it bound in .server-port.
The bridge follows a restart on its own. When the connection fails it re-reads both files, and if the server came back as a different generation — new token, new port, or both — it rebuilds its socket against it (#3078). You do not have to restart the bridge.
Pinning the token is still useful when the bridge runs on a different machine from the server, where it cannot read the workspace at all: set MULMOCLAUDE_AUTH_TOKEN to the same long random value on both sides. The server then uses it verbatim instead of regenerating.
# Server (one-time setup — same value across restarts)
MULMOCLAUDE_AUTH_TOKEN=long-random-string yarn dev
# Bridge (separate process / machine — same value)
MULMOCLAUDE_AUTH_TOKEN=long-random-string \
<bridge-specific-envs> \
npx <this-package>@latestRecommended: at least 32 characters of random data (the server logs a warning at startup for shorter values).
How it works
- At startup, the bridge logs into IMAP via
imapflowand enters a poll loop onINBOX. - Every
EMAIL_POLL_INTERVAL_SEC,SEARCH UNSEENreturns new messages. Each is fetched, parsed withmailparser, and (after allowlist check) forwarded to MulmoClaude keyed by the sender's address. - The reply goes out via
nodemailerwithIn-Reply-To= originalMessage-IDandReferences= full thread chain, so mail clients render it as a proper thread reply. - Processed messages are flagged
\Seenso they aren't re-processed on restart.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| NO Invalid credentials | Using raw password on Gmail / Fastmail | Create an app password with IMAP + SMTP scopes |
| Replies end up in recipient's Spam | Lack of SPF / DKIM on the bot domain | Send from a provider that already signs (Gmail, Fastmail, etc.) or set up DKIM on your own server |
| Reply not threaded in the sender's client | Client ignored References | Some corporate clients strip threading headers — not fixable bridge-side |
| Mail loop (bridge replies to its own replies) | The bridge's own From address is in EMAIL_ALLOWED_SENDERS | Remove it, or use a separate address for the bot |
Security notes
- Use a dedicated bot mailbox. Revocation of the app password then won't affect your personal mail.
- The IMAP password is read-write — the bridge can delete mail if compromised. Store it via a secret manager, not plain env.
- Allowlist via
EMAIL_ALLOWED_SENDERSis strongly recommended. Without it, anyone who emails the bot can converse with your MulmoClaude, and spam is a real risk. - Replies include the bot's response text in plain email. Don't bridge sensitive content unless both mailboxes are under your control.
- Attachments are not processed in v0.1.0 — image / PDF forwarding may land in a follow-up.
Ecosystem
Part of the @mulmobridge/* package family.
Shared libraries:
@mulmobridge/client— socket.io client library used by every bridge below@mulmobridge/protocol— wire types and constants@mulmobridge/chat-service— server-side relay + session store@mulmobridge/relay— Cloudflare Workers webhook proxy@mulmobridge/mock-server— mock server for local bridge development
Bridges (one npm package per platform):
@mulmobridge/bluesky— Bluesky DMs over atproto@mulmobridge/chatwork— Chatwork (Japanese business chat)@mulmobridge/cli— interactive terminal bridge@mulmobridge/discord— Discord bot via Gateway@mulmobridge/email— IMAP poll + SMTP reply, threading preserved ← this package@mulmobridge/google-chat— Google Chat via MulmoBridge relay@mulmobridge/irc— IRC (Libera, Freenode, custom)@mulmobridge/line— LINE Messaging API via MulmoBridge relay@mulmobridge/line-works— LINE Works (enterprise LINE)@mulmobridge/mastodon— Mastodon DMs + mentions@mulmobridge/matrix— Matrix / Element@mulmobridge/mattermost— Mattermost@mulmobridge/messenger— Facebook Messenger via MulmoBridge relay@mulmobridge/nostr— Nostr NIP-04 encrypted DMs@mulmobridge/rocketchat— Rocket.Chat@mulmobridge/signal— Signal via signal-cli-rest-api@mulmobridge/slack— Slack Socket Mode@mulmobridge/teams— Microsoft Teams via Bot Framework@mulmobridge/telegram— Telegram bot@mulmobridge/twilio-sms— SMS via Twilio Programmable Messaging@mulmobridge/viber— Viber Public Account bots@mulmobridge/webhook— generic HTTP webhook bridge@mulmobridge/whatsapp— WhatsApp Cloud API via MulmoBridge relay@mulmobridge/xmpp— XMPP / Jabber@mulmobridge/zulip— Zulip
Related projects
Published from the MulmoClaude monorepo by Receptron.
- MulmoClaude — an open-source AI assistant platform that runs on your own computer. Claude Code as the engine, a personal wiki for long-term memory, schema-driven collections for your data, and chat that summons the right GUI (markdown, charts, forms, spreadsheets, wikis) for each task.
- MulmoTerminal — a terminal-first cockpit for running many AI coding agents in parallel. One roster showing every session's summary and PR status, tmux-backed session persistence, git-worktree isolation, one-click PRs, and mobile push with remote reply.
- MulmoTerminal manual — setup, workflows, feature reference, configuration, mobile notifications, and alternative / local model providers. Available in English and Japanese.
