Gmail AI Triage · Powered by Gemini · LangChain
v 1.1.0 — 2026-05-13

Your Gmail,
intelligently managed

Janus is a self-hosted Gmail triage assistant. It monitors your Gmail inbox, analyses every thread with an LLM, and surfaces only what truly needs your attention — the rest is handled automatically.

Get started → See how it works
📥 Gmail
🔍 Filter
🤖 LLM Analysis
📊 Urgency score
💬 Google Chat

Everything your inbox
should do on its own

Janus runs silently in the background, doing the triage work you don't want to do manually.

🧠

LLM-powered triage

Every thread is analysed by Gemini (or a local Ollama model). You get a classification, an urgency score from 1 to 5, a summary, and a suggested reply — all in one pass.

Urgency-based routing

Emails scoring 3+ land in your Google Chat digest. Everything below is quietly archived and labelled — still searchable, never in your face.

🚫

Mailing list filter

Janus detects newsletters, bots, and no-reply senders automatically. It learns your patterns over time, adding them to an exclusion list so they're never processed again.

📋

Consolidated digest

Instead of a ping per email, Janus sends a single structured report at configurable times. You see everything at a glance — urgency stars, classification, who it's from, what it's about.

✏️

Draft reply suggestions

When the LLM detects an email that needs a response, it drafts a reply body and includes it in the notification. You can copy it or ignore it.

🔒

Runs on your own infra

Janus is a Docker container that you deploy on your own server. Your emails never leave your Google account — only metadata and content summaries hit the LLM API.

🏷️

Feedback via Gmail labels

Disagree with a score? Apply janus/urgent or janus/not-urgent to any email in Gmail. Janus picks it up on the next run, records the correction, and clears the label — no CLI needed.

Five steps, fully automated

1

Poll Gmail for new messages

Every 5 minutes on weekdays (every 30 on weekends), Janus fetches unread messages under the janus label since the last run. Only messages where you are a direct recipient (To / CC) move forward.

2

Filter noise

No-reply addresses, newsletters, and known mailing lists are stripped out before hitting the LLM. Detected mailing lists get a janus-ml label and are archived automatically.

3

Batch LLM analysis

Threads are grouped and sent to the LLM in batches of 10. The model returns classification, urgency (1–5), a summary, whether a reply is needed, and a suggested reply body.

4

Send a consolidated report

All threads scoring urgency ≥ 3 are bundled into a single Google Chat message — not one ping per email. Low-urgency threads are archived silently.

5

Save state and repeat

Processed thread IDs and timestamps are persisted locally so the next run only fetches what's truly new. A digest report is also sent at 8:30, 11:30, 15:30, and 17:30 on weekdays.

6

Correct scores with Gmail labels

If an email was scored wrong, apply janus/urgent or janus/not-urgent directly in Gmail. On the next run Janus reads the label, records the correction in feedback.json, and removes the label — no CLI or separate interface required.

Janus digest — Google Chat

Two phases:
laptop, then server

The OAuth token must be generated on your laptop (it needs a browser). Then you copy a few files to the server and you're done.

I'm lazy — one command

Have Docker, ssh, and scp? Just run this on your laptop and follow the prompts.

The script guides you through the full setup — OAuth credentials, API keys, auth flow, and server deployment — all in one shot.

bash <(curl -fsSL https://raw.githubusercontent.com/esseti/janus/main/setup.sh)

Or follow the steps manually

Phase 1 — On your laptop

Step 01

Create Google OAuth credentials

Janus needs OAuth access to your Gmail. Create a Desktop app client on Google Cloud.

  1. Open Gmail API and click Enable
  2. Go to CredentialsCreate Credentials → OAuth client ID
  3. Choose Desktop app, give it a name, click Create
  4. Click Download JSON — save it as credentials.json
Step 02

Grab the bootstrap files & fill in .env

Download the two config files and edit .env with your keys.

mkdir -p ~/janus && cd ~/janus
curl -fsSLO https://raw.githubusercontent.com/esseti/janus/main/docker-compose.yml
curl -fsSLO https://raw.githubusercontent.com/esseti/janus/main/.env.example
mv .env.example .env
# minimum required in .env
GEMINI_API_KEY=...
GOOGLE_CHAT_WEBHOOK=...
USER_EMAIL=you@example.com
Step 03

Generate token.json — needs a browser

Run the auth container locally. It opens a callback on localhost:8080, catches the OAuth redirect, and writes token.json. Docker is the only requirement.

docker compose run --rm --service-ports auth

Open the printed URL in your browser, sign in, and authorise Janus. Done when you see "Authentication successful".

Phase 2 — On the server

Step 04

Install Docker on the server

Docker Compose is included since Docker Engine v20.10 — no separate install needed.

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# log out and back in, then verify:
docker compose version
Step 05

Copy files from your laptop to the server

You need three files on the server. credentials.json stays on your laptop — it is not needed on the server.

ssh user@server 'mkdir -p ~/janus'
scp ~/janus/docker-compose.yml user@server:~/janus/
scp ~/janus/.env               user@server:~/janus/
scp ~/janus/token.json         user@server:~/janus/
Step 06

Run the setup script

Run these commands on the server. The script pulls the image, initialises state files, and installs the crontab. After this, Janus runs automatically.

cd ~/janus
curl -fsSLO https://raw.githubusercontent.com/esseti/janus/main/server-setup.sh
chmod +x server-setup.sh && ./server-setup.sh

Changelog

Recent changes across releases.

Changelog

All notable changes to Janus are documented here.

[1.1.0] — 2026-05-13

Bug Fixes

  • Fixing setup and relasing 1.0.0

[1.0.0] — 2026-05-12

Bug Fixes

  • Fix layout

  • Fix exit when exception

  • Use localhost instead of 0.0.0.0 for OAuth redirect URI

  • Correct OAuth server address in log message

  • Mount data directory instead of token.json to avoid Docker dir/file conflict

  • Custom OAuth callback server to bind 0.0.0.0 with localhost redirect URI

  • Pass full authorization_response URL to preserve PKCE code_verifier

  • Use serve_forever with threaded shutdown for reliable OAuth callback

  • Remove duplicate HTTPServer instantiation causing address in use error

  • OAuth flow fixes and updated setup documentation

  • Restore main branch trigger for Docker workflow

  • Only shutdown OAuth server after receiving valid authorization code

Documentation

  • Update master→main URLs and credentials setup instructions
  • Clarify Gmail focus and add credentials generation instructions
  • Add Google OAuth credentials creation step to setup guide

Features

  • Multi-platform Docker build and explicit credentials volumes

Miscellaneous

  • Remove .claude worktrees from git tracking

Full changelog on GitHub →