Provider Implementations and Patches¶
Implementation focus
This page summarizes important provider implementations and highlights non-standard behavior or patches that contributors must preserve.
Provider families¶
| Family | Providers | Notes |
|---|---|---|
| Local | Full Note, Daily Note | Vault-backed, file-centric parsing and persistence. |
| Remote | Google, Outlook, CalDAV, ICS, Google Tasks | Network-backed with auth/protocol handling and staged loading behavior. |
| Integration | Tasks, TaskNotes, Bases | Plugin/API integration with custom semantics beyond simple event files. |
| Virtual | Holidays | Computed on-the-fly from bundled data; no vault file or network backing. |
Key implementation notes¶
Full Note Provider¶
Creates one-note-per-event records, supports full CRUD, and uses robust filename collision handling to avoid destructive overwrites.
Location & Description Mapping: Parses and writes location (geographic/logical address) and description (multiline text) dynamically inside the note's YAML frontmatter block.
Daily Note Provider¶
Parses list items under configured heading and performs line-targeted updates. Implements a persistent locally-allocated uid mechanism ([uid:: N]) instead of legacy deduplication matching, enabling deterministic title edits and O(1) hinted line lookups during sync updates.
Location & Description Mapping: Serializes location and description into Dataview inline attribute format ([location:: My Location] [description:: My Description]) within the daily note bullet list items.
Timed-event serialization is source-configured and provider-owned:
default: writes inline Dataview time fields such as[startTime:: 02:30]and[endTime:: 03:30]dayPlanner: writes a strict prefixed title form:HH:mm - HH:mm Title
Read behavior must stay format-agnostic:
- First prefer inline
startTimeandendTimefields when present. - Only if those fields are absent, fall back to strict
HH:mm - HH:mm Titleparsing. - Do not couple read behavior to the provider's configured write format.
This split allows forward writes to follow the selected provider format while preserving compatibility with older daily-note lines and mixed historical data.
ICS Provider (non-standard hybrid)¶
Single provider supports both remote URLs (http, https, webcal) and local vault .ics files. It is intentionally read-only and normalizes remote/local acquisition into one contract surface.
Timezone and recurrence edge handling rationale is documented in RRULE Timezone Date-Shift Fix.
CalDAV Provider (protocol patch behavior)¶
Uses direct REPORT/GET flow with robust XML namespace handling and fallback retrieval paths when calendar-data is not returned inline. This is intentionally defensive due to server variability.
VTODO & VEVENT Dual-REPORT Architecture¶
- Dual-REPORT Strategy: Under the CalDAV RFC 4791 specification, time-range queries cannot filter for both
VEVENTandVTODOcomponents using a logicalORcondition within a singlecalendar-queryreport, because siblingcomp-filterelements are logically ANDed. To query both component types efficiently and standard-compliantly, the provider executes two sequentialREPORTqueries: one forVEVENTand one forVTODO. - Fallback Avoidance: If a server does not support standard
REPORTqueries and triggers a compatibilityPROPFINDfallback (which fetches all files in the collection), the provider flagsfellBack = trueand skips the secondVTODOREPORTquery entirely, as the fallback has already fetched all objects (both events and tasks) in a single request. - Content Deduplication: All retrieved calendar objects are combined and deduplicated based on their raw ICS payload content to eliminate any overlaps.
- VTODO Parser/Formatter Mappings: The ICS parser handles
VTODOcomponents by mapping them toOFCEventstructures (interpreting completion time orCOMPLETEDstatus, and mappingdueas the end date). The ICS formatter serializes tasks into standard-compliantVTODOelements usingDUEandCOMPLETEDproperties.
Google Provider¶
Uses OAuth-backed authenticated requests, handles recurrence exception/override edge cases (both modified and cancelled instances, for both timed and all-day occurrences, are parsed and merged into the master event's skipDates), and keeps provider-facing payload conversion isolated in parser/auth modules.
Location & Description Mapping: Directly maps the canonical location and description string properties to/from Google's native API event resource fields.
For token and permission boundaries, see API Architecture.
Google Tasks Provider¶
Integrates with the Google Tasks API for complete two-way synchronization of tasks. It shares the existing Google Calendar authentication infrastructure (so connected Google accounts appear once in the integrations settings, but Google Tasks is configured as a separate calendar system).
Key Integration and Behavior Characteristics:
- Zero-Impact Backlog Integration: Satisfies the TaskBacklogProvider interface to automatically feed undated/incomplete tasks into the sidebar backlog view.
- Drag-and-Drop Scheduling: Handles moving undated backlog items onto the calendar by setting their due date.
- Single-Event Mappings: Tasks are strictly mapped to single OFCEvent structures since the Google Tasks API only supports single due dates and does not natively support complex event recurrences.
- Incremental OAuth Scopes: Scopes are kept strictly incremental and separate (CALENDAR_SCOPES vs TASKS_SCOPES), both utilizing the standard OpenID Connect email scope. This allows GoogleAuthManager to query the standardized userinfo endpoint (https://openidconnect.googleapis.com/v1/userinfo) to resolve the user's email address for unique account registration without requesting Calendar permissions for Task-only setups.
Outlook Provider¶
Uses OAuth Authorization Code + PKCE with proxy-backed token and refresh exchange. Parsing and payload mapping are isolated in parser/auth modules similar to Google provider boundaries.
Outlook provider intentionally normalizes nullable Graph recurrence linkage (seriesMasterId) into optional event fields expected by core validation.
Current limitation: recurring single-instance override write path is not yet implemented.
Tasks Provider (non-standard surgical writer)¶
Not a simple calendar source: it integrates with Tasks plugin cache, supports task-completion toggles, time-token parsing in task text, and surgical markdown line rewrites while preserving task metadata patterns.
Full flow and invariants are detailed in Tasks Integration Architecture.
Tasks date-field integration contract¶
The Tasks integration has two explicit date-field settings:
settings.tasksIntegration.backlogDateTargetcontrols which incomplete tasks appear in the Tasks Backlog.settings.tasksIntegration.calendarDisplayDateTargetcontrols which Tasks date marker is used for calendar display and calendar event write-back.
Backlog filtering must use backlogDateTarget, not a hardcoded definition of "undated":
| Target | Backlog filter |
|---|---|
scheduledDate |
Include incomplete tasks without scheduledDate |
startDate |
Include incomplete tasks without startDate |
dueDate |
Include incomplete tasks without dueDate |
Calendar display and write-back must use calendarDisplayDateTarget with no fallback:
| Target | Calendar display | Markdown write-back |
|---|---|---|
scheduledDate |
Only tasks with scheduledDate |
Write or replace ⏳ YYYY-MM-DD |
startDate |
Only tasks with startDate |
Write or replace 🛫 YYYY-MM-DD |
dueDate |
Only tasks with dueDate |
Write or replace 📅 YYYY-MM-DD |
Backlog filter UI entry points must use the same backlogDateTarget setting:
- Settings -> Integrations -> Obsidian Tasks Integration.
- The dropdown in the Tasks Backlog view header.
Changing the setting must save plugin settings and call providerRegistry.refreshBacklogViews() so all open backlog views re-query the provider. Backlog filtering belongs in TasksPluginProvider.getUndatedTasks() because the provider owns the Tasks cache shape and the date-field mapping. UI components should not duplicate that filtering logic.
Calendar event drag/update behavior writes calendarDisplayDateTarget. Backlog drag/drop writes calendarDisplayDateTarget and also writes backlogDateTarget when it differs, so a planned task no longer qualifies as a backlog candidate. Returning a task to the backlog clears both fields when they differ. TasksPluginProvider._taskToOFCEvent() must still read only calendarDisplayDateTarget; do not reintroduce scheduled/due/start fallback priority. Because event-cache contents are derived from the display field, changing calendarDisplayDateTarget may require an Obsidian restart or plugin reload for all open views to fully reflect the new policy.
The openEditModalAfterBacklogDrop setting gates the Tasks plugin edit modal after backlog drops. Its default is false, so the normal drag/drop path stays fast and non-blocking unless the user explicitly opts into the modal.
Tasks time-format contract¶
settings.tasksIntegration.taskDisplayFormatcontrols how timed tasks are serialized back to markdown.- Default is
dayPlannerfor new installs and forward writes. standardremains available as a compatibility/user preference mode.
Serialization modes:
| Mode | Example output |
|---|---|
dayPlanner |
- [ ] 5:00 - 19:00 Task title ⏳ 2026-05-02 |
standard |
- [ ] Task title (5:00 AM-7:00 AM) ⏳ 2026-05-02 |
Parsing must support both schemas regardless of the selected write mode. Do not introduce a read-mode switch tied to taskDisplayFormat.
TaskNotes Provider (provider-owned NLP endpoint)¶
TaskNotes is a plugin-runtime integration provider and intentionally avoids HTTP transport. It reads from TaskNotes cache APIs and writes via TaskNotes service/UI endpoints.
Key contracts:
- Create behavior is provider-owned and configurable by source
dispatchMode. - Default dispatch endpoint is
search(Search + Create selector flow). - Alternate endpoint
createopens TaskNotes create modal directly. - Full Calendar NLP dispatch remains generic; provider decides endpoint semantics.
TaskNotes create delegation path:
1. NLP resolves target and calls EventCache.addEvent (1)
2. Registry routes to TaskNotesProvider.createEvent (2)
3. Provider opens UI and throws DelegatedProviderActionError (3)
4. Cache handler rolls back optimistic state (4)
- Determines which calendar should handle the request based on the NLP result.
- The registry acts as the central router for all provider operations.
- The error signal tells the core that the action has been successfully handed off.
- Ensures no "ghost" events remain in the UI while the user is in the other plugin's modal.
This prevents duplicate modal UX and keeps provider-specific behavior outside dispatcher logic.
The mutation lifecycle this relies on is defined in EventCache Contract.
Holiday Provider (virtual, bundled data)¶
Read-only provider that surfaces date-holidays data as all-day OFCEvent objects. No vault file, no network request, no EventLocation.
Architecture invariants:
isRemote = false,loadPriority = 5— loads in the first provider wave alongside local sources.- CRUD methods (
createEvent,updateEvent,deleteEvent,createInstanceOverride) unconditionally reject.getCapabilities()returns{ canCreate: false, canEdit: false, canDelete: false }. - Events are typed
single,allDay: true,date: YYYY-MM-DD, ID formatdate-holidays:{date}:{name}. - Timezone: provider intentionally omits any timezone argument to
date-holidays. All-day events carry no time component; the plugin'sEventEnhancerapplies the global display timezone uniformly. Passing a per-provider timezone here would cause double-correction.
Cache model:
Results are cached in localStorage keyed by a djb2 hash of country|state|region|holidayTypes|display plus year. TTL is 30 days. Hash change on config edit = automatic cache invalidation. localStorage failures are silently swallowed (plugin functions correctly; data is recomputed on next open).
Config hash inputs (src/providers/holidays/HolidayProvider.ts _computeConfigHash):
country | state | region | holidayTypes | display
Source files:
src/providers/holidays/typesHoliday.ts—HolidayProviderConfig,HolidayTypeFilter,getHolidayTypesForFilter()src/providers/holidays/HolidayProvider.ts— provider class, cache layer,OFCEventconversionsrc/providers/holidays/ui/HolidayConfigComponent.tsx— settings modal; links to date-holidays country list and type specification inline
User docs: Holidays calendar
Cross-provider orchestration constraints¶
- Registry is the only runtime router for provider read/write operations.
- Providers expose capabilities (
canCreate,canEdit,canDelete) and optional custom hooks (toggleComplete,canBeScheduledAt). - Persistent event identity must be surfaced through
getEventHandle()so global identifier mapping remains stable.
Provider contract and registration rules are specified in Provider Architecture and Provider Blueprint.
Provider-agnostic recurring instance semantics¶
Recurring-instance completion and skip behavior must remain provider-owned while the UI and core remain provider-agnostic.
Contract location:
src/providers/Provider.tsdefinesRecurringInstanceStateand optionalRecurringInstanceStateProviderhooks.
Contract shape:
getRecurringInstanceState(event, instanceDate)returns normalized state (completed,skipped) for a concrete instance date.setRecurringInstanceState(event, instanceDate, nextState)applies provider-owned persistence and returns success/failure.
Design goals:
- No provider-specific recurrence fields (for example backend-specific arrays or flags) may leak into generic UI logic.
- UI checkbox and styling logic must consume only normalized
RecurringInstanceState. - Mutation orchestration remains generic; provider adapters translate normalized state into backend-native operations.
Rollout pattern for additional providers¶
When adding recurring-instance semantics to another provider (CalDAV, Google, Tasks, etc.), follow this sequence:
- Keep provider-specific recurrence persistence internal to the provider implementation.
- Implement
RecurringInstanceStateProviderin that provider. - Map backend-native state to
RecurringInstanceStateingetRecurringInstanceState(...). - Map normalized target state back to backend-native write operations in
setRecurringInstanceState(...). - Avoid introducing provider-type checks in shared UI/core pathways.
This no-provider-branching rule aligns with Data Flow and Events Architecture.
Current adoption¶
- TaskNotes provider implements this contract and adapts TaskNotes recurring-instance APIs behind the generic interface.
- Existing providers that do not implement this optional interface continue to use legacy fallback behavior.
Integration anchors¶
src/providers/Provider.tssrc/providers/ProviderRegistry.tssrc/providers/fullnote/FullNoteProvider.tssrc/providers/dailynote/DailyNoteProvider.tssrc/providers/ics/ICSProvider.tssrc/providers/caldav/CalDAVProvider.tssrc/providers/google/GoogleProvider.tssrc/providers/googletasks/GoogleTasksProvider.tssrc/providers/tasks/TasksPluginProvider.tssrc/providers/tasknotes/TaskNotesProvider.tssrc/providers/holidays/HolidayProvider.ts