TnsAI

Email

Sona's email channel polls an IMAP inbox for new mail and replies through an SMTP server. It's the right pick when you want Sona reachable from any device a person already uses email on — phones, work laptops, shared accounts — without installing a new app.

If you ran the Quickstart, the wizard skipped this channel; email isn't enabled by default because the configuration involves real credentials and a deliberate sender allowlist. This page walks through enabling it from scratch.

Choose an account

Use a mailbox dedicated to Sona rather than your personal inbox. The channel reads every unread message in INBOX and treats matching senders as conversation partners — pointing it at a busy account is noisy and risks accidentally replying to a real human.

Common setups:

  • Gmail — works with an app password (requires 2-Step Verification on the account). The plain account password will not work; Google blocks IMAP password auth for security.
  • iCloud Mail — same story: turn on 2-Step Verification, then create an app-specific password.
  • Self-hosted / cohosted IMAP (Migadu, Fastmail, Zoho, your company server) — usually accepts the regular login password directly.

Whichever you pick, write down four things: IMAP host + port, SMTP host + port, username (typically the email address), and the password / app password for IMAP. SMTP usually accepts the same credentials, but you can override per direction if your provider gives you a separate token.

Wire it into Sona

Edit ~/.sona/sona.yml and add a channels.email block. Gmail example:

# ~/.sona/sona.yml
channels:
  email:
    imap_host: imap.gmail.com
    imap_port: 993
    imap_user: sona-bot@example.com
    imap_password: ${EMAIL_IMAP_PASSWORD}   # the app password
    imap_ssl: true
    imap_folder: INBOX

    smtp_host: smtp.gmail.com
    smtp_port: 587
    smtp_user: sona-bot@example.com
    smtp_password: ${EMAIL_SMTP_PASSWORD}   # often the same value
    smtp_start_tls: true
    from_address: sona-bot@example.com

    sender_allowlist:
      - you@example.com

    poll_interval_seconds: 60

The ${VAR} form reads from the environment at boot, keeping the secret out of the YAML file. Drop the credentials into your shell profile or systemd unit:

export EMAIL_IMAP_PASSWORD="abcd efgh ijkl mnop"   # Gmail app password format
export EMAIL_SMTP_PASSWORD="$EMAIL_IMAP_PASSWORD"

Generic non-Gmail provider with IMAPS + SMTPS (port 465) looks the same, just swap hosts and flip smtp_start_tls: false if your SMTP wants implicit TLS instead.

Field reference

FieldDefaultNotes
imap_host, imap_portRequired. 993 for IMAPS, 143 for STARTTLS-then-plain.
imap_user, imap_passwordRequired. App password for Gmail/iCloud.
imap_ssltruetrue for port 993, false for 143.
imap_folderINBOXOverride if Sona should poll a label/folder instead.
smtp_host, smtp_portRequired. 587 for STARTTLS, 465 for SMTPS.
smtp_user, smtp_passwordfalls back to IMAP credsSet only if SMTP needs a different login.
smtp_start_tlstruetrue for 587, false for 465 (which negotiates TLS at connect time).
from_addressfalls back to smtp_userUseful when SMTP auth account ≠ display address (e.g. shared-inbox setups).
sender_allowlist[]Critical — see below. Empty list means "drop all inbound mail".
poll_interval_seconds60How often the channel re-checks IMAP. Lower for tighter feedback; higher to spare your IMAP server.

Sender allowlist — read this carefully

Email is the most spammable channel Sona supports. To keep the gateway off the agentic-reply-bot spam list, the channel silently drops every inbound message whose From address is not on sender_allowlist (case-insensitive). A blank allowlist drops everything.

This is intentional. If you remove an address you no longer trust, future mail from that account stops reaching Sona without you having to redeploy. If you forget to add an address, your test message lands in the inbox but never reaches the agent and you get no reply — which is the failure mode you want.

Add every sender you expect to chat with:

    sender_allowlist:
      - you@example.com
      - partner@example.com
      - oncall+sona@company.example.com

Wildcards aren't supported; list each address. If a teammate forwards mail to Sona under their own address, that's the address to allowlist, not the original sender's.

First message

Restart the daemon (sona stop && sona). On a healthy start you'll see a log line like:

INFO  c.t.c.email.EmailChannel [] - Email adapter started — polling imap.gmail.com:993 every 60s

Send a message from one of the allowlisted addresses to sona-bot@example.com. Within the poll window, Sona reads it, runs the agent, and replies. Replies use the original Message-ID for In-Reply-To so they thread correctly in your mail client.

The pairing flow that Telegram uses doesn't apply here — the allowlist is the trust boundary, so every allowlisted address is already paired implicitly.

Operational notes

  • The channel marks messages as read after processing. If you also want them moved out of the inbox, set up a server-side rule keyed off the From: sona-bot@… outbound header.
  • poll_interval_seconds: 60 is a sensible default. Going below 30 seconds rarely helps and starts to look like a misbehaving client to some providers; going above 300 makes replies feel asynchronous in a bad way.
  • IMAP IDLE (push) is not used. Sona's deliberate poll-only design means transient connection drops don't strand inbound mail — the next poll catches up.
  • from_address controls only what arrives in recipients' inboxes; SMTP servers may still rewrite the envelope sender. Test against the actual provider before assuming the From you set is the From users see.

Troubleshooting

Authentication failed on startup. Gmail and iCloud both reject regular account passwords for IMAP. Confirm you're using an app password and that 2-Step Verification is on for the account.

Sona logs Email adapter started but inbound messages never reach the agent. Two usual suspects: (1) the sender address isn't on sender_allowlist — the channel drops these silently. (2) the message is in a folder other than imap_folder (Gmail's "Promotions" / "Updates" tabs apply server-side filters; explicitly route inbound Sona mail to INBOX via a Gmail rule).

Outbound mail fails with 530 Authentication required. Either smtp_user / smtp_password is wrong, or your SMTP host requires STARTTLS that isn't enabled — flip smtp_start_tls: true if you're on port 587.

Replies arrive but don't thread in the original conversation. Some clients use the References header, others use In-Reply-To; Sona sets both. If threading still breaks, check that your mail client honours the standard headers — Outlook web has had on-and-off bugs here.

Mail looks fine in the IMAP server but the agent never sees it. Check ~/.sona/sona.log for Email adapter lines. The most common silent drop is allowlist mismatch on a forwarded address: the From Sona sees is the forwarder, not the original sender.

If something else breaks, sona doctor checks the IMAP and SMTP probes at startup and prints what failed. For framework-side bugs, file an issue against TnsAI.Channels with the relevant log lines.

On this page