OpenMail plugin for Hermes Agent
Gives your Hermes agent an email address. Mail to it wakes the agent; the agent's answer goes out as the reply, in the same thread. Native tools let the agent send, read and provision inboxes on its own.
hermes plugins install openmailsh/hermes-plugin --enable
hermes openmail setup
hermes gateway run
Setup
- Create an API key at console.openmail.sh. Any scope.
- Run
hermes openmail setupand paste it. Pick an inbox, or let it create one. - Run
hermes gateway run, then email the agent.
Check the result any time with hermes openmail doctor.
Two lines in .env:
OPENMAIL_API_KEY=om_...
OPENMAIL_ALLOWED_USERS=alice@x.com,bob@y.io
The second is Hermes's sender gate; see Who may write. Setup asks: list addresses (default), let OpenMail's sender rules decide (writes OPENMAIL_ALLOW_ALL_USERS=true), or decide later. Skipped when a sender setting already exists.
Key scope. Any key works. An account key is swapped for a pod-scoped key over the chosen inbox's pod and never stored; the pod key can still create inboxes and cover the whole pod later (OPENMAIL_POD_ID), but can't reach other pods, webhooks or account-wide policy. Pod and inbox keys are stored as-is. With --inbox <id or address> the key is narrowed further, to that one inbox.
Which inbox. Without OPENMAIL_INBOX_ID or OPENMAIL_POD_ID, the adapter goes by what the key sees:
| Key sees | Adapter does |
|---|---|
| One inbox | Runs as it |
| No inbox | Creates one |
| Several inboxes, one pod | Runs the whole pod |
| Several inboxes, several pods | Stops; asks for OPENMAIL_INBOX_ID or OPENMAIL_POD_ID |
Scripts. --api-key <key>, --api-key-stdin, -y (no prompts, first inbox wins, sender gate left closed), --allow-all (open the sender gate), --inbox <id or address> (run as exactly this inbox, store an inbox-scoped key).
Who may write to the agent
Two gates. OpenMail's allow/block rules run first, server-side; manage them in the console or with openmail policy. Then Hermes checks its own list, as on every platform:
OPENMAIL_ALLOWED_USERS=alice@x.com,bob@y.io: only these reach the agent.OPENMAIL_ALLOW_ALL_USERS=true: OpenMail's sender rules alone decide.setupwrites this on--allow-allor when you choose it in the menu.
Hermes drops unknown senders silently; no pairing code goes out by email.
Modes
OPENMAIL_MODE, default channel.
| Mode | Inbound mail | Agent's answer |
|---|---|---|
channel |
Starts a turn | Sent as the email reply |
notify |
Starts a turn: "summarise, do not act" | Goes to your home channel (Telegram, Slack, Discord), never to the sender |
tool |
Ignored | None; tools only, on request |
Channel mode answers only mail a person could have sent. OpenMail classifies each message; automated, marketing and bounce reach the agent as a notification, spam and malicious not at all.
Per-inbox override, pod scope only, in ~/.hermes/config.yaml:
platforms:
openmail:
inboxes:
sales@omail.sh: { mode: channel }
alerts@omail.sh: { mode: notify }
archive@omail.sh: { mode: tool }
Tools
Toolset openmail. The API key never enters the model context.
| Tool | Does |
|---|---|
openmail_whoami |
Inboxes this agent can use, and its default |
openmail_send |
New thread: to, subject, body, optional cc, attachments (paths under ~/.hermes/media or ~/.hermes/output only) |
openmail_reply |
Reply in a thread; recipient and subject come from it |
openmail_list_threads |
Threads in an inbox, newest first |
openmail_read_thread |
Every message in a thread; marks it read |
openmail_list_messages |
Messages in an inbox |
openmail_attachment_text |
Text from PDF, DOCX, XLSX, images |
openmail_list_inboxes |
Inboxes visible to the key |
openmail_create_inbox |
New inbox (pod or account key) |
The bundled openmail skill covers administration (sender rules, pods, deleting inboxes, feedback) through the openmail CLI.
Subagents and Bots
-
Subagents (
delegate_task) run in the same process with the same key and tools. Give one its own address by creating an inbox (openmail_create_inbox) and passing thatinbox_idon send, reply and list. -
Bots are Hermes profiles with their own
.env. Give each its own inbox-scoped key:openmail inbox create --mailbox-name research --display-name "Research" # once, from your own machine hermes -p research plugins install openmail # plugins are per profile hermes -p research openmail setup --api-key-stdin --inbox research@<your-domain> -y < pod-key.txtSetup writes an inbox-scoped key to the profile's
.env; the pod key on stdin is not stored.
Attachments
OpenMail extracts text server-side; the adapter inlines it (8k chars per file, 24k total) and downloads binaries into Hermes's media cache so the agent can open them.
Reliability
Inbound rides a websocket: no public URL, no webhook. The last event_id is kept in ~/.hermes/openmail/, so a restart replays what it missed. The frame is only a hint; the adapter re-fetches every message from the API first, so a forged frame can't impersonate a sender.
Cron and notifications
--deliver openmail sends a job's output to OPENMAIL_HOME_ADDRESS; --deliver openmail:alice@x.com sends it to any address. Works with the gateway running or not.
Development
ln -s "$PWD" ~/.hermes/plugins/openmail && hermes plugins enable openmail
python -m venv .venv && .venv/bin/pip install pytest httpx websockets
HERMES_AGENT_DIR=~/.hermes/hermes-agent .venv/bin/pytest -c tests/pytest.ini --rootdir=tests tests
Needs Hermes with httpx and websockets; both ship with it. OPENMAIL_BASE_URL points the plugin at another API host.
Every OPENMAIL_* variable also works under platforms.openmail in config.yaml, lower-cased without the prefix (api_key, inbox_id). Env wins.
Related
- OpenMail docs
- OpenClaw plugin, same design for OpenClaw
- Agent skill, bundled here
