Case study · personal product
InboxIntel
In developmentA self-hosted inbox intelligence and cleanup tool — semantic search, AI summaries and inbox-health analytics over your mail.
TL;DR
- What
- Inbox intelligence: split-view reading, semantic search, AI summaries, inbox-health analytics and safe cleanup.
- Why
- To practise Clean Architecture properly, and make search useful and destructive operations safe by design.
- Stack
- .NET 10 · PostgreSQL + pgvector · React · Ollama · Docker · Clean Architecture.
- Role
- Sole architect and developer, front to back.
Problem & context
A busy inbox is hard to search and dangerous to clean up — keyword search misses what you meant, and one wrong bulk filter deletes things you can’t get back. I built InboxIntel to make an inbox actually searchable (by meaning, not just words), understandable at a glance, and safe to tidy — all self-hosted, with a clean, testable backend.
Architecture
A React SPA over an ASP.NET Core API built as Clean Architecture — the dependency rule points inward (Api → Infrastructure → Application → Domain), and controllers hold no business logic. A hosted background worker syncs Gmail into PostgreSQL; a local Ollama model generates embeddings (stored with pgvector) and message summaries, which power hybrid semantic + full-text search.
- Reading: a persistent list beside a resizable pane with an AI summary — triage without opening a new tab.
- Search: semantic (pgvector) + full-text, with why-matched highlights and a relevance score.
- Analytics: an inbox-health grade, emails by category, a 90-day volume trend and top senders.
- Cleanup: preview-then-confirm on every destructive action; the AI is advisory only.
Key decisions & trade-offs
-
01 Local Ollama embeddings + pgvector for semantic search.
alt: A hosted embeddings API and a vector database.
Mail is private, so embeddings stay on my own hardware and live right next to the data in Postgres via pgvector — one datastore, no third party, no per-call cost. The trade-off is running the model myself.
-
02 Encrypt OAuth refresh tokens at rest and never log them.
alt: Store them as plain columns.
Refresh tokens are long-lived keys to someone’s mailbox. They’re encrypted with the ASP.NET Core Data Protection API (AES), with keys persisted to a mounted volume — the single most important security decision in the app.
-
03 Every destructive action is preview-then-confirm.
alt: Delete immediately on request.
All cleanup and unsubscribe actions require a server-side preview and an explicit Confirmed flag. The AI layer is advisory only and can never trigger a deletion.
Security & production notes
- Google-OAuth gated; Gmail scopes are read/modify only — no send scope is ever requested.
- Embeddings and summaries are generated locally (Ollama); mail never leaves the box for a third-party model.
- Polly retry/backoff against Gmail rate limits; Serilog structured logging that never records tokens.
- Clean Architecture keeps the layers testable; integration tests boot the API host and assert authorization.
Screenshots
Status & what’s next
In active development. Search, summaries and analytics work end to end; next up is tuning the semantic-recall thresholds, expanding the integration-test suite, and working toward connectors beyond Gmail so it becomes a genuinely universal inbox tool.