This plugin allows you to synchronize your notes and transcripts from Granola (https://granola.ai) directly into your Obsidian vault. It fetches documents from Granola, converts them from ProseMirror JSON format to Markdown, and saves them as .md files.
Granola stores its credentials encrypted on disk and protects the wrapping key with the OS's user-scoped secret store — your macOS Keychain, Linux libsecret/kwallet entry, or, on Windows, the per-user Data Protection API (DPAPI). To sync, the plugin needs to read those same credentials. It does this entirely on your machine — nothing about your credentials ever leaves your computer except the access token that's sent to Granola's own API (the same destination Granola itself talks to). See docs/CREDENTIALS.md for the full decoding chain.
The first time you sync on macOS, your operating system will ask whether to allow Obsidian to access the Granola Safe Storage keychain item. The prompt looks like a standard system dialog:
Obsidian Helper (Renderer) wants to use your confidential information stored in "Granola Safe Storage" in your keychain.
Choose Always Allow. That records Obsidian as a trusted reader for this specific item, so future syncs don't prompt you again. You can review or revoke this consent at any time in Keychain Access → search Granola Safe Storage → double-click → Access Control:
The same flow applies — your OS will ask once (via libsecret/kwallet) whether Obsidian may read Granola's credentials. Approve once and subsequent syncs are silent.
Granola on Windows does not store anything in Credential Manager — there is nothing to "Always Allow", and you will not see a prompt the first time you sync. Instead, Granola's wrapping key lives DPAPI-encrypted in its Electron Local State file under %APPDATA%\Granola. DPAPI is gated by your Windows login: only the same Windows user account that wrote the key can unwrap it, and the unwrap happens silently in the background. The plugin reads Local State, asks Windows to decrypt the wrapped key, then decrypts stored-accounts.json.enc exactly as it does on the other platforms. If the plugin reports a DPAPI failure, it usually means Granola was installed under a different Windows user account or the user profile was migrated — sign in to Granola again so it can rewrite Local State for the current user.
Granola Safe Storage doesn't give it access to anything else. On Windows, DPAPI is scoped to your Windows user account; the wrapped key cannot be decrypted by another user on the same machine, or by you on a different machine.src/services/credentials.ts, src/services/granolaCredentialsCrypto.ts, src/services/keyringLoader.ts, and src/services/dpapiLoader.ts. The native bindings are the well-maintained @napi-rs/keyring (macOS/Linux) and @primno/dpapi (Windows); only the compiled binary for your platform is loaded.CryptUnprotectData fail; the plugin can't bypass that either.All synced files include structured frontmatter for tracking and identification:
Notes:
---
granola_id: doc-123
title: "Meeting Title"
type: note
created: 2024-01-15T10:00:00Z
updated: 2024-01-15T12:00:00Z
attendees:
- John Doe
- Jane Smith
transcript: "[[Transcripts/Meeting Title-transcript.md]]"
---
Transcripts:
---
granola_id: doc-123
title: "Meeting Title - Transcript"
type: transcript
created: 2024-01-15T10:00:00Z
updated: 2024-01-15T12:00:00Z
attendees:
- John Doe
- Jane Smith
note: "[[Granola/Meeting Title.md]]"
---
The granola_id is consistent across both note and transcript files for the same source document, while the type field distinguishes between them. This allows both file types to coexist with proper duplicate detection.
granola_id: Unique identifier from Granola, consistent across note and transcript filestitle: Document title (with "- Transcript" suffix for transcripts)type: Either note or transcriptcreated: ISO timestamp when the document was createdupdated: ISO timestamp when the document was last updatedattendees: Array of attendee names from the meetingtranscript: Wiki-style link to the transcript file (only in notes saved as individual files, not in daily notes)note: Wiki-style link to the note (in transcripts, links to individual files or daily notes with heading anchors)The transcript field is added when notes are saved as individual files and transcripts are synced. The note field is always added to transcripts when notes are being synced - for individual note files, it links to the file path; for daily notes, it links to the daily note file with a heading anchor (e.g., [[2024-01-15#Meeting Title]]).
When the "Include Private Notes" setting is enabled and a document has private notes content, synced notes will include:
When private notes are disabled or not present, notes display the content directly without section headings.
For combined notes (notes with transcripts), the structure is: Private Notes → Enhanced Notes → Transcript.
For detailed information about how the sync process works, see Sync Process Documentation. This document explains the credentials loading, document fetching, note syncing, transcript syncing, frontmatter structure, file deduplication, and error handling mechanisms.
npm install
To build the plugin:
npm run build
The plugin uses Jest for testing. To run the tests:
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage
For detailed testing information, including testing strategy and development workflow, see CONTRIBUTING.md.
To create a release:
# Auto-bump patch version
node scripts/release.js
# Specify a specific version
node scripts/release.js 1.2.3
Please see CONTRIBUTING.md for info on contributing to this project.
MIT
This plugin is an independent project and is not affiliated with, endorsed by, or sponsored by Granola. It uses Granola's own API and credential files only on your local machine, with credentials you've already authenticated through the official Granola app. Do not use this plugin in any way that breaks Granola's Terms of Service — you are responsible for ensuring your use complies with them.