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.
Features
Janus runs silently in the background, doing the triage work you don't want to do manually.
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.
Emails scoring 3+ land in your Google Chat digest. Everything below is quietly archived and labelled — still searchable, never in your face.
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.
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.
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.
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.
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.
How it works
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.
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.
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.
All threads scoring urgency ≥ 3 are bundled into a single Google Chat message — not one ping per email. Low-urgency threads are archived silently.
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.
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.
Setup
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.
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
Janus needs OAuth access to your Gmail. Create a Desktop app client on Google Cloud.
credentials.json.envDownload 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
token.json — needs a browserRun 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
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
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/
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
What's new
Recent changes across releases.
All notable changes to Janus are documented here.
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