v1.2.1 · open source · zero telemetry · 100% localhost

AI for your inbox. Nothing leaves your machine.

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
Also via go install or release tarballs · signed releases · every byte stays on 127.0.0.1
higgs — local agent session · 127.0.0.1
# 1. The agent discovers the tool — no prompt-engineered syntax $ higgs schema classify { "name": "classify", "stdout": "ndjson", "exit_codes": [0,2,3,4,5,6,7] } # 2. Then drives it — a local model reads your mail, on your machine $ higgs classify --dry-run --limit 3 INBOX {"uid":1842,"subject":"Your order has shipped","suggested_labels":["Orders"],"confidence":0.94} {"uid":1843,"subject":"Stripe receipt","suggested_labels":["Finance"],"confidence":0.91} {"uid":1844,"subject":"New sign-in to your account","suggested_labels":["Security"],"confidence":0.97} {"type":"summary","classified":3,"errors":0,"skipped":0} # 3. And composes it — NDJSON out pipes straight into --uid - $ higgs search INBOX --before 2025-07-01 | higgs archive INBOX --uid -
Privacy by default

Privacy isn't a promise here. It's the architecture.

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.

local-first AI

The model comes to your mail

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.

keyring

Secrets stay out of the AI entirely

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.

verifiable

Don't trust it. Audit it.

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.

Built for agents

Private doesn't have to mean primitive

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.

The usual workaround

Parse --help with the model and hope it guesses flags correctly, every session, forever.
Regex the stdout because prose and data share a stream, and the format shifts between versions.
Branch on English — "authentication failed" vs "auth error" vs "login incorrect" — to decide whether to retry.
Guess when a stream ends. No terminator, so agents wait on timeouts and heuristics.

The higgs way

Load higgs schema once. A JSON manifest of every subcommand: flags, args, stdout format, exit codes. The schema is the prompt.
Read NDJSON until {"type":"summary"}. One object per line, a guaranteed terminator. No heuristics.
Branch on .error.kind. Typed envelopes with kind, code, reason, message, hint. The envelope is the branching logic.
Retry by exit code. Codes map 1:1 to error kinds: retry on 5 imap, prompt the user on 2 auth, surface on 4 config.
The agent contract

Four primitives. Zero ambiguity.

Everything an agent needs to discover, drive, recover, and branch — built into the binary, not into your system prompt.

01 Discover

$ 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]
}

02 Recover

$ 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

03 Stream

# 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}

04 Branch

ExitKindAgent strategy
0successParse stdout, continue
2authPrompt user for credentials
3validationFix flags or args, retry
4configSurface missing config
5imapRetry with backoff
6classifyCheck the local LLM server, retry
7stateInspect the state DB
9internalEscalate to user
Unix, but typed

Every command composes over pipes

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 toolkit

30+ commands, one contract

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.

Read & search

  • searchtyped criteria → NDJSON
  • fetch-and-parsefull messages as JSON
  • thread / threadsconversation groups
  • attachmentsextract attachment bytes
  • scan-folderslist mailboxes
  • watchstream change events

Understand (local AI)

  • classifylabel with your local LLM
  • askagentic Q&A over the mailbox
  • summarizeNDJSON summaries
  • digeststructured recent-mail digest
  • extractpull data with a JSON schema

Act

  • move / archive / trashwith verification
  • mark-read / flagflip flags in bulk
  • apply-labelswrite classifier results
  • unsubscribehonor List-Unsubscribe
  • draft / sendcompose via IMAP/SMTP

State & safety

  • state queryquery past classifications
  • verifyaudit against expected UIDs
  • backfillrebuild state from logs
  • cleanup-labelsconsolidate label sets
  • authcredentials → OS keyring

Portability

  • exportmailbox → mbox / JSONL
  • importmbox / JSONL → mailbox
  • schemathe manifest itself
  • completionshell autocompletion

Guarantees

  • stdoutalways structured JSON
  • stderrsanitized, model-safe
  • exit codesstable 0–9 enum
  • statecheckpointed SQLite, resumable
Get started

Install in 60 seconds

Prerequisites 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.

Homebrew (macOS & Linux)

brew tap higgscli/higgs
brew install higgs

Go install

go install github.com/higgscli/higgs/cmd/higgs@latest

From source

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
Download v1.2.1 Read the docs

AI for your inbox — on your terms

Local-first. Zero telemetry. Open source. Schema-discoverable and agent-ready.

Install higgs Star on GitHub