{"slug": "show-hn-qmail-a-local-mail-client-with-cli-tools-for-your-agent", "title": "Show HN: Qmail a local mail client with CLI tools for your agent", "summary": "A developer released Qmail, a local mail client for Windows with new, untested macOS and Linux builds, that ships a `qmail` command-line tool letting AI agents and scripts read, search, send and file mail across all IMAP/SMTP, Microsoft 365/Outlook and Gmail mailboxes. Qmail stores everything on the user's PC with no server and no per-call API cost, and every command supports `--json` output, non-zero exit codes with `{\"error\": \"...\"}` and piped-in IDs, with search running on a local full-text index over the whole mailbox. The Windows installer is not code-signed and the macOS build is not signed with an Apple developer certificate, so users must bypass OS warnings and, on macOS, run `xattr -cr /Applications/Qmail.app` before opening it.", "body_md": "A fast, Gmail-style mail client for Windows (with new macOS and Linux builds), plus a command-line tool (`qmail`) that AI agents and scripts can use to read, search and send mail across all your mailboxes.\n\n- Works with any IMAP/SMTP mailbox, Microsoft 365 / Outlook, and Gmail.\n- All mailboxes in one inbox, with unread counts on the taskbar.\n- Everything is stored on your PC. Qmail has no server and sends nothing anywhere except to your own mail providers.\n\nReading, searching, sending, filing, attachments, contacts and sorting rules are all available from the terminal. Installing Qmail puts `qmail` on your PATH; it works on the same local mail store as the app, across every mailbox at once, with no API keys and no cost per call.\n\n```\nqmail list --unread --json                          # what is new, in every mailbox\nqmail search \"from:stripe has:attachment newer_than:30d\" --json\nqmail read 1234 --json                              # text, html, attachments\nqmail send --from you@example.com --reply-to 1234 --body \"Thanks!\"\nqmail senders -n 50                                 # who fills the inbox\nqmail move 1234 1235 --to Money/Receipts --create   # also: archive, trash, mark\nqmail rules add --from stripe.com --move Money/Receipts --read\nqmail download 1234 -o ./invoices                   # save attachments\n```\n\n- **Agent friendly** : every command takes`--json` , errors exit non-zero with`{\"error\": \"...\"}` , ids can be piped in on standard input, and`qmail --help` documents everything.\n- **Whole mailbox, not a sample** : search runs on a local full-text index, so an agent can go through tens of thousands of messages in seconds.\n- **Ready-made prompts** :[Things to ask an agent](#things-to-ask-an-agent) has copy-able requests for sorting an inbox, setting up rules, a morning briefing, unsubscribing and more. The full reference is under[Command line](#command-line-for-agents-and-scripts) .\n\n| Reading pane beside the list | Search as you type | \n|---|---|\n\nThe mail in these screenshots is made up. You can open the same demo yourself with `npm run demo` (see [Build from source](#build-from-source)).\n\nI was frustrated with Outlook, and I never liked how Thunderbird looks. It felt clunky, and it had no good way to see all my mailboxes in one place.\n\nNeither one gave me a free, easy command-line tool either. That is the part I wanted most: now an AI agent can search my mail, clean it up, or do whatever else I ask across every mailbox, for free, on my own machine.\n\nSo Qmail is two things that share one local mail store: an app that works the way Gmail does, and a CLI built for agents.\n\nQmail is developed and tested on Windows. macOS and Linux builds are produced automatically and are **new and untested**; please report problems.\n\n1. Download `Qmail-Setup-x.y.z.exe` from the[Releases page](https://github.com/mattgauzza/quickMail/releases/latest) (or build it yourself, see below).\n2. Run it. It installs for your Windows user only, with no administrator prompt, and adds Start menu and desktop shortcuts.\n3. Windows may show a \"Windows protected your PC\" notice because the installer is not code-signed. Choose **More info → Run anyway** .\n\nThe first time the app starts it also makes the `qmail` command available in any new terminal window (the earlier name, `quickmail`, still works). Nothing else (Node.js, etc.) needs to be installed.\n\n1. \nDownload `Qmail-x.y.z-mac-arm64.dmg` from the Releases page, open it, and drag Qmail to Applications.\n2. \nThe app is not signed with an Apple developer certificate, so macOS refuses to open it at first. Run this once in Terminal, then open the app normally: \n\n```\nxattr -cr /Applications/Qmail.app\n```\n\n3. \nPasswords are kept in the macOS Keychain. The `qmail` command is placed in`~/.local/bin` the first time the app starts; add that folder to your`PATH` if it is not there already.\n\nAutomatic updates do not work on macOS (Apple only allows them for signed apps); download the new `.dmg` to update. Intel Macs are not built yet.\n\n- **Debian / Ubuntu** : download`Qmail-x.y.z-linux-amd64.deb` and install it with`sudo apt install ./Qmail-*.deb` . The`qmail` command is placed in`~/.local/bin` on first start.\n- **Any distribution** : download`Qmail-x.y.z-linux-x86_64.AppImage` , make it executable (`chmod +x` ), and run it. The AppImage updates itself; it does not provide the`qmail` command.\n\nPasswords are kept in the desktop keyring (GNOME Keyring, KWallet or another Secret Service provider), which must be running.\n\nOn Windows and the Linux AppImage, updates are automatic. Qmail checks this project's GitHub releases when it starts and every half hour, downloads a new version in the background, and shows a small green download icon in the top bar. Click it to restart into the new version, or just close the app and it updates then. (Version 0.1.0 predates this; install 0.2.0 or later once by hand.)\n\nTo remove Qmail, use **Settings → Apps → Installed apps**. Your mail store is left in place; delete the `.quickmail` folder in your user folder to remove that too.\n\nOn first start, Qmail offers to bring over your Thunderbird accounts and contacts. You can also copy the passwords Thunderbird has saved (they go into Windows Credential Manager). Thunderbird itself is not changed.\n\n1. Click **+** next to**Mailboxes** in the sidebar.\n2. Enter your email address, your name, and your password.\n3. Check the two server fields. Qmail guesses them from your address; your mail provider's help pages list the right values (\"IMAP server\" and \"SMTP server\"). Ports 993 and 465 are assumed; add a different one after a colon, for example `smtp.example.com:587` .\n4. Click **Add mailbox** . The mailbox is only kept if the sign-in works.\n\nSome providers (Yahoo, iCloud, and Gmail if you do not use Google sign-in) do not accept your normal password in mail apps. Create an \"app password\" in that provider's account security settings and use that instead.\n\nClick **+**, enter the address, tick **Microsoft 365 / Outlook account**, and click **Add mailbox**. A Microsoft sign-in window opens; no password is typed into Qmail.\n\nNote: Qmail does not yet have its own Microsoft app registration. By default it signs in using the public client ID that Mozilla Thunderbird publishes for the same purpose, so Microsoft's consent screen names Thunderbird. To use your own registration instead, create one in the Azure portal (public client, redirect URI `https://localhost`, delegated permissions `IMAP.AccessAsUser.All`, `SMTP.Send`, `offline_access`) and set the `QUICKMAIL_MS_CLIENT_ID` environment variable to its application ID.\n\nGoogle requires every mail app to identify itself, and does not offer a shared identity for open-source apps. So you create your own free one, once. It takes about five minutes and can be reused for any number of Gmail accounts.\n\n1. \nGo to [https://console.cloud.google.com/](https://console.cloud.google.com/) and sign in with a Google account.\n2. \nCreate a project (top bar → project picker → **New project** ). Any name works, for example`Qmail` .\n3. \nTurn on the Gmail API: open [https://console.cloud.google.com/apis/library/gmail.googleapis.com](https://console.cloud.google.com/apis/library/gmail.googleapis.com) , make sure your project is selected, and click**Enable** .\n4. \nSet up the sign-in screen: open [https://console.cloud.google.com/auth/overview](https://console.cloud.google.com/auth/overview) and click**Get started** .\n  - **App name** :`Qmail`\n  - **User support email** : pick your address from the dropdown\n  - **Audience** :**External**\n  - **Contact information** : your email address\n5. \nUnder **Audience** , click**Publish app** . (If you leave it in \"Testing\", Google signs you out every 7 days.)\n6. \nOpen **Clients** ([https://console.cloud.google.com/auth/clients](https://console.cloud.google.com/auth/clients) ) →**Create client** → application type**Desktop app** →**Create** . Copy the**Client ID** and**Client secret** .\n7. \nOpen a terminal and save them in Qmail: \n\n```\nqmail accounts google-client <client-id> <client-secret>\n```\n\n They are stored in your system's credential store (Windows Credential Manager, macOS Keychain or the Linux keyring).\n\n**Sharing one setup.** You only do the Google Cloud steps once. The same client ID and secret work:\n\n- for every Gmail account you add on that PC;\n- on your other computers: run the `qmail accounts google-client` command there with the same two values;\n- for other people (family, a small team): give them the two values and they run the same command, then add their own Gmail account. They sign in to their own mailbox; the client does not give them any access to yours, and it does not give you access to theirs.\n\nGoogle allows up to 100 different Google accounts to sign in through an unverified app like this. Beyond that, the app has to go through Google's verification. Only share the values with people you trust, since anyone holding them can present their own app to Google under your app's name.\n\n1. In Qmail, click **+** next to**Mailboxes** .\n2. Enter the Gmail address and tick **Gmail / Google account** .\n3. Click **Add mailbox** . Your browser opens on Google's sign-in page.\n4. Google shows \"Google hasn't verified this app\", because the app identity is your own and has not been reviewed by Google. Click **Advanced → Go to Qmail** , then**Continue** .\n5. When the browser says you are signed in, go back to Qmail.\n\nFrom a terminal the same thing is:\n\n```\nqmail accounts add you@gmail.com --google\nqmail accounts login you@gmail.com\n```\n\nIf you would rather skip the setup above: turn on 2-Step Verification for the Google account, create an app password (Google Account → Security → App passwords), then add the mailbox as a normal IMAP mailbox with that app password. Leave the Gmail box unticked.\n\nGmail presents \"All Mail\" and each of your labels as a folder, so one message can exist in several of them. Qmail shows such a message once in lists and search results, downloads it once, and stores it once. Your labels appear as folders in the sidebar.\n\n- **Reading pane** : the button at the far right of the list toolbar puts the message beside the list. Drag the divider to resize.\n- **New window** : hover a conversation and click the pop-out icon, middle-click it, or press`w` .\n- **Search** : type in the search box for instant suggestions; press Enter for all results. Operators:`from:`` to:``subject:`` in:``is:unread`` is:starred``has:attachment`` from:me``newer_than:7d`` older_than:1y` .\n- **Attachments** : PDFs, pictures and text open in a viewer inside Qmail. Documents open in the program Windows uses for them. Files that can run code (`.exe` , scripts, shortcuts,`.html` …) are never opened directly.\n- **Calendar invitations** : a message that carries an invitation shows the event (date, title, organizer) above the message with**Yes / Maybe / No** buttons. Your answer is mailed to the organizer as a standard calendar reply. Qmail has no calendar of its own, so the event is not saved anywhere and conflicts are not checked.\n- **Unread count** : shown on the taskbar button; the button flashes when new mail arrives.\n- **Sorting rules** : the gear icon in the top bar opens Settings, where you add rules such as \"from stripe.com → move to Money/Receipts\". See below.\n\nA rule looks at mail as it arrives in an inbox and files it for you. Open Settings (the gear icon), choose **New rule**, and fill in:\n\n- **Mailbox** : every mailbox, or just one.\n- **From / Subject / Sent to contains** : any part of the text, not case sensitive. Several entries separated by commas mean \"any of these\". When more than one box is filled, all of them must match.\n- **Then** : move to a folder (created if it does not exist; on Gmail this is a label), archive, delete (to Trash), mark as read, or star. Moving and archiving can also mark the mail as read.\n\nHow rules behave:\n\n- They run each time Qmail checks for new mail, which is every minute while the app is open, and on `qmail sync` . Qmail has to be running; nothing happens on the mail server while it is closed, and mail that arrived meanwhile is sorted the next time it starts.\n- Rules are tried from top to bottom and the first one that matches a message wins.\n- Only new mail is touched. To sort what is already in the inbox, use the apply-now (circular arrow) button on a rule, or `qmail rules run` .\n- Starred mail is never moved or deleted by a rule.\n\nPress `?` in the app for this list.\n\n| Key | Action | \n|---|---|\n| `c` | Compose | \n| `/` | Search | \n| `j` /`k` or arrow keys | Next / previous conversation | \n| `Enter` or`o` | Open | \n| `u` or`Esc` | Back to the list | \n| `x` | Select | \n| `*` then`a` /`n` | Select all / none | \n| `e` | Archive | \n| `#` or`Delete` | Delete | \n| `!` | Report spam | \n| `s` | Star | \n| `Shift+i` /`Shift+u` | Mark read / unread | \n| `r` /`a` /`f` | Reply / reply all / forward | \n| `w` | Open in a new window | \n| `v` | Reading pane on / off | \n| `g` then`i` /`s` /`t` /`d` | Go to Inbox / Starred / Sent / Drafts | \n| `Ctrl+Enter` | Send while writing | \n\nEvery command accepts `--json` for machine-readable output. Errors exit non-zero; with `--json` they print `{\"error\": \"...\"}`. Run `qmail --help` or `qmail <command> --help` for all options.\n\n```\nqmail accounts                                   # mailboxes and their state\nqmail sync                                       # fetch new mail (the app does this every minute while open)\n\nqmail list --unread --json                       # inbox of every mailbox\nqmail list -a you@example.com -f sent -n 10      # one mailbox, one folder\nqmail search \"invoice from:stripe has:attachment newer_than:14d\" --json\nqmail read 1234 --json                           # one message: text, html, attachments\nqmail thread 1234                                # the whole conversation\n\nqmail send --from you@example.com --to a@b.com --subject \"Hi\" --body \"Text\" --attach ./file.pdf\nqmail send --from you@example.com --reply-to 1234 --body \"Thanks!\"      # add --reply-all\nqmail send --from you@example.com --forward 1234 --to c@d.com\n\nqmail mark 1234 1235 --read                      # or --unread --star --unstar\nqmail archive 1234\nqmail trash 1234\nqmail move 1234 --to Receipts                    # add --create to make the folder (Gmail: the label)\nqmail senders -f inbox -n 50                     # who the mail is from, biggest senders first\nqmail list --oldest -n 100 --json                # oldest first\nqmail list --oldest -n 500 --json | ... | qmail archive -   # \"-\" reads ids from standard input\nqmail attachments 1234\nqmail download 1234 -o ./out                     # -i 0 for just the first attachment\nqmail contacts ava                               # search contacts\nqmail contacts add a@b.com Ava Chen\n\nqmail rules                                      # sorting rules, in the order they are tried\nqmail rules add --from \"stripe.com, paypal\" --move Money/Receipts --read\nqmail rules add -a you@example.com --subject \"newsletter\" --archive\nqmail rules edit 3 --move Receipts               # change only what you pass\nqmail rules disable 3                            # or enable / remove\nqmail rules run --dry-run                        # count what the rules would sort in the inbox right now\nqmail rules run --rule 3                         # apply one rule to mail already in the inbox\n```\n\nAccount setup from the terminal:\n\n```\nqmail accounts import-thunderbird [--passwords]\nqmail accounts add <email> --imap host[:port] --smtp host[:port] [--name \"Your Name\"]\nqmail accounts add <email> --microsoft\nqmail accounts add <email> --google\nqmail accounts login <email>\nqmail accounts remove <email>\n```\n\nNotes for agents:\n\n- Message ids are local numbers from `list` and`search` . A message gets a new id when it moves to another folder.\n- Run `qmail sync` first if the app is not open.\n- `send` really sends. There is no draft or confirmation step.\n- For hundreds of messages, pass ids on standard input (`qmail move - --to X` ) in batches of about 200.\n- `trash` moves to the mailbox's Trash, which the mail provider empties on its own schedule (Gmail: 30 days).`trash --permanent` cannot be undone.\n\nAny agent that can run terminal commands (Claude Code, Codex, Cursor and the like) can use the CLI. Tell it once that `qmail` is installed and that `qmail --help` lists everything, then copy one of these.\n\n**Sort an overflowing inbox into folders**\n\n```\nUse the qmail CLI to clean up my inbox for you@gmail.com.\n1. Run `qmail senders -a you@gmail.com --oldest --scan 5000 -n 300 --json` to see who the oldest 5,000 messages are from,\n   and `qmail folders -a you@gmail.com` to see the folders I already have.\n2. Propose a plan: which senders go to which folder (reuse my folders, add as few new ones as possible),\n   which senders are junk to delete, and that everything else is archived. Leave starred mail alone.\n3. Show me the plan and the list of junk senders, and wait for my OK.\n4. Then get the ids with `qmail list -a you@gmail.com --oldest -n 5000 --json` and apply the plan in batches of 200:\n   `qmail move - --to \"Folder\" --create`, `qmail archive -`, `qmail trash -` (ids on standard input).\n5. Finish with `qmail sync -a you@gmail.com --no-bodies` and `qmail folders -a you@gmail.com`, and tell me the before and after counts.\n```\n\n**Set up sorting rules from what is already there**\n\n```\nLook at `qmail senders -n 200 --json` and `qmail folders`. Suggest sorting rules for the senders that keep filling my inbox\n(receipts, newsletters, notifications), using my existing folders. Show me the list first. For the ones I approve, create them with\n`qmail rules add --from \"<sender>\" --move \"<folder>\"` (add --read for mail I never need to see), check each with\n`qmail rules run --rule <id> --dry-run`, and then apply them to the mail already in my inbox with `qmail rules run`.\n```\n\n**Morning briefing**\n\n```\nRun `qmail sync`, then `qmail list --unread --json`. Read anything that looks like it needs me with `qmail read <id> --json`.\nGive me a short list: what needs a reply today, what is only for information, and what I can ignore. Do not mark anything read.\n```\n\n**Unsubscribe candidates**\n\n```\nRun `qmail senders --since 90d -n 200 --json`. List the senders that look like newsletters or marketing, with how many messages each sent,\nbiggest first. For the ones I pick, find the unsubscribe link with `qmail read <id> --json`, and trash their old mail with `qmail trash -`.\n```\n\n**Find a document**\n\n```\nFind the most recent invoice from Acme: `qmail search \"from:acme has:attachment invoice\" --json`,\nthen `qmail attachments <id>` and `qmail download <id> -o ./invoices`. Tell me the amount and due date.\n```\n\n**Draft replies for me to approve**\n\n```\nFor each unread message in `qmail list --unread --json` that is a direct question to me, read the thread with `qmail thread <id>`\nand write a reply. Show me every reply first. Send only the ones I approve, with\n`qmail send --from <the mailbox it arrived in> --reply-to <id> --body-file reply.txt`.\n```\n\n**Clear out old noise**\n\n```\nRun `qmail search \"older_than:2y is:read in:inbox\" -n 2000 --json`. Group by sender, show me the groups, and archive the ones I agree to with `qmail archive -`.\n```\n\n| What | Where | \n|---|---|\n| Mail store (SQLite database and raw messages) | `.quickmail` in your user folder (override with the`QUICKMAIL_HOME` environment variable) | \n| Passwords, sign-in tokens, Google client | Windows Credential Manager (macOS Keychain, or the Linux desktop keyring), under \"quickMail\" (the app's earlier name) | \n\nNothing is uploaded anywhere. Removing a mailbox in Qmail deletes its local copy only; nothing is deleted on the mail server.\n\nRequires Node.js 22 or newer on Windows.\n\n```\nnpm install\nnpm run demo         # try the app with made-up mail: no accounts needed, nothing is sent or synced\nnpm run dev          # run the app with live reload\nnpm run build        # build the app and the CLI\nnpm start            # run the built app\nnpm link             # optional: put qmail on PATH from this folder\nnpm run dist         # build the installer into release/\nnpm run release      # maintainers: tag the version in package.json; GitHub Actions builds and publishes all installers\n```\n\nLayout:\n\n- `src/core` : the mail engine shared by the app and the CLI (IMAP sync, SMTP send, local store, sign-in, Thunderbird import)\n- `src/cli` : the command line\n- `src/main` ,`src/preload` ,`src/renderer` : the Electron app (React)\n- `scripts/make-icon.cjs` : regenerates`resources/icon.png` (`npm run icon` )\n\nEnvironment variables: `QUICKMAIL_HOME` (store location), `QUICKMAIL_MS_CLIENT_ID` (use your own Microsoft app registration instead of the default public one), `QUICKMAIL_GOOGLE_CLIENT_ID` / `QUICKMAIL_GOOGLE_CLIENT_SECRET` (instead of `accounts google-client`).\n\n- **A mailbox shows a red \"!\"** : its sign-in failed or expired. Use the banner at the top of the list to sign in again.\n- **\"Google sign-in is not set up yet\"** : do the one-time setup under[Adding a Gmail account](#adding-a-gmail-account) .\n- **Google signs me out every week** : the Google project is still in \"Testing\". Publish it (step 5).\n- **`qmail` is not recognised** : start the app once, then open a new terminal window.\n- **Wrong server settings** : remove the mailbox (`qmail accounts remove <email>` ) and add it again.\n\nQmail is free and open source. If it is useful to you, you can support its development through [GitHub Sponsors](https://github.com/sponsors/mattgauzza) (also the heart button in the app's top bar).\n\nMIT. See [LICENSE](https://github.com/mattgauzza/quickMail/blob/main/LICENSE).", "url": "https://wpnews.pro/news/show-hn-qmail-a-local-mail-client-with-cli-tools-for-your-agent", "canonical_source": "https://github.com/mattgauzza/quickMail", "published_at": "2026-11-12 13:15:54+00:00", "updated_at": "2026-11-12 13:21:30.171836+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products"], "entities": ["Qmail", "qmail", "Microsoft 365", "Outlook", "Gmail", "Thunderbird", "macOS", "Windows"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-qmail-a-local-mail-client-with-cli-tools-for-your-agent", "markdown": "https://wpnews.pro/news/show-hn-qmail-a-local-mail-client-with-cli-tools-for-your-agent.md", "text": "https://wpnews.pro/news/show-hn-qmail-a-local-mail-client-with-cli-tools-for-your-agent.txt", "jsonld": "https://wpnews.pro/news/show-hn-qmail-a-local-mail-client-with-cli-tools-for-your-agent.jsonld"}}