Email as a durable channel for a personal AI agent — six small, dependency-light
Python tools extracted from a running deployment. They share one credential
contract (SMTP_USER / SMTP_PASS in a .env that never enters git) and one
egress trick (optional SOCKS5 when the mail host is blocked).
Part of the Starshard communication stack (starshard-communication is the protocol layer; this repo is the mail channel adapter set).
| tool | direction | what it does |
|---|---|---|
bin/zhizi-mail |
out | SMTP send with a durable local spool: if SMTP fails the message is written to ~/.agent-mail-stack/mail-spool/ (0600) and --flush-spool retries later. The body never leaves the machine on failure — no "fallback to the cloud". |
bin/zhizi-mail-attach |
out | same credentials, adds --attach FILE (one or more). |
bin/gmail-fetch-attach |
in | IMAP: download attachments of the newest mail matching --from / --subject into a directory. |
bin/fire-and-forget-deliver |
out (owner only) | wrapper for background sub-agents: they may email only OWNER_EMAIL; any other recipient is refused with a non-zero exit. Also emits a receipt skeleton. |
bin/email-task-intake |
in → agent | turns an inbound [Agent Task] email into a task-candidate memo in a Memory Hub (MCP) so an agent runner can pick it up; dedupes on message-id. |
bin/mail-signal-relay |
in → agent | pure-local relay: when a watched sender writes, drop a wakeup packet into a queue directory your agent runner consumes; tracks pending / handled / stale with status, reconcile, sweep. |
lib/hub_client.py |
— | 170-line MCP-over-HTTP client for a self-hosted Memory Hub (reference-impl); used by email-task-intake. |
- Spool, don't leak. The first version of
zhizi-mailfell back to writing the whole body into a shared memory service when SMTP failed. Bodies routinely carry invite links with tokens. The spool is the fix: failure keeps the body local, an alert (metadata only) goes wherever you want viaMAIL_RECEIPT_HOOK. - Owner-only outbound for background agents.
fire-and-forget-deliverhard-codes the policy "a sub-agent may never email a third party". External sends are a human decision. - Inbound is a queue, not a poller loop.
mail-signal-relay/email-task-intakeonly write packets; whatever runs your agent decides when to wake. Triggers are events (new mail), timers are fail-safes. - No framework. Standard library plus
httpx(Hub client) and optionalPySocks. Every tool is one file; read it in five minutes.
git clone https://github.com/starshard-ai/agent-mail-stack && cd agent-mail-stack
cp .env.example ~/.config/agent-mail/.env && chmod 600 ~/.config/agent-mail/.env # fill SMTP_USER / SMTP_PASS
echo "hello from my agent" | bin/zhizi-mail --to you@example.com --subject "test"
bin/zhizi-mail --flush-spool # retries anything queued during an outageFull steps, Gmail app-password setup, SOCKS egress and the inbound tools: INSTALL.md.