Your inbox, triaged and drafted by AI that runs entirely on your own hardware β no OpenAI, no vendor lock-in, no API bill.
Features β’ Architecture β’ Quick Start β’ Production Guide β’ Security β’ Author
SYJ Mail Intelligence AI watches your Gmail inbox, classifies every message, scores it for importance, summarizes it, and drafts a reply in your own writing style β then auto-sends it only above a confidence threshold you control. Everything else waits for your one-tap approval.
It's model-agnostic by design β there's no OpenAI dependency anywhere in the stack. It runs entirely against local, open-weight models through Ollama (DeepSeek V3/R1, Qwen 3, Qwen Coder, Mistral, Llama 3), and swapping providers is a config change, not a code change.
This isn't a weekend script β it's shipped through four real phases (core pipeline β dashboard β production hardening β an audit-driven reliability pass), each tested against live infrastructure rather than assumed to work.
π¬ Demo: drop a short screen recording at
assets/demo.gif(1280Γ720, <10s, showing an email arriving β getting classified β a reply drafted) and reference it here as. Skipped in this README rather than linking a placeholder that doesn't exist yet.
- π§ AI email classification β 22 business-relevant categories (
Urgent,Client,Invoice,Security Alert,Phishing,Meeting, and more), tuned to avoid the classic false-urgency trap of marketing copy - π Importance scoring β 1β100 score with plain-English reasoning and automatic deadline detection
- βοΈ Style-aware reply generation β drafts in your writing style, with echo/paraphrase detection that forces a stricter regeneration before ever falling back to a safe template
- β Confidence-gated approval workflow β auto-send, Gmail draft, or manual review, decided in plain Python (not just prompted), so a model can't talk its way past your thresholds
- π¬ Full Gmail integration β polling, thread detection, draft creation, sending, archiving, read-state sync
- π Telegram notifications β with Markdown-escaping so special characters never silently break a message
- π₯οΈ Next.js dashboard β inbox, approval queue, contact intelligence, analytics, live prompt editor, and system logs, JWT-authenticated end to end
- π Production-grade security β API-key auth on every route, a server-side proxy so the key never touches the browser, rate limiting, and Postgres isolated on an internal network
- π§© Truly model-agnostic β swap
LLM_MODELin.envto run DeepSeek, Qwen, Mistral, or Llama β zero code changes - π± Runs anywhere β Windows, Linux, macOS, and natively in Termux on Android
| Phase | Status | What Shipped |
|---|---|---|
| Phase 1 | β | Core pipeline β Gmail polling, classification, importance scoring, Telegram notifications, style-learned reply drafting, the 95%/80% approval workflow. Termux-runnable, SQLite by default. |
| Phase 2 | β | Next.js dashboard β Inbox, Important, Approval Queue, Notifications, Analytics, Contacts, Prompt Editor, Logs, Settings. |
| Phase 3 | β | Production hardening β API-key auth, server-side dashboard proxy, rate limiting, Postgres + Alembic, Docker Compose + Nginx/HTTPS, systemd path, GitHub Actions CI, pytest suite. |
| Phase 3.1 | β | Audit-driven reliability pass β see below. |
π What Phase 3.1 actually fixed (click to expand)
- Gmail is no longer a hard dependency to start β the API and dashboard run fully with zero Gmail credentials
- The poller reconnects with capped backoff instead of crashing outright
- A single
GET /readyendpoint reports real DB + Gmail readiness - The AI provider is a true singleton, reusing one persistent HTTP connection instead of one per call
- Prompt edits from the dashboard take effect without a restart
- A failing AI call at any stage degrades to a safe default and flags the email
needs_manual_review - Telegram notifications escape Markdown special characters instead of silently failing
Covered by tests/test_pipeline_resilience.py and tests/test_provider_singleton.py.
π§ Not yet built (by design, not by accident)
- RAG over past sent emails
- Redis/Celery for higher-throughput async processing
- Multi-Gmail-account support
- WhatsApp/Slack/Discord notification channels
- A live Model Manager UI (today:
.env+ restart)
flowchart TD
A[π₯ Gmail API<br/>OAuth2 Poller] -->|new message| B[βοΈ Pipeline Orchestrator]
B --> C[π·οΈ Classifier<br/>category Β· confidence Β· reason]
B --> D[π Importance Scorer<br/>1-100 Β· sender rep Β· deadlines]
B --> E[π Summarizer<br/>1-line Β· detailed Β· action items]
C --> F[βοΈ Reply Generator<br/>style profile + tone selection]
D --> F
E --> F
F --> G{Confidence Router}
G -->|"β₯ 95%"| H[π Auto-Send<br/>via Gmail API]
G -->|"80β94%"| I[π Gmail Draft<br/>+ Telegram approval request]
G -->|"< 80%"| J[ποΈ Manual Review<br/>dashboard only]
H --> K[π Telegram Notification]
I --> K
J --> K
K --> L[(ποΈ Postgres / SQLite)]
L --> M[π FastAPI REST Backend]
M --> N[π₯οΈ Next.js Dashboard<br/>JWT-authenticated]
style A fill:#4285F4,color:#fff
style H fill:#2EA44F,color:#fff
style I fill:#F59E0B,color:#fff
style J fill:#EF4444,color:#fff
style N fill:#7C3AED,color:#fff
Every AI stage runs in its own try/except. A failure in classification, scoring, summarization, or reply generation never drops the email β it falls back to a safe default, flags the message for manual review, and logs the exact failure reason.
sequenceDiagram
participant Browser
participant Dashboard as Next.js Server Proxy
participant API as FastAPI Backend
participant DB as Postgres / SQLite
Browser->>Dashboard: Request (no API key attached)
Dashboard->>API: Forwarded request + X-API-Key (server-side only)
API->>API: Verify key
API->>DB: Query
DB-->>API: Result
API-->>Dashboard: JSON response
Dashboard-->>Browser: JSON response
The API key never reaches client-side JavaScript β see Security for why this matters.
| Layer | Technology |
|---|---|
| Backend | Python 3.11+, FastAPI, SQLAlchemy, Alembic, Uvicorn |
| AI / LLM | Ollama β DeepSeek V3/R1, Qwen 2.5/3, Qwen Coder, Mistral, Llama 3 (fully swappable) |
| Frontend | Next.js 14+, React, TypeScript, Tailwind CSS |
| Database | SQLite (dev) β PostgreSQL + Alembic (production) |
| Auth | JWT (HTTP-only cookies) + API-key auth, Google OAuth2 (Gmail) |
| Notifications | Telegram Bot API |
| Deployment | Docker Compose + Nginx/HTTPS, or systemd (non-Docker), GitHub Actions CI |
| Requirement | Version / Notes |
|---|---|
| Python | 3.11+ |
| Node.js | 18+ (for the dashboard) |
| Ollama | Latest β local LLM inference |
| RAM | 8 GB minimum Β· 16 GB+ recommended for 14B-class models |
| Disk | ~10 GB free for models |
| Gmail account | With API access enabled via Google Cloud Console |
Local development β SQLite, no auth required.
1. Clone and set up the backend
git clone https://github.com/SHalimoosavi/syj-mail-intelligence-ai.git
cd syj-mail-intelligence-ai
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env2. Add Gmail credentials
Go to Google Cloud Console β APIs & Services β Credentials β Create OAuth client ID β Desktop app, download the file, and place it in the project root as credentials.json.
3. Run the one-time OAuth flow, then start the backend
python -m app.gmail.auth # one-time OAuth2 flow
python main.py # starts the poller + API on :80004. Start the dashboard (in a second terminal)
cd dashboard
npm install
cp .env.local.example .env.local
npm run dev # dashboard on :30005. Verify it's running
curl http://localhost:8000/healthYou should get a 200 OK. Open http://localhost:3000 for the dashboard.
π± Running in Termux (Android)
pkg update && pkg upgrade -y
pkg install python git nodejs -y
bash scripts/setup_termux.shThen follow the same steps as Quick Start above. If your phone can't comfortably run a 7B+ model, point OLLAMA_HOST at a LAN or Tailscale machine β most people do this, since Android backgrounding also makes Termux unsuitable for true 24/7 operation. See Making It Production-Ready for a proper deployment path.
Note:
next buildisn't currently supported on Android/Termux (the SWC binary isn't available forandroid/arm64).next devandnpx tsc --noEmitboth work fine β runnext buildon Vercel, GitHub Actions, Docker, or WSL2.
A step-by-step checklist, in the order you'd actually run it.
python3 -c "import secrets; print(secrets.token_urlsafe(32))"Put the output in .env as API_KEY=... and set ENVIRONMENT=production. Without this, the backend refuses to start β since it can send email on your behalf, it must never be reachable unauthenticated. Add the same key to dashboard/.env.local as BACKEND_API_KEY; the browser never sees it, only the dashboard's own server-side proxy does.
# create a database, then:
DATABASE_URL=postgresql://user:pass@host/dbname
alembic upgrade headRun alembic upgrade head on every deploy after a model change, and alembic revision --autogenerate -m "describe the change" to create the next migration.
| Option | Best for |
|---|---|
π³ Docker Compose (deploy/README.md) |
Postgres + backend + dashboard + Nginx in one docker compose up -d, HTTPS via Certbot |
βοΈ systemd, no Docker (deploy/README-systemd.md) |
Same result, managed as three systemd units |
Both put Nginx as the public entry point; neither exposes FastAPI or Postgres directly to the internet.
.github/workflows/ci.yml runs on every push: backend tests against a real Postgres service container, an Alembic migration check, and a full dashboard npm run build. Nothing merges if any of those fail.
scripts/prune_logs.py prunes logs table rows older than N days (default 30). Both deploy paths install it as a scheduled job.
| Mechanism | What It Prevents |
|---|---|
X-API-Key required on every route except /health |
Unauthenticated access to a service that can send email on your behalf |
Server-side dashboard proxy (BACKEND_API_KEY, never NEXT_PUBLIC_) |
The API key ever reaching client-side JavaScript |
CORS locked to CORS_ALLOW_ORIGINS |
Unauthorized browser-based clients |
Rate limiting on approve/reject routes (slowapi) |
Abuse of the approval endpoints |
Path-traversal-safe /prompts/{name} routes |
Arbitrary filesystem access via prompt names |
| Postgres on an internal Docker network | Direct external access to the database |
| OAuth2-only Gmail auth, tokens never committed | Plaintext credential exposure |
Recommended: chmod 600 .env token.json credentials.json on any host you deploy to.
| Endpoint | Auth | Purpose |
|---|---|---|
GET /health |
None | Liveness only β "is the process running." |
GET /ready |
None | Readiness β runs a real DB query and reports Gmail's connection state. Returns 503 if the database is unreachable. |
Use /ready for Docker/systemd health checks and load balancer probes; use /health where you want a lightweight "process alive" check with no DB round-trip.
pip install -r requirements.txt -r requirements-dev.txt
pytest tests/ -v19 tests, covering:
- β Approval-threshold logic (auto-send vs. approval-queue vs. draft-only, including exact boundaries)
- β API-key enforcement end-to-end
- β AI-provider singleton behavior
- β Per-stage AI failure isolation in the pipeline
- β Telegram Markdown escaping
Runs against SQLite locally and against real Postgres in CI.
LLM_PROVIDER=ollama
LLM_MODEL=deepseek-r1:14b # or qwen2.5:14b, qwen2.5-coder:14b, mistral, llama3.1
LLM_FALLBACK_MODEL=qwen2.5:7b # used if the primary model errors or times outNo OpenAI dependency exists anywhere in the codebase β every model runs locally through Ollama.
| Confidence | Action |
|---|---|
| β₯ 95% | Auto-send reply via Gmail API |
| 80β94% | Draft created, Telegram approval request sent, waits for your review |
| < 80% | Draft only, saved to the database, never sent, never touches Gmail |
Enforced in plain Python (app/workflows/pipeline.py::_handle_reply_confidence) β not just prompted β so a model misreporting its own confidence can't bypass it.
- π RAG over past sent emails (
sqlite-vecor Chroma, Ollama embeddings β no OpenAI) - β‘ Redis + Celery for real async/concurrent processing at higher volume
- π§ Multi-Gmail-account support
- π¬ WhatsApp / Slack / Discord / Desktop notification channels
- ποΈ Live Model Manager in the dashboard (swap
LLM_MODELwithout a restart)
A solo, full-stack technical founder building across AI/SaaS, cybersecurity, blockchain, and business automation β end to end, from backend architecture to deployment to the docs you're reading right now. Core stack: Python, FastAPI, Next.js, React, TypeScript, PostgreSQL, Docker.
This project reflects a broader building philosophy: no phase is marked "done" until it's been run against real infrastructure β a live Postgres instance, a live Docker-equivalent stack, an actual main.py run with credentials deliberately removed. If this README says something is tested, it's because it was tested.
| Project | What It Is |
|---|---|
| SYJ GST Invoice Reconciliation | Production-ready GST invoice reconciliation engine β duplicate detection, GSTIN matching, multi-sheet Excel reporting. 137 tests, ~90% coverage. |
| SYJ NexusIntel AI | Multi-tenant enterprise SaaS β CRM, revenue intelligence, RBAC, subscriptions, audit logging. |
| SYJ Media Tools | Social media downloader β GitHub Pages frontend, FastAPI + yt-dlp backend. |
| Sayanjali OSINT β Sentinel Intelligence | Threat intelligence aggregation from VirusTotal, Shodan, AbuseIPDB, AlienVault OTX. |
| NexusRank AI | AI-powered SEO/GEO SaaS β async FastAPI backend, multi-provider AI integration, Stripe billing. |
| Real Estate CRM SaaS | Multi-tenant AI-powered CRM with WhatsApp automation. |
| SYJ MOMENTUM | Unified B2B automation β WhatsApp, LinkedIn outreach, review monitoring, GST compliance. |
Full, up-to-date list: github.com/SHalimoosavi?tab=repositories
MIT License β see LICENSE.
Model-agnostic. Self-hosted. Actually tested against real infrastructure.
Built to run anywhere. Shipped like a product.