You chose Proton Mail because your email belongs to you — not to advertisers, data brokers, or model providers. higgs keeps it that way: a local-first AI CLI that classifies, searches, and answers questions about your mail entirely on your own hardware, through Proton Mail Bridge and a local model you host — Ollama by default, or any OpenAI-compatible server like llama.cpp. No cloud inference. No cloud API keys. No telemetry.
$ brew install higgscli/higgs/higgs
Proton's philosophy is that nobody should exploit your data, period — that's why your mail is end-to-end encrypted in the first place. Piping it to a cloud AI API would undo all of that in one request. higgs is built so it can't: mail flows through Proton Mail Bridge on 127.0.0.1, the model runs locally — via Ollama or any self-hosted OpenAI-compatible server such as llama.cpp llama-server — and there is no code path that sends a message anywhere else.
Classification, search, summaries, and Q&A run on a model you host — Gemma 4 via Ollama by default, or any OpenAI-compatible server you run yourself, like llama.cpp llama-server (PM_LLM_BACKEND=openai). No cloud inference, no cloud API keys, no third party ever sees a subject line.
Bridge credentials live in the OS keyring — macOS Keychain, Windows Credential Manager, libsecret — with an AES-256-GCM encrypted-file fallback. Nothing sensitive ever enters a model's context window.
Apache-2.0 open source, zero telemetry — no analytics, no crash reporting, no update pings. Releases are cosign-signed with an SBOM, so you can verify exactly what you're running.
Keeping AI local usually means giving up the polish of cloud tooling. higgs refuses that trade. It's engineered for agents from the first byte: stdout that's always structured data, errors that are typed, exit codes that are an enum, and a tool spec that isn't --help prose.
higgs schema once. A JSON manifest of every subcommand: flags, args, stdout format, exit codes. The schema is the prompt.{"type":"summary"}. One object per line, a guaranteed terminator. No heuristics..error.kind. Typed envelopes with kind, code, reason, message, hint. The envelope is the branching logic.5 imap, prompt the user on 2 auth, surface on 4 config.Everything an agent needs to discover, drive, recover, and branch — built into the binary, not into your system prompt.
$ higgs schema classify { "name": "classify", "short": "Classify messages with a local LLM…", "args": [{"name":"mailbox","default":"Folders/Accounts"}], "flags": [ {"name":"dry-run","type":"bool"}, {"name":"apply","type":"bool"}, {"name":"limit","type":"int","default":"0"} ], "stdout": "ndjson", "exit_codes": [0,2,3,4,5,6,7] }
$ higgs classify --apply INBOX { "error": { "kind": "auth", "code": 401, "reason": "authFailed", "message": "IMAP authentication failed", "hint": "Check PM_IMAP_PASSWORD matches Bridge" } } # exit 2 → agent prompts the user, not the void
# stdout: data. stderr: sanitized human progress — # ANSI escapes, bidi controls, zero-width chars stripped, # safe to feed back into a model's context. {"uid":1842,"suggested_labels":["Orders"]} {"uid":1843,"suggested_labels":["Finance"]} {"type":"summary","classified":2,"errors":0}
| Exit | Kind | Agent strategy |
|---|---|---|
| 0 | success | Parse stdout, continue |
| 2 | auth | Prompt user for credentials |
| 3 | validation | Fix flags or args, retry |
| 4 | config | Surface missing config |
| 5 | imap | Retry with backoff |
| 6 | classify | Check the local LLM server, retry |
| 7 | state | Inspect the state DB |
| 9 | internal | Escalate to user |
Any command that accepts --uid also accepts --uid -: it reads the UID set from stdin — plain UIDs or NDJSON, from which each row's uid field is taken. One command's output is the next one's input, and verify audits the result.
# Archive everything older than July $ higgs search INBOX --before 2025-07-01 | higgs archive INBOX --uid - # Move every non-mailing-list message the classifier saw to Personal $ higgs state query INBOX --is-mailing-list false | higgs move INBOX Folders/Personal --uid - # Then prove the move happened — audit the mailbox against an expected UID set $ higgs search INBOX --before 2025-07-01 | higgs verify INBOX --uid - --expect absent {"type":"summary","checked":312,"violations":0}
The first workload was a local inbox classifier. The contract turned out to be the product — and now the whole mailbox is scriptable by an agent.
searchtyped criteria → NDJSONfetch-and-parsefull messages as JSONthread / threadsconversation groupsattachmentsextract attachment bytesscan-folderslist mailboxeswatchstream change eventsclassifylabel with your local LLMaskagentic Q&A over the mailboxsummarizeNDJSON summariesdigeststructured recent-mail digestextractpull data with a JSON schemamove / archive / trashwith verificationmark-read / flagflip flags in bulkapply-labelswrite classifier resultsunsubscribehonor List-Unsubscribedraft / sendcompose via IMAP/SMTPstate queryquery past classificationsverifyaudit against expected UIDsbackfillrebuild state from logscleanup-labelsconsolidate label setsauthcredentials → OS keyringexportmailbox → mbox / JSONLimportmbox / JSONL → mailboxschemathe manifest itselfcompletionshell autocompletionstdoutalways structured JSONstderrsanitized, model-safeexit codesstable 0–9 enumstatecheckpointed SQLite, resumablePrerequisites for the AI commands: a running Proton Mail Bridge and a local model server — Ollama (default) or any OpenAI-compatible server like llama.cpp llama-server. Everything else just needs Bridge.
brew tap higgscli/higgs brew install higgs
go install github.com/higgscli/higgs/cmd/higgs@latest
git clone https://github.com/higgscli/higgs.git cd higgs && make build
# Point higgs at Bridge and your local model (Ollama by default) $ export PM_IMAP_USERNAME="alice@proton.me" $ export PM_IMAP_PASSWORD="bridge-generated-password" $ export PM_IMAP_HOST="127.0.0.1" PM_IMAP_PORT="1143" PM_OLLAMA_MODEL="gemma4" # Or any OpenAI-compatible server you host, e.g. llama.cpp llama-server $ export PM_LLM_BACKEND="openai" PM_OPENAI_BASE_URL="http://localhost:8080" PM_OPENAI_MODEL="qwen3.6-35b-a3b" # Dry-run against your inbox — preview, apply only when it looks right $ higgs classify --dry-run --limit 20 INBOX $ higgs classify --apply --workers 4 INBOX
Local-first. Zero telemetry. Open source. Schema-discoverable and agent-ready.