Modal-vim
email.
IMAP, SMTP, Gmail, Outlook, JMAP. One client, every inbox.
Maildir on disk. SQLite index. hjkl-powered composer.
- ● GitHub PRkryptic-sh/inbx #42 — wire IMAP IDLE socket 2m
- ★ Linus T. Re: Maildir layout — looks fine, ship it 14m
- ● Fastmail JMAPYou have 3 new messages 1h
- · cron daemon [backup] nightly snapshot complete (124 GB) 3h
- · Stripe workReceipt for invoice #INV-2026-0418 yest
- · rust-announce Rust 1.95.0 released — let-chains stabilized 2d
why
An email client shaped like vim, not Outlook.
Modal everything
Vim navigation through the list and the reader — j/k, h/l, gg/G, Tab between panes — and a composer that is a real modal editor, with its own motions, text objects and ex commands.
One keymap table
Every chord lives in a single data-driven bind table — the dispatcher and the ? help overlay read the same rows, so no key is defined twice. / runs an FTS search with n/N, m and y open move / copy pickers, <Space>F does folder create-rename-delete, and <Space>R marks a whole folder read.
Multi-provider, one client
IMAP/SMTP, Gmail XOAUTH2, Microsoft Graph, JMAP. One config, every account. Provider abstracted behind a trait.
Maildir + SQLite + FTS5
On-disk Maildir per account. SQLite index in WAL mode (TUI + sync hold the DB concurrently), with an FTS5 full-text index over subject / from / to / body. JWZ §2 threading, mailing-list bracket tags stripped. Offline-first.
hjkl-powered composer
Composer embeds hjkl — the same modal editor. Headers and body share one FSM. MIME built with mail-builder. <Space>t opens a template picker; PGP key lookup runs inline.
Multi-folder sync · friendly names
Each cycle syncs every server folder; IMAP IDLE / JMAP push triggers a
full re-sync. [Gmail]/Trash renders as Trash,
\Noselect virtual parents hidden. Two-line message rows:
sender on top, subject below.
inbx-sync IPC daemon
Optional inbx-sync daemon (or
inbx sync subcommand) runs the IDLE / fetch / indexing loop
out of process. TUI auto-detects via unix-socket IPC; falls back to
in-process sync when the daemon isn't running.
CalDAV + CardDAV + Sieve
Two-way CalDAV via inbx cal put|delete|rsvp. CardDAV
auto-push on contact upsert. ManageSieve overlay caches the session
across list/get/put. Etag-aware delta fetches.
PGP — gpg or local keys
Per-account pgp.key_source picks between the system GnuPG
keyring or inbx-managed keys on disk. Composer signs and encrypts
inline, via rpgp. Public keys resolve over WKD; Autocrypt 1.1 headers
are harvested on sync, so a correspondent advertising
prefer-encrypt=mutual gets encrypted replies by default.
TLS or nothing
Implicit TLS or STARTTLS only. Never falls through to plaintext. Remote content blocked by default. HTML sanitized via ammonia. Keyring backend selected per platform — no mock-keystore fallback.
Who actually sent this
The reader parses the Authentication-Results header (RFC
8601) and surfaces the SPF / DKIM / DMARC verdicts next to a handful of
cheap phishing signals, flags 1×1 tracker pixels and known beacon hosts
by name, and detects PGP / S/MIME parts. Read receipts are RFC 8098
MDNs, and List-Unsubscribe supports RFC 8058 one-click.
providers
Whatever you use, inbx talks to it.
/me/messages,
/me/sendMail) with delta sync. Tenant-aware.
install
Prebuilt binaries on every tagged release.
macOS via Homebrew tap, Arch via AUR, Alpine .apk, or grab a prebuilt binary. Linux x86_64 (glibc 2.28 and musl) — .tar.gz. macOS, Apple Silicon and Intel — .tar.gz. Windows x64 — .zip. SHA-256 sidecars on every artifact. More targets land as the workspace stabilises.
$ brew install kryptic-sh/tap/inbx $ inbx
$ yay -S inbx-bin $ paru -S inbx-bin # installs inbx + inbx-sync to /usr/bin/, auto-tracks tagged releases
$ # download .apk from releases page, then: $ apk add --allow-untrusted inbx-*.apk $ inbx
$ git clone https://github.com/kryptic-sh/inbx $ cd inbx $ cargo build --release # binaries in target/release/{inbx,inbx-sync}