feat(sync): add Microsoft Graph read adapter

Add strict /me identity, atomic paged calendar discovery, and resumable primary-calendar v1 delta synchronization through the durable provider cache. Keep opaque continuations on trusted Graph URLs and retain credentials only per operation.

Document that stable Graph v1 delta covers only the primary calendar; secondary calendars still require a v1 reconciliation or beta dependency decision.

Verified: 18/18 normal, 18/18 -Werror, and 18/18 ASan/UBSan tests.
This commit is contained in:
2026-07-18 12:34:21 +01:00
parent 3560a7d23d
commit 0582444c61
9 changed files with 1957 additions and 26 deletions

View File

@@ -240,7 +240,48 @@ read/write/delete failure remains an explicit failure; none triggers plaintext
fallback or partial connected state. An account becomes connected only after
the complete versioned payload has been stored successfully.
Graph requests, account UI, and usable account setup remain absent. After this
boundary passes verification, the Microsoft adapter will call `/me`, discover
calendars, and apply read-only delta pages through the durable cache using only
delegated `Calendars.Read`.
Graph requests, account UI, and usable account setup remain absent at this
boundary. The read adapter uses delegated `User.Read` for `/me`, then delegated
`Calendars.Read` for calendars and events. Neither permission normally requires
administrator consent, although tenant policy can require approval or prevent
user consent.
### Microsoft Graph read boundary
The adapter remains a public desktop, multitenant client for organizational and
personal Microsoft accounts. It takes
an access token for each operation without retaining it; persistent access and
refresh tokens remain solely in the versioned Secret Service payload.
Identity uses `/me`. Calendar discovery consumes every page before atomically
replacing the account's cached calendar set, so a malformed or failed later page
leaves the prior complete listing intact. Local calendar IDs are deterministic
from stable provider, account, and remote identities. Event requests send the
Graph `Prefer: IdType="ImmutableId"` header so the provider identity remains
stable when an item moves within its mailbox. Nocal's calendar-scoped local
event identity remains stable for the lifetime of that cached calendar mapping.
JSON parsing is strict and bounded for identity, calendars, events, and paging
metadata. Error construction excludes authorization headers, access tokens, and
credential-bearing response material. Requests use only HTTPS
`graph.microsoft.com` URLs. Returned `@odata.nextLink` and `@odata.deltaLink`
values are opaque continuation capabilities: Nocal validates their scheme and
host but does not reconstruct or reinterpret their query parameters.
Event synchronization uses half-open UTC windows. Each page's normalized
events, deletion tombstones, and opaque next/delta cursor commit in one cache
transaction. A partial round is therefore durable and resumable, while a cursor
can never advance beyond its corresponding event changes.
Stable Microsoft Graph v1.0 documents `calendarView` delta only for the primary
calendar. The adapter discovers all calendars but delta-syncs events only from
the primary calendar. It does not call beta or an undocumented per-calendar
delta route. Secondary calendars remain an explicit architecture decision:
either reconcile complete v1 calendar-view windows with well-defined deletion
semantics, or accept a beta-specific delta dependency. That choice must be made
and tested before Nocal claims Outlook-equivalent calendar coverage.
There is no account UI or CLI orchestration and no live Microsoft credential
integration yet; tests use injected transports and credentials. Remote writes
remain out of scope. Account setup and orchestration plus the secondary-calendar
reconciliation decision are next.