Markdown Email
AIMX is Markdown-native end-to-end. Inbound mail is stored as Markdown for direct LLM consumption; outbound --body is interpreted as Markdown by default and rendered to HTML on the wire. Agents read Markdown, write Markdown, and the binary handles the MIME plumbing.
How a default send is delivered
- Caller submits Markdown.
aimx send --body "..."(or the MCPemail_sendtool withbody: "...") ships the Markdown source to the daemon over the local UDS. - Daemon appends the per-mailbox signature to the body so the recipient always sees it in the
text/plainregion. On the default Markdown path the signature is part of the Markdown source, so it also renders into the HTML alternative (and a[link](url)in the signature becomes a clickable<a>). - Daemon renders Markdown to HTML. The renderer uses
comrakconfigured for CommonMark + GFM extensions (tables, strikethrough, autolinks, task lists, footnotes, tag filter for unsafe tags). Raw HTML embedded in Markdown is escaped, not rendered — operators wanting raw HTML use--html-bodyinstead. - Inlined stylesheet pass. The renderer walks the rendered HTML tree and adds
style="..."attributes per element. Inlining is required because Gmail, Outlook for Web, and Yahoo Mail strip or limit<style>blocks. - Multipart assembly. The daemon builds a
multipart/alternativeMIME message with the Markdown source as thetext/plainpart and the rendered HTML as thetext/htmlpart. With one or more attachments, themultipart/alternativeis wrapped in an outermultipart/mixedso attachments sit as siblings of the alternative block. - DKIM signing. The signed body bytes are the canonical message body — the new MIME shape doesn’t affect the signing logic, and DKIM verification works identically across plain-text, Markdown-rendered, and
--html-bodypaths. - MX delivery. The daemon resolves the recipient domain via MX records and delivers the signed message.
The recipient sees:
- Rich-text clients (Gmail, Outlook, Apple Mail, Thunderbird): the rendered HTML with headings, links, tables, blockquotes, code blocks — all styled by the inlined stylesheet.
- Text-only clients (
mutt,mailx, screen readers in plain mode): the Markdown source verbatim. Markdown is good plain text by design —# Headingis clear,[link](url)shows the URL inline, bullets with-look like bullets.
Supported Markdown features
The default render path supports CommonMark plus GFM extensions:
| Feature | Markdown input | Renders as |
|---|---|---|
| Headings | # H1 … #### H4 | <h1> … <h4> with sized typography |
| Paragraphs | blank-line separated | <p> with comfortable line-height |
| Bold / italic | **bold**, *italic* | <strong>, <em> |
| Strikethrough (GFM) | ~~strike~~ | <del> |
| Links | [text](https://url) | <a> with non-default link color, no underline at rest |
| Autolinks (GFM) | bare https://example.com | <a> |
| Inline code | `code` | <code> with monospace + light gray background |
| Code blocks | ```lang … ``` | <pre><code> with monospace + padded background |
| Blockquotes | > quoted | <blockquote> with left border |
| Horizontal rule | --- | <hr> thin neutral rule |
| Unordered list | - item | <ul><li> with sensible spacing |
| Ordered list | 1. item | <ol><li> with sensible spacing |
| Tables (GFM) | | h1 | h2 | / |---|---| / cells | <table> with collapsed borders and subtle row separators |
| Task lists (GFM) | - [x] done / - [ ] todo | checkbox-prefixed list items |
| Footnotes (GFM) | text[^1] … [^1]: note | numbered footnote anchor + reference list |
| Tag filter (GFM) | <script>...</script> | stripped (security) |
Built-in stylesheet
The inlined stylesheet covers the elements above. It is intentionally minimal — operators wanting custom CSS use --html-body for now.
| Element | Style intent |
|---|---|
body | sans-serif font stack, line-height: 1.55, max-width: 720px, comfortable padding |
h1, h2, h3, h4 | size scale, modest top margin |
p, ul, ol, li | sensible spacing |
a | non-default link color (avoids the browser-default purple-after-visit), no underline at rest |
code, pre | monospace, light gray background, padding |
blockquote | left border, dim foreground |
table, th, td | collapsed borders, subtle row separators |
hr | thin neutral rule |
The stylesheet is self-contained: no <link> to Google Fonts, no remote CSS, no remote images injected by the renderer. Privacy-conscious clients and corporate firewalls do not strip the rendering. The total rendered HTML for a typical 5KB Markdown briefing fits comfortably under 25KB.
If your content uses an HTML element the inlined stylesheet does not cover (e.g. <details>, <sub>), it renders unstyled (browser defaults). The element list above is the v1 scope; expand on real demand.
Body size limit
The renderer enforces a 5 MiB cap on the Markdown source byte length (the constant is MAX_MARKDOWN_BODY_BYTES = 5 * 1024 * 1024). Above the cap, the daemon refuses with the canonical error:
markdown body exceeds 5 MiB; use --html-body for pre-rendered large documents or --attachment for sending the document as a file
Rationale: a 5 MiB Markdown body renders to roughly 15–25 MiB on the wire (Markdown source + HTML + inlined styles, with ~37% base64 overhead). That sits at the edge of mainstream receiver caps — Gmail 25 MB, Outlook.com / iCloud 20 MB, Microsoft 365 up to 150 MB. Send anything larger as an attachment, not as a body.
The cap is enforced at the renderer entry point so all callers (aimx send, MCP email_send, future cron jobs) share one limit. Operators on the wire surface that scripts can branch on the failure see the dedicated ERR BODY_TOO_LARGE ack code; the canonical reason string survives in the wire response for human readers.
Escape hatches
--text-only
Forces the wire to single-part text/plain with --body shipped verbatim. No Markdown rendering, no HTML part, no multipart/alternative wrapper.
aimx send --from alice@example.com --to bob@example.com \
--subject "Verification code" \
--body "Your code: 184293" \
--text-only
The per-mailbox signature is auto-appended on this path. AIMX appends the configured signature (or the built-in default) to the body before encoding, so even one-liner text-only sends carry the configured footer. Disable globally with signature = "" in config.toml if your one-liner shape really needs to ship verbatim.
Use for:
- OTPs, transactional one-liners, system-generated alerts.
- Migrating existing scripts that depended on
--bodyshippingtext/plain. Adding--text-onlypreserves the old shape exactly.
--html-body
Supplies a custom HTML body. AIMX uses the --html-body value verbatim as the text/html part and uses --body as the text/plain fallback so text-only clients still see something readable.
aimx send --from alice@example.com --to bob@example.com \
--subject "Newsletter" \
--body "Plain-text fallback for text-only clients." \
--html-body "$(cat newsletter.html)"
The shell pattern --html-body "$(cat template.html)" reads the template from a file and passes it on the command line. Linux’s typical ARG_MAX is around 1MB, well above any reasonable HTML email size. For templates that exceed ARG_MAX, write the template to a tempfile and use a wrapper script — but at that size you should consider attaching the document instead.
--html-body and --text-only are mutually exclusive. clap rejects the invocation before any UDS round-trip. The same canonical error fires server-side on the MCP email_send / email_reply tools so operators see one consistent message regardless of the surface.
The per-mailbox signature is appended to the text/plain fallback so text-only readers still see it. The supplied HTML stays operator-verbatim — if you want the signature visible in the HTML view, include it inside your template. Disable globally with signature = "" in config.toml if you want the text fallback to ship verbatim too.
--html-body bypasses the renderer’s tag-filter and other safety checks; the operator owns the consequences. Do not pass user-controlled HTML through this flag.
Sent storage
Sent records under sent/<mailbox>/ always store the Markdown source (or the literal text for --text-only, or the --body text part for --html-body). The recipient’s HTML view is recoverable by re-running the same renderer on the stored Markdown — output is deterministic given the pinned comrak version.
The frontmatter declares the wire shape so an operator browsing sent/ can tell at a glance what the recipient saw:
outbound_format | Wire shape | Sent body content |
|---|---|---|
"markdown" | multipart/alternative (text + rendered HTML) | Markdown source with signature appended before render |
"text" | single-part text/plain | body with signature appended |
"html" | multipart/alternative (text + custom HTML) | the --body text part with signature appended (custom HTML is not stored) |
The outbound_format field appears immediately after outbound = true in the Outbound block of the frontmatter:
outbound = true
outbound_format = "markdown"
delivery_status = "delivered"
Pre-feature sent records lack the field; on read, those default to "text" (the historic single-part text/plain shape) so legacy records keep parsing cleanly.
No .html sibling file is ever written. Operators who need a record of the exact custom HTML they sent should keep their template under version control — the --html-body payload is not persisted by AIMX.
Determinism
The renderer is deterministic: the same Markdown input produces byte-identical HTML output across two invocations. This is what justifies dropping the rendered HTML from sent storage — the recipient’s view is recoverable. comrak is pinned to an exact patch version in Cargo.toml; bumping it requires re-blessing the renderer fixtures and is called out on the corresponding GitHub Release.
CI defends the determinism guarantee with two checked-in fixtures (tests/fixtures/markdown/briefing-5kb.md and tests/fixtures/markdown/report-50kb.md) and their pinned expected outputs. A comrak bump that changes whitespace or attribute ordering surfaces at CI time, not in production.
Worked example
Input Markdown:
# Daily briefing — 2026-05-07
## Open positions
| Symbol | Shares | Cost basis |
|--------|-------:|-----------:|
| AAPL | 100 | $150.00 |
| GOOG | 50 | $2,800.00 |
## Notes
- Earnings season starts next week.
- See the [shareholder letter](https://example.com/letter) for context.
> The market always overreacts in the short term.
```python
def total(positions):
return sum(s * p for s, p in positions)
```
What the recipient sees:
- Gmail / Outlook / Apple Mail: rendered
<h1>,<h2>, a styled<table>, a clickable<a>link, a<blockquote>with a left border, and a<pre><code>block with monospace background. - Text-only client: the Markdown source verbatim — readable, with hierarchy preserved by
#and##, the table cells visible as pipes-and-dashes.
What gets stored in sent/alice/2026-05-07-120000-daily-briefing-2026-05-07.md:
+++
id = "2026-05-07-120000-daily-briefing-2026-05-07"
# ... (other frontmatter fields) ...
outbound = true
outbound_format = "markdown"
delivery_status = "delivered"
+++
# Daily briefing — 2026-05-07
# ... (Markdown source verbatim) ...
Re-running the daemon’s renderer on the stored body reproduces the recipient’s HTML view exactly.
Ready to try AIMX?
One command, one box, one inbox.
Get started