Files
Inboxintel/docs/discovery/01-architecture-review.md
T
cesnimda 4ce2df0a2b
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 14s
Deploy Staging / deploy (push) Successful in 18s
CI / backend (pull_request) Successful in 52s
CI / frontend (pull_request) Successful in 15s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 55s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 54s
docs: discovery blueprint + multi-provider design (#8)
2026-07-01 19:59:53 +02:00

5.3 KiB

01 — Architecture Review (current state)

Phase 1 deliverable: a grounded assessment of InboxIntel as it exists today, from the source. This is the baseline the redesign builds on.

Architecture

Clean Architecture, .NET 8 — strict dependency rule Api → Infrastructure → Application → Domain.

Layer Responsibility Key contents
Domain Entities + enums, no external deps Email, Sender, Domain, MailThread, Label/EmailLabel, Attachment; EmailCategory
Application Interfaces, DTOs, validation, parsing ISearchService et al., GmailQueryParser, FluentValidation
Infrastructure EF Core/Npgsql, integrations, workers SearchService, CleanupService, AnalyticsService, HeuristicClassifier, AI providers, exports, GmailSyncWorker, DigestWorker
Api ASP.NET Core Web API Thin controllers, DI, Serilog, OAuth
Frontend React 18 + Vite SPA Chart.js, react-grid-layout (draggable dashboard), Tailwind
  • Data: PostgreSQL (EF Core + Npgsql). Email designed for 100k+ rows/user; a generated tsvector column backs full-text search.
  • Background: hosted workers — GmailSyncWorker (scheduled sync), DigestWorker (digest).
  • AI seam (already present): IAiProvider with NullAiProvider (disabled), OllamaProvider (local /api/chat), OpenAiProvider (cloud). Contract today is a single CompleteAsync(systemPrompt, userPrompt).
  • Security: Google OAuth2 (read-only Gmail), cookie session + JWT, refresh tokens encrypted via Data Protection API, IDOR-safe global query filters, SSRF egress guard, non-root containers, destructive actions require Confirmed + server preview.
  • Delivery: Docker Compose (Postgres/API/frontend/optional nginx) + Gitea CI/CD.

Feature set

Gmail connect + sync → Postgres · analytics dashboard (draggable widgets) · advanced search (Gmail operators + FTS) · safe bulk cleanup · unsubscribe management (List-Unsubscribe / one-click) · heuristic categorisation (smart folders) · exports (PDF/CSV/JSON) · optional advisory AI · sender/domain aggregation.

Existing search implementation (flagship)

  1. GmailQueryParser (Application) parses from: to: domain: after: before: is:unread|read has:attachment; remaining text → free-text.
  2. SearchService (Infrastructure) composes structured filters as EF WHERE clauses, and free text via Postgres FTS: SearchVector.Matches(PlainToTsQuery('english', term)), where SearchVector = to_tsvector('english', coalesce(Subject,'') || ' ' || coalesce(BodyText,'')).
  3. Results are per-user filtered (IDOR-safe), ordered by SentAtUtc DESC, offset-paginated, projected to EmailSummaryDto.

Strengths

  • Clean, testable layering; DI throughout; 39 tests + CI/CD gate.
  • Real Postgres FTS (generated tsvector), not naïve LIKE.
  • Security-forward (IDOR filters, encrypted tokens, SSRF guard, confirmed destructive ops).
  • AI already decoupled behind IAiProvider — the "no-AI / Ollama / future" requirement is architecturally seeded.
  • Read-only scope + advisory AI = privacy-respecting.

Weaknesses

  • Search is single-mode: ordered by date, not relevance (no ts_rank); no fuzzy/typo tolerance, no semantic/vector search, no grouping, no "why matched," no saved/recent/suggested searches.
  • FTS is English-only and covers only Subject + Body (not sender, attachment names, labels).
  • Sender/domain filters use .Contains()non-sargable ILIKE scans (no pg_trgm).
  • Category is a single heuristic enum — no multi-label, confidence, or learning.
  • IAiProvider is chat-only — no embeddings/classification/extraction contract, so semantic search & structured extraction can't yet be expressed.
  • Threading (ThreadId) exists but isn't surfaced as conversation intelligence.

Technical debt

  • EF global-query-filter vs required Email↔EmailLabel relationship warning (in logs).
  • Hardcoded English FTS config.
  • 678 KB single JS chunk (no code-splitting) — cold-load cost.
  • Relevance-blind ordering.
  • AI interface too narrow for the roadmap.

Performance bottlenecks

  • .Contains() sender/domain → sequential scans (need pg_trgm GIN indexes).
  • Offset pagination (Skip/Take + Count) degrades on deep pages → keyset/cursor.
  • No score-based top-N (no ranking) → sorts full match set by date.
  • Single JS chunk (~219 KB gzip) slows cold loads.
  • Full-mailbox sync latency at 100k+ (batching/Polly present, but per-message fetch).

Documentation quality

Strong for a scaffold: README, docs/ARCHITECTURE.md, OAuth setup, docs/specs/*, AGENTS.md, plus WORKFLOW.md/CHANGELOG; meaningful XML-doc comments. Gaps: no API reference, no ERD/data-model doc, no ADRs, no search/AI design docs — which this discovery produces.

Verdict

A clean, secure, well-tested foundation with a genuine FTS base and an AI seam already in place. The biggest opportunities are exactly where the product wants to win: relevance-ranked, multi-mode, assisted search; a richer AI contract (embeddings/classification/extraction); conversation intelligence; and a modern, approachable UX. None of these require a rewrite — they extend the existing seams.