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:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user