MML [](https://docs.rs/mime-meta-language/latest/mml) [](https://matrix.to/#/#pimalaya:matrix.org) [](https://fosstodon.org/@pimalaya)
June 7, 2026 · View on GitHub
CLI and lib for the Emacs MIME message Meta Language (MML), written in Rust.
This repository ships two layers:
- Low-level library exposing two pipelines (MML→MIME compiler, MIME→MML interpreter) and a template builder for compose/reply/forward drafts.
- High-level CLI wrapping the library, plus editor-driven
compose/reply/forwardcommands bundling "template →$EDITOR→ compile → validate/re-edit/view/abort", andinterpret(aliasedread) for the inverse MIME→MML flow.
Table of contents
Features
- MML → MIME compilation (requires
compilerfeature):<#part>/<#multipart>directives withtype,filename,disposition,encoding,description,name,recipient-filename, dates, etc.- Inline parts, attached parts, nested multiparts (
alternative,mixed,related) - File-path expansion via
shellexpand - MIME-type detection via
tree_magic_mini - Parse-error reporting via
ariadne(CLI)
- MIME → MML interpretation (requires
interpreterfeature):- Header include / exclude filters
- Part include / exclude filters
- HTML → text rendering via
nanohtml2text - Attachment save-to-disk
mml interpret(aliasedmml read): MIME on stdin, MML/text on stdout
- Editor-driven flow (requires
cli+compiler+interpreter):mml compose/mml reply/mml forward: open$EDITOR, compile on save, prompt to validate / re-edit / view / abort
- TOML configuration with per-account identities and per-section defaults (
[compose],[reply],[forward],[read])
Tip
MML is written in Rust and uses cargo features to gate functionality. The default feature set is declared in Cargo.toml.
Installation
Pre-built binary
The CLI binary mml can be installed from the latest GitHub release using the install script:
As root:
curl -sSL https://raw.githubusercontent.com/pimalaya/mml/master/install.sh | sudo sh
As a regular user:
curl -sSL https://raw.githubusercontent.com/pimalaya/mml/master/install.sh | PREFIX=~/.local sh
For a more up-to-date version, check out the pre-releases GitHub workflow: pick the latest run and grab the artifact matching your OS. These are built from the master branch.
Note
Pre-built binaries are built with the default cargo features. If you need a different feature set, use another installation method.
Cargo
cargo install mime-meta-language --locked --features cli
You can also use the git repository for a more up-to-date (but less stable) version:
cargo install --locked --git https://github.com/pimalaya/mml.git
To use mml as a library, add it to your Cargo.toml:
[dependencies]
mime-meta-language = { version = "1.1", default-features = false, features = ["compiler", "interpreter"] }
Drop cli (and pick only compiler and/or interpreter) for a slim library build with no clap, no ariadne, no editor integration.
Nix
If you have the Flakes feature enabled:
nix profile install github:pimalaya/mml
Or run without installing:
nix run github:pimalaya/mml -- compile <<<'<#part>Hello, world!<#/part>'
Sources
git clone https://github.com/pimalaya/mml
cd mml
nix run
Configuration
A sample config.sample.toml is shipped at the repository root. Drop it into one of:
$XDG_CONFIG_HOME/mml/config.toml$HOME/.config/mml/config.toml$HOME/.mmlrc
Override the path with -c <PATH> or MML_CONFIG=<PATH>.
CLI flags always win; config values fill in the blanks. Pick an account with -a <NAME>, or flag one entry default = true.
Usage
Library
Compile MML to MIME:
use mml::compiler::message::MmlCompilerBuilder;
let mml = "<#part>Hello, world!<#/part>";
let mime = MmlCompilerBuilder::new()
.build(mml)?
.compile()?
.into_string()?;
println!("{mime}");
Interpret MIME back to MML:
use mml::interpreter::message::MimeInterpreterBuilder;
let mime = b"From: a@b\r\nTo: c@d\r\nSubject: Hi\r\n\r\nHello!\r\n";
let mml = MimeInterpreterBuilder::new()
.with_show_only_headers(["From", "To", "Subject"])
.build()
.from_bytes(mime)?;
println!("{mml}");
CLI
Compile MML on stdin, emit MIME on stdout:
mml compile <<< '<#part>Hello, world!<#/part>'
Interpret MIME back to MML/text:
mml interpret < message.eml
Open the editor on a fresh compose draft, then emit the compiled MIME message on stdout, or to a file when a path is given:
mml compose --from me@example.org
mml compose --from me@example.org /tmp/draft.eml
Reply / forward read the source MIME on stdin (or from a path after --) and route the result the same way; the leading positional is the optional output path:
cat message.eml | mml reply --all
cat message.eml | mml forward /tmp/draft.eml
mml reply --all /tmp/draft.eml -- /tmp/source.eml
Pipelines into himalaya v2:
mml compose --from me@example.org /tmp/draft.eml && himalaya messages send /tmp/draft.eml
mml compose --from me@example.org >(himalaya messages send)
himalaya messages read 42 | mml reply >(himalaya messages send)
The path-arg or process-substitution forms keep mml's stdout connected to the terminal, so the editor mml spawns sees a real tty. The bare-pipe form mml compose | himalaya messages send hangs the editor because mml's stdout (and therefore the editor's inherited stdout) is the pipe to himalaya.
Read (MIME → text), useful for piping through less or chaining with himalaya:
cat message.eml | mml read --exclude-header Received,DKIM-Signature
Generate a draft template without opening the editor:
mml template compose --from me@example.org
mml template reply --all < message.eml
mml template forward < message.eml
Plug mml into himalaya v2 by wiring shell pipelines or aliases against the messages send / messages add / messages read primitives. A typical .bashrc snippet:
hsend() { local f=$(mktemp --suffix=.eml); mml compose "$f" && himalaya messages send "$f"; rm -f "$f"; }
hreply() { himalaya messages read "\$1" | mml reply >(himalaya messages send); }
FAQ
How to debug the CLI?
Use --log <level> where <level> is one of off, error, warn, info, debug, trace:
mml --log trace compile < message.mml
The RUST_LOG environment variable, when set, overrides --log and supports per-target filters (see the env_logger documentation). RUST_BACKTRACE=1 enables full error backtraces, including source lines where the error originated from.
Logs are written to stderr, so they can be redirected easily to a file:
mml --log trace compile < message.mml 2>/tmp/mml.log
How does `mml compose` pick the editor?
The edit crate resolves $VISUAL first, then $EDITOR, then an OS default. mml does not expose a config knob on top: set VISUAL / EDITOR in your shell rc file.
License
This project is licensed under either of:
at your option.
AI disclosure
This project is developed with AI assistance. This section documents how, so users and downstream packagers can make informed decisions.
-
Tools: Claude Code (Anthropic), Opus 4.7, invoked locally with a persistent project-scoped memory and a small set of repo-specific rules.
-
Used for: Refactors, mechanical multi-file edits, boilerplate (feature gates, error enums, derive macros, trait impls), test scaffolding, doc polish, exploratory design conversations.
-
Not used for: Engineering, critical code, git manipulation (commit, merge, rebase…), real-world tests.
-
Verification: Every AI-assisted change is read, compiled, tested, and formatted before commit (
nix develop --command cargo check / cargo test / cargo fmt). Behavioural correctness is verified against the relevant RFC or upstream spec, not assumed from the model output. Tests are never adjusted to fit AI-generated code; the code is adjusted to fit correct behaviour. -
Limitations: AI models occasionally produce code that compiles and passes tests but is subtly wrong: off-by-one errors, missed edge cases, plausible but nonexistent APIs, stale RFC references. The verification workflow catches most of this; it does not catch all of it. Bug reports are welcome and taken seriously.
-
Last reviewed: 31/05/2026
Social
- Chat on Matrix
- News on Mastodon or RSS
- Mail at pimalaya.org@posteo.net
Sponsoring
Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:
- 2022 → 2023: NGI Assure
- 2023 → 2024: NGI Zero Entrust
- 2024 → 2026: NGI Zero Core
- 2027 in preparation…
If you appreciate the project, feel free to donate using one of the following providers:
