Skip to content
GitHubBuy Me A Coffee

Governance

This is what makes it safe to give an AI write access to your notes.

The principle is fail-closed. If anything goes wrong during an approval check (missing callback, unloaded config, unexpected error), the operation is denied. The agent never silently auto-approves. Every tool call, internal or MCP, flows through one central pipeline.

The pipeline

ToolExecutionPipeline (src/core/tool-execution/ToolExecutionPipeline.ts) is the single enforcement point. Nothing bypasses it.

Three questions, in order: Is the path allowed? Is the operation approved? Only then does the tool run. After execution, the result is logged.

Path protection

Two files in the vault root control which paths the agent can access:

FileEffect
.obsidian-agentignorePaths completely invisible to the agent. Uses gitignore syntax.
.obsidian-agentprotectedPaths readable but never writable, even with explicit approval.

Both files use glob patterns. A line like journal/private/** blocks everything under that folder.

Some paths are always blocked regardless of configuration: .git/, the Obsidian workspace and cache files. The governance config files themselves are always write-protected, so the agent cannot edit its own restrictions.

The IgnoreService (src/core/governance/IgnoreService.ts) enforces this. If it hasn't finished loading its patterns yet, it denies all access. Fail-closed, as always.

Approval categories

Every tool is classified into an approval group. The group determines whether the operation runs automatically or needs human consent.

GroupExamplesDefault behavior
readread_file, search_files, semantic_searchAuto-approved
note-editwrite_file, edit_file, append_to_fileRequires approval
vault-changecreate_folder, delete_file, move_fileRequires approval
webweb_fetch, web_searchAuto-approved when web tools are enabled
agentattempt_completion, switch_mode, update_todo_listAlways auto-approved
subtasknew_taskConfigurable
mcpuse_mcp_toolConfigurable
skillexecute_command, call_plugin_apiConfigurable
sandboxevaluate_expressionRequires explicit opt-in
self-modifymanage_skill, manage_sourceAlways requires human approval, no bypass

Self-modification tools are the strictest category. The agent can create and edit its own skills and source code, but a human must approve every change. There is no auto-approve setting for this group.

For note edits, the approval UI can show a semantic diff grouped by Markdown structure (frontmatter, headings, lists, code blocks) rather than raw line hunks. You can approve, reject, or edit individual sections before confirming.

Checkpoints

Before any write operation, the pipeline takes a git snapshot of the affected file. This uses a shadow repository at .obsidian/plugins/obsilo-agent/checkpoints/ powered by isomorphic-git (pure JavaScript, no native git binary needed).

GitCheckpointService (src/core/checkpoints/GitCheckpointService.ts) commits the file's current content into the shadow repo before the tool modifies it. Each checkpoint records the task ID, commit hash, timestamp, changed files, and the tool that triggered it. Files that didn't exist before the checkpoint are tracked separately so restore can delete them.

The result: after any task, you can undo all changes. The undo is granular: each write operation gets its own checkpoint, so you can roll back to any intermediate state. The vault's own git history (if it has one) is never touched.

Audit log

Every tool call is logged to a JSONL file via OperationLogger (src/core/governance/OperationLogger.ts). One file per day, stored at .obsidian/plugins/obsilo-agent/logs/YYYY-MM-DD.jsonl. Files older than 30 days are automatically deleted.

Each entry records:

FieldContent
timestampISO 8601
taskIdWhich task triggered the call
modeActive mode at the time
toolTool name
paramsInput parameters (PII-scrubbed)
resultOutput summary (capped at 2000 chars)
successWhether the call succeeded
durationMsExecution time

Sensitive values (passwords, tokens, API keys) are replaced with [REDACTED] before logging. File content fields are logged as [N chars] rather than the full text. URLs have credentials stripped.

The log is append-only during a session. You can read it with any tool that understands JSONL, or use the built-in read_agent_logs tool to have the agent analyze its own history.