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.

View File

@@ -169,11 +169,35 @@ parsing, and storage failures are reported distinctly, with no plaintext
fallback and no partially connected account. Connection state is published
only after the complete secret store succeeds.
This phase still does not make Office 365 usable. It contains no Microsoft Graph
call, account UI, or usable account setup. After verification, the next product
slice calls `/me`, discovers calendars, and performs read-only Graph delta
synchronization using delegated `Calendars.Read`. Remote writes remain gated on
the durability and conflict paths being exercised end to end.
The Microsoft Graph read phase keeps Nocal a public desktop multitenant
application for organizational and personal
Microsoft accounts. It requests delegated `User.Read` for `/me` and delegated
`Calendars.Read` for calendars and events. Neither normally requires
administrator consent, although tenant policy may require approval or block
user consent. Access tokens are passed only to the operation that needs them;
Secret Service retains the persistent token set.
Calendar discovery reads every page and publishes an atomic replacement only
after the full listing succeeds. Stable provider/account identity gives each
calendar a deterministic local ID. Event reads request immutable Graph IDs and
use strict bounded parsing with credential-safe failures. Requests and opaque
next/delta links are restricted to HTTPS `graph.microsoft.com` URLs.
Event windows are half-open UTC intervals. Each event page, its deletions, and
its continuation cursor cross the cache boundary in one transaction. A partial
round can resume from its durable cursor, and deletion tombstones remain
explicit instead of silently dropping remote state.
The stable Graph v1.0 `calendarView` delta API is documented only for the
primary calendar. This slice discovers all calendars but delta-syncs only the
primary; it uses neither beta nor undocumented per-calendar routes. Secondary
calendar support requires a deliberate choice between v1 full-window
reconciliation with defined deletion semantics and beta-specific delta. Nocal
does not yet claim Outlook-equivalent coverage.
There is still no account UI/CLI orchestration or live Microsoft credential
integration, and remote writes remain out of scope. Account setup/orchestration
and the secondary-calendar reconciliation decision are the next product phase.
## Local file interchange

View File

@@ -70,11 +70,26 @@ Completed token/secret boundary:
- OAuth secrets only in Secret Service, never SQLite, logs, CLI arguments, or
environment variables
Completed Microsoft Graph read phase:
- Public desktop multitenant organizational/personal account support
- Delegated `User.Read` for `/me` and `Calendars.Read` for calendars/events;
tenant policy may still require approval or block user consent
- Per-operation access tokens with persistent tokens retained only by Secret
Service
- Complete paged all-calendar discovery with atomic cache replacement and
deterministic local IDs
- Immutable Graph event IDs, strict bounded parsing, and credential-safe errors
- HTTPS `graph.microsoft.com`-only opaque continuation/delta links
- Half-open UTC event windows and per-page event/tombstone/cursor transactions
- Resumable primary-calendar v1 delta synchronization
Next provider-boundary slices:
- Microsoft `/me` identity and read-only calendar discovery
- Read-only Graph delta synchronization using delegated `Calendars.Read`
- Generic `SyncProvider` contract and observable sync status
- Account setup and CLI/TUI orchestration with live credential integration
- Choose secondary-calendar behavior: v1 full-window reconciliation with
deletion semantics, or an explicit beta-specific delta dependency
- Generic `SyncProvider` integration and observable sync status
## 0.3 — CalDAV
@@ -89,11 +104,12 @@ Next provider-boundary slices:
ETag-aware remote writes after the conflict boundary is proven
- Account/calendar management UI and per-calendar ANSI identity
The cache, generic HTTP/OAuth boundary, and concrete Linux adapters are
prerequisites, not usable Microsoft 365 integration. The token/Secret
Service phase still provides no Graph call, account UI, or usable account setup.
After it passes verification, the immediate sequence is Microsoft `/me`,
read-only calendar discovery, and Graph delta synchronization.
The Graph read phase discovers all calendars but uses stable v1 delta only for
the primary calendar; it deliberately avoids beta and undocumented
per-calendar routes. No account UI/CLI orchestration or live Microsoft
credential integration exists yet, and remote writes remain out of scope. Do
not claim Outlook-equivalent coverage until the secondary-calendar strategy is
chosen and verified.
## 1.0 — distribution quality