arizuko

arizuko › components › emaid

emaid

emaid reads mail over IMAP, checks the sender, sends replies over SMTP, and trusts the DMARC result of one named mail server.

emaid connects email to arizuko

emaid connects email to arizuko, so the agent can read incoming mail and send replies in the same thread.

emaid is the email channel adapter. It reads IMAP for inbound messages and sends outbound messages with SMTP. It keeps its own table that maps email threads, by Message-ID and References, to their chat_jid, so replies stay in the conversation. For routd, it is a normal channel adapter with the JID prefix email:.

emaid checks each inbound message against the DMARC result in the Authentication-Results header of the upstream mail server before the agent sees it. Verified mail gets verb=message. All other mail gets verb=untrusted, so route rules can quarantine it.

Email reaches every customer and needs sender checks

Email is the inbound channel that everyone has: every customer, vendor and ticket system can reach an arizuko agent with an SMTP address. Without emaid, there is no IMAP loop, no thread table and no sender check, and the agent cannot receive email at all.

Mail without authentication is easy to forge. An attacker who forges a reply to the bot must not get verb=mention. For this reason, emaid splits verb=message from verb=untrusted, and the sender check happens at the channel boundary, before routing.

Each message passes five steps

  1. emaid reads the IMAP INBOX for messages newer than the last seen UID, with IMAP IDLE where the server supports it.
  2. It parses Authentication-Results. A DMARC pass from the configured authserv-id, with an optional allowlist of From domains, gives verb=message. Anything else gives verb=untrusted, with an optional [UNVERIFIED] subject prefix. EMAIL_STRICT_AUTH=1 drops untrusted mail completely.
  3. It looks up the thread row by Message-ID or References. On a hit, it uses the existing chat_jid. On a miss, it creates a new one.
  4. It posts to /v1/messages on routd, with an ES256 service token from AUTHD_SERVICE_KEY.
  5. For outbound mail, routd calls POST /send. emaid gives the body to SMTP with In-Reply-To and References set from the thread row.

emaid sits between the mail server and routd

mail server (Gmail, Fastmail, …)
        │  IMAP IDLE / poll
        ▼
      emaid     (parse Authentication-Results, classify verb)
        │  POST /v1/messages    (ES256 service token)
        ▼
      routd
        │  POST /send           (callback to emaid)
        ▼
      emaid     (SMTP submit; In-Reply-To = thread root)
        │
        ▼
      mail server ──► recipient
Inputs
IMAP traffic on the configured mailbox, and HTTP callbacks from routd.
Outputs
SMTP submissions, and signed posts to routd.
Dependencies
A reachable routd, an IMAP and SMTP account, and a trusted upstream mail server whose authserv-id is set in EMAIL_TRUSTED_AUTHSERV.

emaid trusts the DMARC result of one named mail server

emaid uses the library emersion/go-msgauth/authres. It runs no DMARC check of its own: it trusts the mail server that delivered the mail. The operator names that server with EMAIL_TRUSTED_AUTHSERV, and with no value, emaid refuses every message. Four settings control the classification:

EMAIL_TRUSTED_AUTHSERV
Required. The authserv-id whose DMARC result emaid trusts, for example mx.google.com.
EMAIL_TRUSTED_DOMAINS
Optional allowlist of From domains. When it is set, a DMARC pass is not enough, and the sender domain must also match.
EMAIL_STRICT_AUTH
A true value, true, 1, yes or on, drops untrusted mail with no agent turn. The default is false, and untrusted mail still reaches the store with verb=untrusted.
EMAIL_UNVERIFIED_SUBJECT_PREFIX
A true value adds an [UNVERIFIED] prefix to the subject of untrusted mail.

routd never promotes an untrusted reply to the bot to verb=mention. Operators route verb=untrusted to a quarantine child group.

emaid runs alone with routd and a mail account

emaid is a plain Go binary. It needs a reachable routd, an IMAP and SMTP account and a writable directory for its thread store.

export ROUTER_URL=http://routd:8080
export AUTHD_URL=http://authd:8080
export AUTHD_SERVICE_NAME=emaid
export AUTHD_SERVICE_KEY=<key from authd>
export LISTEN_ADDR=:8080
export DATA_DIR=/srv/data/arizuko_demo
export EMAIL_IMAP_HOST=imap.fastmail.com EMAIL_IMAP_PORT=993
export EMAIL_SMTP_HOST=smtp.fastmail.com EMAIL_SMTP_PORT=587
export EMAIL_ACCOUNT=bot@example.com EMAIL_PASSWORD=<password>
export EMAIL_TRUSTED_AUTHSERV=mx.example.com
./emaid

GET /health returns 503 when the IMAP login fails or the read loop fails, and 200 after a successful read.

Email supports only plain sending

Email has no reactions, no edit and no delete. emaid implements send and fetch_history. Every other BotHandler verb returns chanlib.ErrUnsupported with a hint, so the agent can pick another action and the call does not fail without a sign.

Four sources cover the details