dainvo

Architecture

Dainvo is an Electron desktop app with a local-first calendar backend. The renderer presents data through React and FullCalendar, while the Electron main process owns persistence, provider sync, OAuth, and trusted platform access.

Main Process

The Electron main process is the trusted backend boundary. It owns:

SQLite and provider SDK/network calls must stay in the main process. The renderer must not open database files, call Google/Microsoft/CalDAV directly, or receive OAuth tokens.

Preload

The preload layer exposes a narrow window.calendarApi object to the renderer. The current scaffold uses contextIsolation: true and nodeIntegration: false. Preload is intentionally narrow and does not expose Node, Electron primitives, or raw ipcRenderer; if preload is later bundled into a single file, Electron sandboxing can be revisited.

Responsibilities:

Renderer

The renderer is a React app focused on user experience:

FullCalendar is a renderer and interaction layer only. It fetches visible events through typed application APIs and reports edits through callbacks. The app’s services persist those changes.

Domain and Services

The application service layer contains provider-neutral calendar behavior:

The local database is the source of truth/cache for UI rendering. Provider sync adapters reconcile remote calendars with local records.

Local Recurrence MVP

Local recurring events store an iCalendar-compatible RRULE string on the master event. CalendarService.listEvents expands recurring masters only for the visible requested range and returns synthetic instances for the renderer. Instance-level exceptions and edits are intentionally deferred; editing a recurring event updates the whole series.

The current recurrence expansion uses stored ISO start/end timestamps and UTC date arithmetic. Full provider-grade timezone and DST parity is deferred to the later recurrence/sync milestones.

Provider Adapters

Provider-specific behavior belongs behind adapters:

Adapters map provider-specific calendar and event payloads into provider-neutral domain objects. They must be mockable and must not be called from renderer code.

The CalDAV adapter uses tsdav in the main process for account discovery and calendar-object fetches, then parses VEVENT payloads with ical.js. The current CalDAV implementation is read-only and uses full object reconciliation for later syncs because CalDAV server delta behavior varies by provider.

Data Flow

  1. Renderer asks window.calendarApi for calendars and events.
  2. Preload forwards typed requests to IPC.
  3. Main process validates inputs.
  4. Services read or write SQLite through repositories.
  5. Renderer receives DTOs and renders them with React and FullCalendar.
  6. Provider sync updates SQLite through services, then the renderer refreshes local data.

This keeps rendering, persistence, and sync responsibilities separate.