Skip to content

Desktop: unify data source of truth — migrate from Rust backend to Python backend #6174

@beastoin

Description

@beastoin

The Python backend (api.omi.me) is the mature, production API powering mobile and web — it covers the full business logic stack including Firestore, Redis caching, Typesense search, Pinecone vector DB, Stripe billing, fair-use enforcement, content locking, and webhook integrations. The Rust desktop backend (desktop-backend-hhibjajaja-uc.a.run.app) was built to support the desktop macOS app but only partially reimplements this stack — it talks to the same Firestore but does not cover Redis, Typesense, Pinecone (except screen_activity), Stripe enforcement, fair-use gates, or content locking logic. This creates a split-brain data source of truth: two backends reading/writing the same Firestore with divergent business logic and missing coverage of the surrounding infrastructure.

Every feature added to the Python backend (locking, fair-use, search, billing, caching, vector retrieval) widens the gap. The Rust backend will always lag behind or require expensive parallel maintenance.

Related: #6065 (subscription routing fix), #5393 (remove client-side API keys), #5396 (move proactive AI to backend), #5882 (fair-use limits)

Current Behavior

  • Desktop routes ALL data CRUD through the Rust backend, except 3 subscription/payment endpoints (fix(desktop): route subscription/payment endpoints to Python backend (fixes #6065) #6068)
  • Rust backend connects to Firestore only — missing integration with:
    • Redis — caching, rate state, fair-use counters, session data
    • Typesense — full-text search with lock filtering
    • Pinecone — vector similarity for memories/conversations (except screen_activity)
    • Stripe — subscription enforcement, plan checks, payment gating
    • Content locking — no redaction, no 402, no truncation for locked content
    • Fair-use engine — no credit tracking, no DG budget gates, no enforcement stages
    • Webhook dispatch — no integration/developer webhook filtering for locked content
    • Knowledge graph — no lock guards during extraction
  • Two backends read/write the same Firestore — but only Python maintains consistency with Redis/Typesense/Pinecone
  • Business logic diverges silently as the mature Python backend evolves and Rust stays Firestore-only
  • Symptoms: free users seeing empty content with no explanation, no upgrade prompts, missing fair-use enforcement, search results not matching mobile, stale caches

Expected Behavior

Desktop macOS app uses the Python backend (api.omi.me) as the single source of truth for all data CRUD. One backend, one set of business rules, full infrastructure coverage (Firestore + Redis + Typesense + Pinecone + Stripe). Desktop-specific concerns (Gemini/Deepgram proxying, screen capture ingest) stay on a thin Rust service.

Affected Areas

Component Current Target
APIClient.swift baseURL Rust backend for all data Python backend for data CRUD
APIClient.swift pythonBackendURL Only 3 subscription endpoints Becomes the primary data URL
Rust backend proxy.rs Gemini + Deepgram proxy Keep — proxy + rate limiting
Rust backend screen_activity.rs Screenshot sync (Firestore + Pinecone) Keep phase 1 — high-volume, desktop-specific
Rust backend config.rs API keys endpoint Keep phase 1 — no Python parity yet
Rust backend data CRUD (25+ modules) Firestore-only partial reimplementation Remove — use Python directly

Solution

Phase 1 — Keep on Rust (thin proxy/ingest only):

  • proxy.rs — Gemini API proxy (rate-limited, keys server-side) + Deepgram REST/WebSocket proxy
  • rate_limit.rs — Rate limiting for Gemini proxy
  • screen_activity.rs — Screenshot metadata + Pinecone embeddings (high-volume, no Python route parity yet)
  • health.rs — Health check
  • config.rs / API keys — Desktop depends on /v1/config/api-keys for runtime keys (no Python parity yet)

Phase 1 — Move to Python backend:
All data CRUD: conversations, memories, action_items, chat, chat_sessions, messages, folders, goals, knowledge_graph, people, personas, apps, users, advice, daily_score, focus_sessions, stats, staged_tasks, updates, webhooks, crisp, agent, llm_usage

Phase 2:

  • Add POST /v1/desktop/conversations/from-segments to Python (desktop-specific conversation creation from local transcription)
  • Migrate screen_activity to Python after benchmarking
  • Migrate config/api-keys to Python
  • Fully deprecate Rust backend CRUD routes

Migration strategy (Codex-recommended):
Route-by-route switchover, not big-bang. APIClient.swift already supports per-call customBaseURL — use this to flip routes incrementally:

  1. Read-only endpoints with clear parity (conversations list, memories list, action items list)
  2. Non-destructive writes (create, update)
  3. Destructive writes (delete)
  4. from-segments (needs new Python endpoint)
  5. screen_activity (after benchmarking)

Add a server-target map and kill switch so any route can revert to Rust instantly.

Parity Gaps to Resolve Before Migration

  • screen_activity — Python has DB layer but no public route
  • config/api-keys — Desktop-specific, no Python equivalent
  • POST /v1/conversations/from-segments — Desktop-specific ingestion, needs new Python endpoint
  • daily_score, stats, llm_usage, crisp — Need parity verification

Impact

  • Single source of truth: Eliminates split-brain — one backend owns all data CRUD with full infrastructure coverage
  • Full stack consistency: Desktop gets Redis caching, Typesense search, Pinecone vectors, Stripe enforcement — not just Firestore
  • Feature parity for free: Desktop automatically inherits every Python backend improvement
  • Reduced maintenance: Stop duplicating and diverging business logic across two languages/backends
  • Auth: No change needed — both backends validate Firebase bearer tokens

Risks

  • Shared Python backend capacity — measure desktop p50/p95 before broad rollout
  • Cold starts on Python deployment for desktop-specific traffic
  • from-segments endpoint needs careful porting to preserve dedup behavior

by AI for @beastoin

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendBackend Task (python)desktopp1Priority: Critical (score 22-29)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions