Obsidian Rate LimitsSAFE
Model-agnostic agent-skills platform with a harness-free canonical layer, verified adapters, and the ccpi package manager. Explore at tonsofskills.com.
Overview
Model-agnostic agent-skills platform with a harness-free canonical layer, verified adapters, and the ccpi package manager. Explore at tonsofskills.com.
4f83675ca38aOBSERVED · 2026-10-09Host compatibility
What the documentation claims. We have not run a compatibility test.
| Host | Status | Notes |
|---|---|---|
| claude-code | mentioned |
What it tells the agent
The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.
--- name: obsidian-rate-limits description: 'Handle Obsidian file system operations and throttling patterns. Use when processing many files, handling bulk operations, or preventing performance issues from excessive operations. Trigger with phrases like "obsidian rate limit", "obsidian bulk operations", "obsidian file throttling", "obsidian performance limits". ' allowed-tools: Read, Write, Edit version: 1.13.0 license: MIT author: Jeremy Longshore <[email protected]> tags: - saas - obsidian - performance compatibility: Designed for Claude Code --- # Obsidian Rate Limits ## Overview Obsidian has no traditional API rate limits, but it runs on Electron with a single-threaded UI. This skill covers debouncing, batching, throttling, and async queue patterns to keep plugins responsive and prevent UI freezes. ## Prerequisites - Understanding of JavaScript event loop and `requestAnimationFrame` - Familiarity with async/await and Promises - Working Obsidian plugin with file operations ## Instructions ### Step 1: Debounce vault.on('modify') Events `vault.on('modify')` fires on every keystroke when a user types in a note. Without debouncing, your handler runs hundreds of times per second. ```typescript import { Plugin, TFile, debounce } from 'obsidian'; export default class ThrottledPlugin extends Plugin { async onload() { // Obsidian provides a built-in debounce utility const debouncedHandler = debounce( (file: TFile) => this.handleFileModified(file), 500, // wait 500ms after last keystroke true // run on leading edge too (immediate first call) ); this.registerEvent( this.app.vault.on('modify', debouncedHandler) ); } private async handleFileModified(file: TFile) { // This runs at most once per 500ms per burst of edits const cache = this.app.metadataCache.getFileCache(file); if (cache?.frontmatter?.tracked) { await this.updateIndex(file); } } } ``` If you need per-file debouncing (common when multiple files change simultaneously): ```typescript private fileTimers = new Map<string, NodeJS.Timeout>(); private debouncedPerFile(file: TFile, fn: () => void, delay = 500) { const existing = this.fileTimers.get(file.path); if (existing) clearTimeout(existing); const timer = setTimeout(() => { this.fileTimers.delete(file.path); fn(); }, delay); // Use activeWindow for Obsidian's timeout tracking this.fileTimers.set(file.path, timer); } ``` ### Step 2: Batch File Operations with UI Yielding Processing hundreds of files synchronously locks the UI. Yield back to the main thread between batches. ```typescript async processAllFiles(): Promise<void> { const files = this.app.vault.getMarkdownFiles(); const BATCH_SIZE = 50; const results: ProcessResult[] = []; for (let i = 0; i < files.length; i += BATCH_SIZE) { const batch = files.slice(i, i + BATCH_SIZE); // Process one batch for (const file of batch) { const content = await this.app.vault.cachedRead(file); results.push(this.processContent(file.path, content)); } // Yield to UI thread between batches await sleep(0); // Update progress if you have a status bar or notice const pct = Math.round(((i + batch.length) / files.length) * 100); this.statusBar?.setText(`Processing: ${pct}%`); } this.statusBar?.setText(`Done: ${results.length} files processed`); } // Obsidian exports sleep(), or use this: function sleep(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } ``` ### Step 3: Throttle UI Updates Updating DOM elements on every event causes layout thrashing. Throttle to animation frames. ```typescript class ThrottledStatusView { private pendingUpdate = false; private el: HTMLElement; private data: { count: number; lastFile: string } = { count: 0, lastFile: '' }; constructor(el: HTMLElement) { this.el = el; } // Call this as often as you want — it coalesces to one paint per frame update(count: number, lastFile: string) { this.data = { count, lastFile }; if (!this.pendingUpdate) { this.pendingUpdate = true; requestAnimationFrame(() => { this.render(); this.pendingUpdate = false; }); } } private render() { this.el.empty(); this.el.createEl('span', { text: `${this.data.count} files` }); this.el.createEl('span', { text: this.data.lastFile, cls: 'nav-file-title' }); } } ``` ### Step 4: Async Queue for Write Operations Concurrent writes to the same file corrupt data. Queue writes so only one runs at a time. ```typescript class WriteQueue { private queue: Array<() => Promise<void>> = []; private running = false; async enqueue(fn: () => Promise<void>): Promise<void> { return new Promise((resolve, reject) => { this.queue.push(async () => { try { await fn(); resolve(); } catch (e) { reject(e); } }); this.process(); }); } private async process() { if (this.running) return; this.running = true; while (this.queue.length > 0) { const task = this.queue.shift()!; await task(); // Small delay between writes to avoid overwhelming disk I/O await sleep(10); } this.running = false; } } // Usage in plugin class MyPlugin extends Plugin { private writeQueue = new WriteQueue(); async safeWrite(file: TFile, content: string) { await this.writeQueue.enqueue(async () => { await this.app.vault.modify(file, content); }); } } ``` ### Step 5: Progress Notice for Long Operations Give users feedback during operations that take more than a second. ```typescript async bulkUpdateFrontmatter( files: TFile[], updater: (fm: any) => void ): Promise<{ success: number; failed: string[] }> { const failed: string[] = []; let success = 0; // Use Notice with a timeout of 0 to create a persistent notice const noti
Trust audit
SAFEgrade B · trust 89/100 Nothing in the source contradicts what it says it does. Grade A is reserved for packages that have also passed the behavioural sandbox.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | PASS |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- none-observed
- Network
- none-observed
- Shell
- none-observed
- Dependencies
- pinned
- Secrets in source
- none-found
Findings (0)
No findings outside the package's declared scope.
Gates applied: no_behavioural_pass.
4f83675ca38afull audit observations/trust-audit/skill/jeremylongshore__obsidian-rate-limits.json · Report an issue / request a re-scanAudit history
Every audit this skill has had.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-09 | 4f83675ca38a | SAFE | B | 89 | first audit |
Questions
What does the Obsidian Rate Limits skill do?
Model-agnostic agent-skills platform with a harness-free canonical layer, verified adapters, and the ccpi package manager. Explore at tonsofskills.com.
Is Obsidian Rate Limits safe to install?
The audit found nothing in the source that contradicts what it says it does, and graded it B (89/100). Grade A is held back for packages that have also passed a sandboxed behavioural run, which is why a clean skill reads B.
What can Obsidian Rate Limits access on my machine?
The audit observed no filesystem, network or shell use at all in its source.
Which assistants does Obsidian Rate Limits work with?
Its documentation mentions claude-code. That is what the text claims, not a compatibility test we ran.
How current is this page?
The grade is for one exact copy of the source (4f83675ca38a), read on 2026-10-09. The repository is watched, and a new audit runs when it changes — this is the first audit.