A CLI tool for extracting and converting emails from mbox archive files.
- Browse emails interactively in a terminal UI (search, select, export)
- Extract emails from
.mboxfiles as individual.eml,.txt, or.mdfiles - Saves attachments alongside converted emails
umbox requires Go 1.21 or later.
macOS (Homebrew):
brew install gomacOS/Linux (official installer): Download from https://go.dev/dl/ and follow the instructions.
Verify installation:
go version
# Should print something like: go version go1.26.1 darwin/arm64If you've never used Go before, here are the key things to know:
go.mod— Likepackage.json(Node) orpyproject.toml(Python). Defines the module name and dependencies.go build— Compiles your code into a single binary. No runtime needed!go run .— Compiles and runs in one step (useful during development).go mod tidy— Adds missing dependencies and removes unused ones (likenpm install).- Packages — Each folder is a "package". Files in the same folder share the same package namespace.
- Exported vs unexported — Names starting with an uppercase letter (like
Parse) are public. Lowercase names (likeparseMessage) are private to the package.
# Clone the repository
git clone https://github.com/jneuendorf/umbox.git
cd umbox
# Download dependencies (cobra CLI framework)
go mod tidy
# Build the binary
go build -o umbox .
# (Optional) Install globally — puts the binary in your $GOPATH/bin
go install ../umbox browse inbox.mboxThis opens a terminal UI with:
- Left pane: Scrollable email list with selection checkboxes
- Right pane: Preview of the highlighted email
- Search: Press
/to filter by sender, subject, or body text - Export: Select emails with
space, then presseto export
Key bindings:
| Key | Action |
|---|---|
↑/↓ or j/k |
Navigate email list |
tab |
Switch focus between list and preview |
space |
Toggle select current email |
a |
Select/deselect all |
/ |
Search/filter |
e |
Export selected emails |
q |
Quit |
# Extract as raw .eml files (default)
./umbox extract inbox.mbox -o ./my-emails
# Extract as Markdown
./umbox extract inbox.mbox -f markdown -o ./readable
# Extract as plain text
./umbox extract inbox.mbox -f plaintext -o ./readableAvailable formats:
raw(default) — Standard.emlfiles, openable by any email clientmarkdown—.mdfiles with metadata table and bodyplaintext— Simple.txtfiles
Attachments are saved in numbered subfolders alongside each email (for markdown and plaintext formats):
readable/
├── 001.md
├── 001_attachments/
│ ├── report.pdf
│ └── photo.jpg
├── 002.md
└── 003.md
./umbox --help
./umbox extract --help
./umbox browse --helpumbox/
├── main.go # Entry point — just calls cmd.Execute()
├── cmd/ # CLI commands (thin wrappers around core logic)
│ ├── root.go # Base command + help text
│ ├── browse.go # "browse" subcommand (launches TUI)
│ └── extract.go # "extract" subcommand (all formats)
├── tui/ # Interactive terminal UI (Bubble Tea)
│ ├── tui.go # Main model — Init/Update/View + Run()
│ ├── keymap.go # Key binding definitions
│ └── styles.go # lipgloss color/layout styles
├── mbox/ # Core library — parsing mbox files
│ ├── message.go # Message and Attachment data types
│ └── parser.go # Mbox file parser
└── formatter/ # Output format system (extensible)
├── formatter.go # Formatter interface
├── registry.go # Format registry (lookup by name)
├── raw.go # Raw .eml output (no conversion)
├── plaintext.go # Plain text output
└── markdown.go # Markdown output
The architecture is modular by design:
mbox/handles all parsing — no I/O decisions, no formattingformatter/handles all output formatting — pluggable via an interfacetui/importsmboxandformatterdirectly — no logic duplicationcmd/is just glue code that wires everything together
The formatter system is designed to be extended. To add a new format (e.g., HTML):
- Create a new file
formatter/html.go - Implement the
Formatterinterface:
package formatter
import (
"fmt"
"io"
"github.com/jneuendorf/umbox/mbox"
)
// init registers this formatter automatically when the package is imported.
func init() {
Register(&HTMLFormatter{})
}
type HTMLFormatter struct{}
func (f *HTMLFormatter) Name() string { return "html" }
func (f *HTMLFormatter) Extension() string { return ".html" }
func (f *HTMLFormatter) Format(msg *mbox.Message, w io.Writer) error {
fmt.Fprintf(w, "<html><body>")
fmt.Fprintf(w, "<h1>%s</h1>", msg.Subject)
// ... your HTML formatting logic here ...
fmt.Fprintf(w, "</body></html>")
return nil
}That's it! The init() function registers the formatter automatically, and it becomes available via --format html on the CLI and in the TUI export dialog.
- Gmail: Google Takeout → select "Mail" → downloads as
.mbox - Thunderbird: Right-click a folder → "ImportExportTools NG" addon → Export as mbox
- Apple Mail: Mailbox → Export Mailbox
# Run without building (useful during development)
go run . extract inbox.mbox -o ./test-output
# Build
go build -o umbox .
# Run tests
go test ./...
# Run tests with verbose output
go test ./... -v
# Format code (Go has an official formatter — always use it)
go fmt ./...- Extract emails (raw .eml, plain text, Markdown)
- TUI for browsing and selectively exporting emails
- HTML output format
- Additional search filters (date range, attachment presence)