unreadherring

Unread Herring

View on GitHub

Unread Herring

“Cut down the mightiest inbox.”

A local-first, open-source Elixir tool that scans your Gmail, renders an interactive sunburst of where your mail comes from (grouped by sender domain, sender, or label), lets you drill down through the rings, and lets you click a wedge to open that exact filtered view in Gmail. Built on Phoenix LiveView; launched from the terminal.

Nothing leaves your machine except calls to the Gmail API itself. No database, no hosted service, no telemetry, no shipped credentials.

Unread Herring dashboard: a sunburst of unread mail grouped by sender domain, with drill-down, per-domain counts and bulk actions

Use at your own risk

This tool bulk-modifies your real mailbox. By using it you accept that you do so entirely at your own risk and that you must know what you are doing:

If any of the above gives you pause, explore the chart and the “Open in Gmail” links only, and leave the Mark read button alone.

How it works

  1. mix herring.serve boots an OTP app with a Phoenix endpoint bound to 127.0.0.1 and opens your browser.
  2. The first time, you are sent through a one-time Google OAuth flow (a loopback redirect back to the same local endpoint). The token is stored in ~/.config/unread_herring/token.json with 0600 permissions.
  3. Pick a grouping (domain / sender / label), scope (unread / all) and time window, hit Scan, and watch the live progress bar while the BEAM fans out concurrent Gmail metadata reads. Scans cover the Inbox by default (matching Gmail’s sidebar badge); untick “Inbox only” to include archived and filtered-away mail. Counts are individual messages, not conversations.
  4. Explore the sunburst: click a wedge to drill down, click the center to go back up, click “Open in Gmail” to see the real filtered view, or bulk mark a bucket read. Mark read asks for confirmation (twice when it targets the whole scan result) and acted-on buckets gray out until the next scan. “Disconnect Gmail” revokes the app’s authorization at Google and deletes the stored token and local scan cache when you are done.

Setup: bring your own OAuth client (required)

This project requires Google credentials to work. You create your own OAuth client in your own Google Cloud project, so your data is only ever between you and Google:

  1. Go to Google Cloud Console and create a project (any name, e.g. unread-herring).
  2. Enable the Gmail API: APIs & Services -> Library -> Gmail API -> Enable.
  3. Configure the OAuth consent screen: External, fill in the app name and your email, and add yourself as a test user. Leave the app in Testing mode - with only you as a user this is exempt from Google’s verification and CASA assessment.
  4. Create credentials: APIs & Services -> Credentials -> Create Credentials -> OAuth client ID -> Application type Desktop app.
  5. Hand the client to Unread Herring either way:
    • download the JSON and save it as ~/.config/unread_herring/credentials.json, or
    • export environment variables:

      export GOOGLE_CLIENT_ID="...apps.googleusercontent.com"
      export GOOGLE_CLIENT_SECRET="..."
      

Note: while the consent screen is in Testing mode, Google may expire refresh tokens after about 7 days; just re-run the auth flow when prompted.

Running

mix deps.get
mix herring.serve     # boots on http://127.0.0.1:4000 and opens your browser

Useful extras:

mix herring.smoke     # prints sender-domain counts to stdout (API smoke test)
mix test              # full test suite; no live Gmail calls anywhere

Limits and tuning

Privacy model

Packaging

Build and run a standard self-contained release:

mix assets.deploy                  # compile + digest assets (prod requires the manifest)
MIX_ENV=prod mix release
PHX_SERVER=1 PORT=4000 _build/prod/rel/unread_herring/bin/unread_herring start

Note: mix release builds for the current MIX_ENV, so a bare invocation produces a dev-mode release under _build/dev/rel/... instead - set MIX_ENV=prod for the real thing.

Development

Standard Phoenix app, no Ecto. See unread-herring-plan.md for the full design document and CLAUDE.md for a condensed architecture overview.

License

See LICENSE.