--- title: Email description: Mount an IMAP mailbox as a Mirage filesystem and send messages through SMTP from Python workspaces. icon: at --- The Email resource exposes any IMAP mailbox as a virtual filesystem mounted at some prefix such as `/email/`. Sending is handled via SMTP. ## Config ```python import os from mirage import MountMode, Workspace from mirage.resource.email import EmailConfig, EmailResource config = EmailConfig( imap_host=os.environ["IMAP_HOST"], smtp_host=os.environ["SMTP_HOST"], username=os.environ["EMAIL_USERNAME"], password=os.environ["EMAIL_PASSWORD"], ) resource = EmailResource(config=config) ws = Workspace({"/email": resource}, mode=MountMode.READ) ``` ### Config Reference | Field | Type | Default | Description | | ----------- | ------ | ------- | ------------------------ | | `imap_host` | `str` | | IMAP server hostname | | `imap_port` | `int` | `993` | IMAP port | | `smtp_host` | `str` | | SMTP server hostname | | `smtp_port` | `int` | `587` | SMTP port | | `username` | `str` | | Email address / login | | `password` | `str` | | Password or app password | | `use_ssl` | `bool` | `True` | Use SSL for IMAP | ### Common Resource Settings | Resource | IMAP Host | SMTP Host | Notes | | ----------- | ----------------------- | --------------------- | -------------------------- | | Outlook/365 | `outlook.office365.com` | `smtp.office365.com` | App password or OAuth2 | | Yahoo | `imap.mail.yahoo.com` | `smtp.mail.yahoo.com` | App password required | | Fastmail | `imap.fastmail.com` | `smtp.fastmail.com` | App password | | iCloud | `imap.mail.me.com` | `smtp.mail.me.com` | App password | | ProtonMail | `127.0.0.1` (Bridge) | `127.0.0.1` (Bridge) | Requires ProtonMail Bridge | | Self-hosted | Your server hostname | Your server hostname | Whatever you configured | ## Filesystem Layout ```text /email/ / / __.email.json __/ # only if message has attachments ``` Example: ```text /email/ INBOX/ 2026-04-14/ Meeting_Notes__12345.email.json Meeting_Notes__12345/ report.pdf screenshot.png Simple_Email__12346.email.json 2026-04-13/ Hello__12347.email.json Sent/ 2026-04-14/ Reply__12348.email.json Drafts/ Archive/ ``` Folder directories appear at the root, using IMAP folder names (e.g., `INBOX`, `Sent`, `Drafts`). ### Date Directories Inside each folder, messages are grouped into date subdirectories formatted as `YYYY-MM-DD`. The date is derived from the message's `Date` header. ### Message Files Each message is stored as a `.email.json` file. The filename shape is: ```text __.email.json ``` ### Attachments Messages that have attachments get a companion subdirectory with the same base name (without `.email.json`): ```text Meeting_Notes__12345.email.json # message JSON Meeting_Notes__12345/ # attachment directory report.pdf screenshot.png ``` ## Cache The Email resource uses `IndexCacheStore` (same as Gmail, Slack, Discord, and other resources). Index entries store folder names, message UIDs, and message metadata. ## Example ```python import asyncio import os from dotenv import load_dotenv from mirage import MountMode, Workspace from mirage.resource.email import EmailConfig, EmailResource load_dotenv(".env.development") config = EmailConfig( imap_host=os.environ["IMAP_HOST"], smtp_host=os.environ["SMTP_HOST"], username=os.environ["EMAIL_USERNAME"], password=os.environ["EMAIL_PASSWORD"], ) resource = EmailResource(config=config) async def main(): ws = Workspace({"/email": resource}, mode=MountMode.READ) # List folders r = await ws.execute("ls /email/") print(await r.stdout_str()) # List date directories in INBOX r = await ws.execute("ls /email/INBOX/") print(await r.stdout_str()) # List messages for a specific date r = await ws.execute("ls /email/INBOX/2026-04-14/") print(await r.stdout_str()) # Read a message r = await ws.execute( "cat /email/INBOX/2026-04-14/Meeting_Notes__12345.email.json") print(await r.stdout_str()) # Extract subject with jq r = await ws.execute( 'jq ".subject"' " /email/INBOX/2026-04-14/Meeting_Notes__12345.email.json") print(await r.stdout_str()) # List attachments r = await ws.execute("ls /email/INBOX/2026-04-14/Meeting_Notes__12345/") print(await r.stdout_str()) # Search across all messages r = await ws.execute('rg "quarterly" /email/INBOX/') print(await r.stdout_str()) # Tree view r = await ws.execute("tree -L 2 /email/INBOX/") print(await r.stdout_str()) # Triage unread messages r = await ws.execute("email-triage --folder INBOX --unseen --max 5") print(await r.stdout_str()) # Send an email r = await ws.execute( 'email-send --to "user@example.com"' ' --subject "Hello from MIRAGE"' ' --body "This email was sent via the MIRAGE email resource."') print(await r.stdout_str()) if __name__ == "__main__": asyncio.run(main()) ``` ## Finding UIDs Resource-specific commands require message UIDs. These can be extracted from the filesystem: ```bash # UID is embedded in filename after "__" ls /email/INBOX/2026-04-14/ # -> Meeting_Notes__12345.email.json <- uid = 12345 # Read a message then reply email-read --uid 12345 --folder INBOX email-reply --uid 12345 --folder INBOX --body "Thanks for the notes" ``` ## Shell Commands Standard commands available on the mounted email tree: | Command | Notes | | --------------- | ------------------------------------------ | | `ls` | List folders, dates, messages, attachments | | `cat` | Read message JSON or attachment content | | `head` / `tail` | First/last N lines | | `grep` / `rg` | Pattern search (file or directory level) | | `jq` | Query message JSON fields | | `wc` | Line/word/byte counts | | `stat` | File metadata (name, size, type) | | `find` | Recursive search with `-name`, `-maxdepth` | | `tree` | Directory tree view | | `basename` | Extract filename from path | | `dirname` | Extract directory from path | | `realpath` | Resolve path to absolute form | | `nl` | Number lines of output | Resource-specific commands: ### `email-send` Send a new email. ```bash email-send --to "user@example.com" --subject "Hello" --body "Hi there" ``` | Option | Required | Description | | ----------- | -------- | ----------------------- | | `--to` | yes | Recipient email address | | `--subject` | yes | Email subject line | | `--body` | yes | Email body text | Returns the sent message status JSON. ### `email-reply` Reply to a message. ```bash email-reply --uid 12345 --folder INBOX --body "Thanks for the update" ``` | Option | Required | Description | | ---------- | -------- | ---------------------------- | | `--uid` | yes | Message UID | | `--folder` | no | IMAP folder (default: INBOX) | | `--body` | yes | Reply body text | Returns the sent reply JSON. ### `email-reply-all` Reply-all to a message. ```bash email-reply-all --uid 12345 --folder INBOX --body "Acknowledged by the team" ``` | Option | Required | Description | | ---------- | -------- | ---------------------------- | | `--uid` | yes | Message UID | | `--folder` | no | IMAP folder (default: INBOX) | | `--body` | yes | Reply body text | Returns the sent reply JSON. ### `email-forward` Forward a message to another recipient. ```bash email-forward --uid 12345 --folder INBOX --to "colleague@example.com" ``` | Option | Required | Description | | ---------- | -------- | ---------------------------- | | `--uid` | yes | Message UID | | `--folder` | no | IMAP folder (default: INBOX) | | `--to` | yes | Recipient email address | Returns the forwarded message JSON. ### `email-triage` Search and triage emails. ```bash email-triage --folder INBOX --unseen --max 10 ``` | Option | Required | Description | | ---------- | -------- | ---------------------------- | | `--folder` | no | IMAP folder (default: INBOX) | | `--max` | no | Max results (default: 20) | | `--unseen` | no | Only show unread messages | Returns matching messages as JSON. ### `email-read` Read a message by its UID. ```bash email-read --uid 12345 --folder INBOX ``` | Option | Required | Description | | ---------- | -------- | ---------------------------- | | `--uid` | yes | Message UID | | `--folder` | no | IMAP folder (default: INBOX) | Returns the full message JSON.