Magic Apps

Documentation

macmagic.app

Magical calendar sync

Magical reads every signed-in account's schedule without OAuth. CalendarPoller posts to Google Calendar's internal range endpoint with the partition's own session cookies, one loop per account partition. The payload and its decoder are in calendar-wire-format.md, and notifications.md consumes what this produces.

Why a session cookie poller

Magical already renders Google Calendar in a WebContentsView, so the user is signed in and this transport costs no incremental authentication. Every other transport asks for a second sign-in or fails its own way: a background WebContentsView costs 250MB–350MB of RAM and 800ms–1.5s per navigation, the Calendar API v3 needs OAuth that many Workspace administrators block (../shared/sessions-and-security.md's Why neither app holds an OAuth client), and a private .ics feed is cached for 6 to 24 hours at Google's edge, too stale for an alert five minutes before a meeting.

EventKit is kept as an opt-in secondary source, because it reaches a calendar's default notifications and the iCloud, Exchange and local calendars, none of which the range payloads carry. On the data an alert needs the two are close, since participation status and conference links are both decodable from the wire format.

One request covers the whole 365-day window in roughly 50KB under 150ms. Its failure mode is narrow and loud: Google serialises Protocol Buffers with numeric tags that are stable within a message, so pinned field indices hold, and the realistic breakages are a restructured response envelope or a newly required request parameter, each surfacing as a 4xx, a thrown decode, or a range that suddenly has no events. Plausible but wrong data, the one failure an alert engine cannot tolerate, comes only from the decoder guessing, which calendar-wire-format.md's Pinned indices forbids.

Poller

CalendarPoller mirrors Magimail's atom-poller.ts: it polls Google Calendar's internal web endpoint from the Electron main process, one loop per account partition.

flowchart TD
    Partition["per-account partition (persist:magical_<id>)"] --> Fetch["session.fetch (Chromium network stack)"]
    Fetch --> Endpoint["POST /calendar/u/<index>/sync.prefetcheventrange"]
    Endpoint --> Decoder["calendar-decoder.ts"]
    Decoder --> Cache["CalendarService cache"]
    Cache --> Surfaces["menu bar and Dock"]

CalendarPoller keeps no events: it returns what it read and forgets it. They live in CalendarService.cachedEvents, keyed by account. The poller only reads; it never writes to Google.

Zero-OAuth session mechanism

The protocol below was read from a live signed-in session (fixtures in apps/magical/tests/fixtures/calendar-web-prefetcheventrange.json).

Source contract

Nothing downstream imports the poller. The alert scheduler, the banner dispatcher and the deep-link resolver depend only on the interface below, and CalendarPoller is one implementation of it:

export type SourceHealth = 'ok' | 'stale' | 'signed-out' | 'offline' | 'unreadable';

export interface CalendarEventSource {
  /** Stable across restarts; scopes the cache and the already-notified set. */
  readonly id: string; // 'google:account_personal'
  readonly kind: 'google-session';

  /** The account this source speaks for, for banner attribution. */
  readonly accountId: string;

  events(range: { start: Date; end: Date }): Promise<NormalizedCalendarEvent[]>;

  /** Push where the source supports it; poll-driven here. */
  onChanged(listener: () => void): () => void;

  readonly lastSuccessfulReadAt: Date | null;
  readonly health: SourceHealth;
}

The interface keeps the transport a swap rather than a rewrite.

health is never inferred from an empty result. "Signed in with nothing scheduled" and "cannot read your calendar" look identical in the event list and must not look identical here; a poller with no known calendar ids reports unreadable rather than ok.

offline and unreadable are separate states because they call for opposite messages. A thrown fetch means the request never reached Google, so the calendar is untouched and the last good read still describes it; the poller raises CalendarNetworkError and reports offline. Google answering with something the decoder cannot use means the app is broken rather than the network, and reports unreadable. Collapsing the two would tell a user on a train that Magical could not read their calendar, and tell a user whose wire format has moved to check their wifi.

classifyFailure maps a thrown read to one of the three failure states. Anything it does not recognise is unreadable, the safer error of the two: an outage wrongly called a broken decoder still tells the user something is wrong, where a broken decoder wrongly called an outage sends them after a fault no reconnection will fix.

Polling window and triggers

Window and paging

Triggers

Suspending an inactive account

Magical mirrors Magimail's AccountsManager suspension (inactiveAccountSuspendMinutes):