Purpose-led inquiry checkpoints (#401)¶
An explicitly saved inquiry stores sensitive user text (purpose, response and gaps), selected and consulted paths, and its human resolution in the plugin's local data. This is not the content-free activity counter. One inquiry is retained until explicit clear/replace; the optional judgement log's 500-entry cap and recording toggle do not control it. Nothing is transmitted. Ordinary vault/device sync remains under the user's configuration, not a new plugin sync or encryption guarantee.
Drafts are not persisted by unrelated settings changes. A successful Save/Pause acknowledges one revision; newer edits or failed saves are not guaranteed after restart. Unsupported saved data is retained for explicit recovery/reset. Clearing inquiry bookkeeping never removes authored Markdown.
Capabilities & privacy¶
ZettelFlow discloses every capability it uses, ahead of Obsidian's upcoming capability-label system (network / file system / clipboard / script execution). The plugin collects no telemetry and transmits no personal data or vault contents.
What the plugin accesses¶
| Capability | Used? | What for | How |
|---|---|---|---|
| File system (vault) | Always | Read your Canvas flow files; create and edit notes as the output of a flow; install community templates into your vault. | Obsidian's Vault API and FileManager โ no hardcoded .obsidian paths, path-normalised, desktop and mobile. |
| Network | Opt-in | Only the community templates browser: read-only GETs that fetch example flows/steps/actions/systems and preview images from the static ZettelFlow community catalog on GitHub raw. No backend, no account, no uploads. | request/requestUrl in src/application/community/. No requests are made unless you open the community browser. |
| AI provider (external) | Opt-in, off by default | The optional ๐ค AI action category (summarize, classify, generate-questions, โฆ). When you enable it and run an AI action, the (length-bounded) note content is sent to the single https OpenAI-compatible endpoint you configure to get a completion back. Nothing the model writes reaches a note until you accept it โ every completion is a proposal you accept, edit or reject (#337, ยงXII) โ and AI never runs in automations at all. | requestUrl in src/architecture/ai/OpenAiCompatibleProvider.ts only. No bundled key, no default endpoint, no other endpoint, no telemetry. Off by default; input/output are capped and the endpoint must be https (or localhost). Your verdict on each proposal is recorded in the judgement record. See AI provider setup. |
| Clipboard | On your click | Writes only, never reads. The โcopyโ buttons put a step / action / canvas-node configuration on the clipboard as JSON so you can paste it elsewhere or share it. | navigator.clipboard.writeText in the step, action and canvas-node editors. Nothing is ever read from your clipboard, and nothing is copied unless you press a copy button. |
| Vault enumeration | Always | Lists every markdown file path in the vault to build the offline knowledge model that every analysis reads (health, discovery, the graph, Cultivate). | vault.getMarkdownFiles() via the ObsidianApi facade. Paths and metadata only, all local, no network. You can keep folders out of it entirely with the knowledge scope. |
| Dynamic code execution | Opt-in | Runs the JavaScript you author: the Script action, dynamic selectors, vault hooks and workflow-event conditions. Your code is handed zf and Obsidian's app, so it can do whatever the plugin itself can โ including reading the whole offline knowledge model through zf.knowledge. That is a read-only surface: it exposes no vault writer, and your script still writes only through the note it is building or a hook's response. Reaching the AI provider from a script goes through zf.ai.propose, which cannot write without your verdict (ยงXII). | Built in exactly one module, architecture/api/lib/FnConstructor.ts โ both the async form the script surfaces use and the synchronous CommonJS wrapper that loads your library scripts (a guardrail test fails the build if a second site appears). A library script is loaded as a CommonJS module and is handed Node's require on desktop only, where Obsidian exposes it; on mobile it is simply absent. No remote code is ever fetched or executed โ only code stored in your own vault or settings. A community system may ship JavaScript, and the install now says so before it writes anything, naming the steps that carry code and asking you to acknowledge it. |
| Judgement record | On by default | Stores the decisions you make on your own ideas โ accept / modify / reject / confirm / challenge โ so cognitive agency can be derived instead of invented. | A local, bounded log (most recent 500) in the plugin's data.json: a note path, a short locale-free subject id, an origin and a verdict. No note content, no AI output, no network. Honours the knowledge scope and can be turned off. |
What the automated scan sees that is not ours¶
Obsidian's Community-hub scan reports runtime base64 encode/decode. That call does not come from ZettelFlow code โ there is no atob/btoa anywhere in src/. It comes from the bundled 3D graph library (3d-force-graph, which vendors three.js loaders) used by the graph view. It decodes assets the library ships with; it does not encode anything about your vault, and it makes no request.
No telemetry¶
ZettelFlow does not embed analytics, crash reporting, or any phone-home. The only outbound requests are the opt-in community fetches (read-only) and the opt-in AI provider above. Nothing about your notes leaves your machine unless you enable AI and run an AI action (which sends the note content only to the endpoint you set). Sharing a template is a manual GitHub contribution you make yourself โ the plugin never uploads anything.
Diagnostics stay content-free (#401)¶
The inquiry journey reads the knowledge index, so a diagnostic emitted while indexing the very note you are working on must not become a side channel. The index diagnostics that journey can trigger โ inline-relation enrichment, per-note upsert, and the conceptual-timeline/development-journal hooks โ log only a fixed failure category (the error type, e.g. TypeError), never the note's path, its body, or the raw exception message. A private inquiry note cannot leak into the log this way.
The thinking-heatmap journal (local only)¶
The thinking heatmap is fed by a development-event journal stored in the plugin's own data.json as a per-day count map (day โ count) โ no note names, no content. It is pruned to the last ~year and makes no network request. It is on by default (it only records benign aggregate counts) and can be disabled under Settings โ ZettelFlow โ Thinking journal.
The community gallery is static¶
The community gallery is fully static: the plugin only ever does read-only GETs of catalog files from GitHub raw. There is no backend, no account, and the plugin never uploads anything โ contributions are made through GitHub (an issue form + a PR).
Ahead of Obsidian's labels¶
Obsidian has announced machine-readable disclosure labels on the community hub. When that format ships, ZettelFlow will adopt it so these capabilities are declared in the plugin manifest as well as here. Until then, this page and the README section are the authoritative disclosure.