Guide

Give Hermes Its Own Email Address

Hermes reaches you on Telegram, Discord, and Slack, but it has no email of its own. The instinct is to hand it your Gmail — don't. This guide gives the self-hosted Hermes agent a mailbox it owns: a managed email address, split credentials so the agent never holds a platform key, and rule-based guardrails that run before the model reads a message.

Written by Qasim Muhammad Staff SRE

VerifiedCLI 3.1.28 · Nylas managed · last tested July 9, 2026

What Hermes is missing

Hermes is the open-source, self-hosted agent from Nous Research, released in February 2026 under an MIT license. You run it on your own machine, connect your messaging accounts, and it becomes a persistent assistant — it remembers your projects across sessions, writes its own skills, and reaches you across six channels: Telegram, Discord, Slack, Signal, WhatsApp, and a local CLI. Everything runs on your own hardware, and it swaps model providers with no code change.

Hermes does ship an email connector, but it treats email the way it treats Telegram or Discord: a channel for reaching you. That is different from email as an identity the agent acts from. Signing up for a service, confirming a booking, replying to a customer, sending a receipt: those need the agent to be a sender with its own address, not a notifier routed through yours. So people wire Hermes into a sending inbox the fast way: paste a personal Gmail OAuth token into the config and let the agent read and send from the human inbox. That is the setup worth avoiding, and the rest of this guide is the alternative.

Why not just give it your inbox?

Hermes reads untrusted content by design. Its whole value is ingesting messages, remembering them, and acting on them. The moment it can also send from your real address, a single message with hidden instructions — white-on-white text, an HTML comment, a poisoned calendar invite — can turn the agent into a sender that acts as you. There is no layer between the model and your outbox to catch it. This is Simon Willison's lethal trifecta: an agent that reads untrusted content, holds private data, and can send externally will eventually be steered to exfiltrate. Handing it your inbox gives it all three legs at once, and a persistent agent that improves its own skills makes the blast radius larger over time, not smaller.

Three concrete problems come from borrowing a human inbox. Prompt injection reaches your real contacts and history. Every reply the agent triggers lands in your personal inbox and buries your own mail. And the agent holds a credential that can touch everything the account can touch. Giving Hermes its own address fixes all three at once — it gets a real, first-class identity, and a bad instruction can only ever escalate inside a box that holds nothing of yours.

What you are building

The target setup is a managed agent mailbox that Hermes owns — a real hermes@youragent.nylas.email address that sends and receives on its own, with no OAuth handshake and no human account behind it. You drive it from the terminal, which means Hermes can drive it too: the same commands you run by hand become a skill the agent calls. The mailbox sits behind a policy and a set of rules that you control with a key the agent never sees.

This is the split that makes the pattern safe rather than just tidy. It comes down to two credentials: you keep the one platform key and use it to set the guardrails, and Hermes gets one mailbox login and nothing else. The diagram below shows who holds what.

Split credentials: you hold the API key and set policy; Hermes holds only an app password for its own mailboxYou (the operator)holds the API keyPolicy + rulessend caps, blocksAgent mailboxhermes@youragent.nylas.emailHermesapp password onlyconfigureslogs in togoverns

Set up the CLI once

You need the Nylas CLI and an account with a domain that can host agent mailboxes. A single command handles all of it: it signs you up, authenticates the machine, and provisions a free *.nylas.email subdomain you can create agent addresses under. Install the tool, then run init.

# macOS / Linux / WSL
brew install nylas/nylas-cli/nylas
# or: curl -fsSL https://cli.nylas.com/install.sh | bash

# sign up, authenticate, and get a free agent domain
nylas init

# confirm the managed-email connector is ready
nylas agent status

The free tier is enough to build and test the whole flow: three agent accounts, unlimited inbound mail, and 200 sends a day. Move to a custom domain later when you want the agent sending from your own brand rather than a subdomain.

Create the mailbox Hermes owns

Provision the address with one command. Pass a display name so recipients see a real sender rather than a bare address, and capture the grant ID it prints — that ID is how every later command refers to this specific mailbox.

nylas agent account create hermes@youragent.nylas.email \
  --name "Hermes" \
  --json

There was no OAuth screen, no refresh token, and no human mailbox involved. That single API call returns in under two seconds, and the address is live immediately. Send a test message to it from any client, then list the inbox to confirm it arrived:

nylas email list hermes@youragent.nylas.email --limit 5 --json

Split the credentials so Hermes holds nothing powerful

This is the part that turns a tidy setup into a safe one. Your API key can create accounts, change policy, and reach every mailbox on the application. Hermes should never see it. Instead, give the agent an app password that is scoped to its one mailbox and works over plain IMAP and SMTP. Add it to the account you just created:

nylas agent account update hermes@youragent.nylas.email \
  --app-password "SetAStrongPassphrase2026!"

Now the responsibilities are cleanly separated. You hold the API key and use it to set the rules and limits on the box. Hermes holds only the app password and logs in to send and receive. If that password leaks, the worst case is one inbox — it cannot touch policies, other mailboxes, or the application. Rotate or revoke it and nothing else is disturbed. It is the same instinct as giving a service its own credential instead of sharing yours, with the guardrails living on the key you keep.

Add guardrails before you turn autonomy on

Hermes is persistent and acts on its own, so the guardrails matter more here than for a one-shot script. Rules are deterministic condition-action pairs that run at the mailbox layer, outside the agent's decision loop — they fire before the model reads an inbound message and before an outbound send leaves. A prompt injection cannot talk its way past them.

Start with two. An inbound rule that drops mail from a known-bad domain before Hermes ever classifies it:

nylas agent rule create \
  --name "Block junk sender" \
  --condition from.domain,is,spam-sender.com \
  --action block

And the single most important rule for any autonomous sender — an outbound allowlist. This blocks Hermes from emailing anyone outside the domains you trust, which shuts down the classic exfiltration path where injected text tells the agent to mail your data somewhere. The block action is terminal, and an outbound block is rejected before the message reaches a provider:

nylas agent rule create \
  --name "Outbound allowlist" \
  --trigger outbound \
  --match-operator any \
  --condition recipient.domain,is_not,yourcompany.com \
  --condition recipient.domain,is_not,trusted-partner.com \
  --action block

Bundle account-level limits into a policy so a runaway loop has a ceiling. The daily send cap is the one to set first — without it, an injected send loop has no upper bound. Create the policy, then confirm your rules are attached before the first production send.

nylas agent policy create --name "Hermes guardrails" --json

# confirm what is live on the box
nylas agent rule list
nylas agent policy list

Wire the mailbox into Hermes

Hermes builds skills from the tools you give it, so the cleanest integration is to expose the mailbox as a skill it can call. There are two ways to do that, and which one you pick depends on how your Hermes instance is set up.

If your gateway speaks the Model Context Protocol, register the CLI as an MCP server and the agent gets typed email, calendar, and contacts tools without shelling out. Point the agent at the grant for its own mailbox so it never operates on anything else:

# expose email/calendar/contacts as MCP tools
nylas mcp install --all
nylas mcp status

If you would rather keep it simple, wrap the raw commands as a shell skill. Hermes calls the same terminal commands you tested by hand — list unread, read one, reply — and you pass --json so the model parses structured output instead of scraping text.

# read
nylas email list hermes@youragent.nylas.email --unread --limit 10 --json
nylas email read <message-id> --json

# reply (the outbound allowlist still applies)
nylas email send hermes@youragent.nylas.email \
  --to "customer@yourcompany.com" \
  --subject "Re: your request" \
  --body "Thanks — here are the next steps." \
  --yes --json

React to replies in real time

Polling works for a batch job, but a persistent agent wants to react the moment a reply lands. Subscribe to a webhook and Hermes gets a push the instant mail arrives, which fits the always-on gateway model far better than a poll loop.

nylas webhook create \
  --url https://your-hermes-host.example/webhooks/nylas \
  --triggers "message.created,message.updated"

Each inbound event carries a thread ID. Because email conversations span hours or days, Hermes should fetch the thread before it drafts a reply — that is where its persistent memory earns its keep, matching a fresh message to the project and history it already holds. Store the thread ID against whatever internal task the conversation belongs to, so a reply three days later restores the right context instead of starting cold.

One gotcha bites persistent agents specifically: do not assume one reply per message. A recipient might answer twice, two people on a thread might both respond, and Hermes can receive its own sent copy back through the webhook. Track which messages you have already acted on by their message ID and skip anything you have seen, so the agent never fires a second reply into a thread it already answered. Filter out no-reply and mailer-daemon senders before the classify step too — an auto-reply loop is the fastest way for an always-on agent to burn its 200-a-day send cap on nothing.

What you end up with

Hermes now has email as a first-class channel alongside Telegram and Discord — an address it sends from, receives on, and owns. The agent holds a single scoped mailbox login and nothing else. Every inbound message passes through rules before the model sees it, every outbound send is checked against an allowlist before it leaves, and a daily cap (200 sends on the free tier) sits under the whole thing. Your personal inbox is untouched, and a leaked agent credential logs in to exactly one box.

Keep treating message bodies, attachments, and calendar descriptions as untrusted input — the model can classify and draft, but the surrounding code and the rules should decide the recipient and the final send. The mailbox is the containment boundary; the rules are the policy on it; the app password is all the agent ever gets. That separation is what lets you widen Hermes's autonomy later without widening the risk.

Troubleshooting

Three failures account for almost every setup issue with an agent mailbox. If a mail client or the agent's IMAP login is rejected, the app password is almost always the cause — it has to be set on the account, and it is write-only, so if you are unsure, set it again with nylas agent account update. If nylas agent rule list comes back empty, confirm the address is a managed agent account with nylas auth list and that your rules are attached to its workspace. If a send returns a block, that is an outbound rule working as intended — check the recipient against your allowlist before assuming it is a bug.

Next steps