Skip to content

How it works

This page is for the curious — you don’t need any of it to use Shrike, but it explains why the app feels the way it does.

The UI reads exclusively from a local SQLite store with an FTS5 full-text index. The network sync layer writes into that store asynchronously; the interface never reads from the network directly. That’s why scrolling, opening, and searching are instant, and why Shrike keeps working offline — the store is the source of truth the UI renders, and sync just keeps it fresh.

Shrike follows the Elm architecture: state → message → update → view. There is exactly one place where state changes — the update function. The keymap and the command palette both emit the same action type, which is why a keystroke and its palette entry behave identically. Triage actions mutate local state immediately and enqueue a background sync, with rollback on failure and a ⌘Z undo window.

Two async boundaries keep the app decoupled and replaceable:

  • MailProvider — the seam to Gmail. The Gmail implementation does OAuth, then IMAP + SMTP over XOAUTH2, with incremental sync into the store.
  • AiEngine — the seam to the on-device model. The embedded llama.cpp backend sits behind it, so the model and even the inference backend are decoupled from the rest of the app — which is exactly where the in-app model picker plugs in.

The calendar follows the same shape, with a CalendarProvider trait and a Google Calendar adapter that shares the mail OAuth token.

LayerChoice
LanguageRust
GUIiced (wgpu → Metal)
Local storeSQLite (rusqlite, bundled) + FTS5
Mail transportIMAP + SMTP over OAuth XOAUTH2
On-device AIllama.cpp (Metal), GGUF models
PlatformmacOS, Apple Silicon