Your mail as a native Omarchy window — not a browser tab.
Omamail is an Omarchy desktop email client: a Quickshell plugin that reads,
triages, and answers your mail over the official Gmail API, or over IMAP and
SMTP for every other mailbox. It runs inside the omarchy-shell process you
already have, follows your active theme, and puts an unread count in the bar.
Works with Gmail, Fastmail, iCloud Mail, Outlook, Yahoo, Zoho, GMX, Proton Mail (through its Bridge), and any other IMAP server — including one you run yourself.
- Designed, not assembled. Monospace, square-cornered, and built to sit inside Omarchy rather than to look like a web app in a window. Three columns when there is room, one when there is not, and nothing on screen that is not your mail.
- Gmail and IMAP. Sign in to Gmail with Google directly, or add any IMAP mailbox with an address and an app password. Several accounts at once, each with its own inbox, cache and unread count.
- Keyboard-first.
j/kto move,eto archive,sto star,rto reply,cto compose,g ifor the inbox,/to search — Gmail's own shortcuts, so there is nothing new to learn. - Always counting. The unread badge keeps working while the window is shut, for every account, with a desktop notification when new mail lands.
- One window. Read, archive, star, trash, search, and answer without a second window taking a region of its own.
- Images stay blocked. Loading a sender's pictures tells them the mail was read, from which address and when. They load when you ask, for that one message.
- Your theme. Every colour comes from the active Omarchy theme, so the mailbox changes the moment the desktop does.
- Keyring-backed. The Gmail refresh token and every IMAP password live in GNOME Keyring — never in a config file, never on a command line.
And with mini size mode:
Three parts, one plugin:
- an unread badge in the bar, which keeps counting whether or not the window is open
- an application window — a real Hyprland window, tiled like any other, with your mailboxes, the message list, and the reader side by side
- compose and reply inside that same window, because a second window would take a region of its own under Omarchy's panel mechanism
omarchy plugin add https://github.com/huacnlee/omamail.git --enableThen click the envelope in the bar. To open it from the keyboard, add this to
~/.config/hypr/bindings.lua:
o.bind("SUPER + SHIFT + G", "Omamail", "omarchy shell shell toggle omamail '{}'")The target is shell, not the plugin id: the window is summoned by the shell,
which is what loads it in the first place. A plugin-scoped target would have to
be registered by code that is only running once the window is already open.
Requires Omarchy 4, plus socat, secret-tool, openssl, xdg-open and
curl — all of which Omarchy already ships.
Adding a mailbox asks which kind first, because the two setups have nothing in common.
Gmail signs in with Google directly. Google issues Gmail API access per project, so this route needs an OAuth client you create once — the setup page walks through it. In exchange it gets labels, conversations, Gmail's own search syntax, and a "report spam" that Google actually learns from.
IMAP is an address and a password. Fastmail, iCloud, Zoho, Outlook, GMX, Proton via its Bridge, or a server of your own: the servers are filled in from the address for the ones this knows, and shown behind a disclosure so they can be corrected for the ones it does not. Most providers want an app password rather than the one you sign in to their website with, and the form says so before you find out the hard way.
What IMAP does not have, the panel does not offer: no labels, no server-side conversations, no "report spam" — moving a message to a Junk folder teaches a server nothing, and a button that quietly meant that would be a promise this could not keep. Archive appears only when the server has an archive folder to move to. Sending goes out over SMTP, or the mailbox is read-only if no SMTP server is set.
HEY is listed as a future integration. A HEY CLI is reportedly in development; once it is ready, Omamail can support it through the provider seam that is already in place.
To remove it:
omarchy plugin remove omamailThat takes the plugin itself. Nothing it wrote lives inside your Omarchy config, so removing those is separate and entirely up to you:
secret-tool clear service omamail # the refresh token and IMAP passwords
rm -rf ~/.config/omamail # the OAuth client and account list
rm -rf ~/.cache/omamail # cached mailSigning out from inside the app clears the keyring entry on its own. The plugin never edits your shell, Hyprland or theme configuration — the one keybinding above is yours to add and yours to remove.
Gmail has no shared application to sign in through. Google issues API access per Cloud project, so Omamail signs in with an OAuth client you own. The window walks you through it in five steps, each with the console page one click away. It takes about two minutes, once.
The step people skip, and the one that decides whether the sign-in lasts: press "Publish app" on your own project. A project left in Testing is issued refresh tokens that expire after seven days, so the app would sign you out every week. Publishing shows an "unverified app" warning once — expected for a client you made yourself, since you are the developer and the only user.
If you have the gcloud CLI, scripts/google-cloud-setup.sh does the two
steps that have an API — creating the project and enabling Gmail — and opens
the console on the rest with the project already selected. The consent screen
and the client itself are console-only; there is no CLI for them.
Why isn't a client built in?
gmail.modifyandgmail.sendare restricted scopes. Shipping a client would mean this project completing Google's OAuth verification first; until then it would be stuck in Testing, handing every user a seven-day session. The code is ready for one —Credentials.BUILTINis a single constant — and your own client always wins over it.
| Key | What it does |
|---|---|
j / k |
Move down / up |
Enter or o |
Open the selected message |
Esc |
Back to the list; close the window from the list |
e |
Archive |
d |
Move to trash |
s |
Star or unstar |
Shift+I / Shift+U |
Mark read / unread |
r / a / f |
Reply, reply all, forward |
c |
Compose |
Ctrl+Enter |
Send |
/ or Ctrl+K |
Search |
g then i / s / u / t |
Inbox, starred, unread, sent |
Ctrl+= / Ctrl+- / Ctrl+0 |
Zoom the message body, or reset it |
F5 |
Check for mail |
Ctrl+? |
Every shortcut |
Search takes Gmail's own operator syntax straight through — from:jane,
has:attachment, older_than:7d. The Unread mailbox is scoped to Primary:
category tabs do not remove the INBOX label, so an unread filter without that
scope returns the whole promotional backlog rather than the mail you have not
read. Right-click any row in the list for archive,
trash, spam, star and read/unread without leaving the keyboard cursor behind.
- No embedded browser. Message bodies render through Qt's own rich text
engine, which handles the HTML-4-and-inline-styles subset that real mail is
written in. A browser engine cannot be embedded in a plugin at all:
QtWebEngineQuick::initialize()has to run before the host process builds itsQGuiApplication, and a plugin loads long after that. - No attachment downloads. Not yet.
Remote images in a message body are blocked until you ask for them, and asking
covers that one message. Qt really does fetch an <img src="https://…">, so
loading a message's pictures fires whatever tracking pixels it carries and tells
the sender when the mail was read — which is why it is a decision rather than a
default. Images pointed at this machine or at the network around it (loopback,
private addresses, .local names, file:) are never fetched at all, however
often you ask: a message must not be able to make the client knock on the door
of something listening on your own network.
Several mailboxes can be added and switched between; each keeps its own cache, its own refresh token, and its own unread count, and the bar badge counts all of them. They share one OAuth client, since a client belongs to a Cloud project rather than to an address — so adding a second mailbox is a sign-in, not another trip through the console. Mailboxes are added and removed on the settings page, and switched from the menu or the user bar at the foot of the rail.
The message list, labels and profile are cached per account so switching never waits on the network. Message bodies are cached one file per message — a thousand of them, evicted least-recently-used.
- The refresh token goes to GNOME Keyring, keyed by client and account, written over stdin so it never appears in the process table. Two mailboxes share one client, so keying by client alone would have let the second sign-in overwrite the first.
- The OAuth client goes to
~/.config/omamail/credentials.json, mode0600. Not to plugin settings —shell.jsonis world-readable. - The access token exists only in memory.
- Signing out clears the keyring entry.
The app asks for gmail.modify and gmail.send. gmail.modify covers reading,
labelling, archiving and trashing, and deliberately cannot delete anything
permanently.
./install.sh # symlink this checkout into ~/.config/omarchy/plugins
make validate # node tests, source regressions, qmllint, manifest checkWorking agreements are in AGENTS.md and the specification is in docs/SPEC.md.
Omamail is an independent project and is not affiliated with Google. Gmail is a trademark of Google LLC.
Licensed under the MIT License.

