How claude-obsidian Separates the Product Repository from the User Vault

claude-obsidian enforces strict separation between immutable product code and mutable user data through hierarchical vault discovery logic, filesystem safety checks in paths.py, and explicit documentation that prevents the repository checkout from ever being selected as a writable vault.

The claude-obsidian project maintains a rigorous architectural boundary between its own installation files and the knowledge vaults it manages. Understanding how the system separates the product repository from the user vault is essential for safe deployment and daily operation. This design ensures that updates to the tool never risk corrupting your personal knowledge base, while preventing accidental writes into the code repository.

Hierarchical Vault Discovery

The resolve_vault_root() function in claude_obsidian/paths.py (lines 61-78) implements a priority-based discovery system that never implicitly returns the product directory as a valid vault. The resolution order follows this strict hierarchy:

  1. Explicit --vault argument passed via CLI
  2. CLAUDE_OBSIDIAN_VAULT environment variable
  3. Nearest .claude-obsidian.json workspace config file
  4. Nearest initialized vault directory containing sentinel files

This structured fallback mechanism ensures that even when running commands from within the product checkout directory, the system searches outward for a legitimate user vault rather defaulting to the current working directory.

Filesystem Safety Guards

To prevent catastrophic data corruption, the assert_not_plugin_tree() function (lines 29-58 in claude_obsidian/paths.py) implements a hard boundary check. The function canonicalizes both the candidate vault path and the repository root, then raises a VaultSelectionError if the candidate matches or descends from the plugin directory.

This guard guarantees that mutable operations cannot be written into product files. If you attempt to use the installation directory as a vault, the system immediately blocks the operation with a descriptive error code.

The validate_vault_root() function (lines 103-124) adds an additional legacy-compatible layer by checking for vault sentinel files. Valid vaults must contain at least one of the following directories:

  • wiki/
  • .obsidian/
  • .raw/

If none exist, the path is rejected as "not a claude-obsidian vault," preventing accidental selection of arbitrary directories.

Documented Architectural Boundaries

The separation is reinforced through explicit documentation rather than code alone. According to docs/compound-vault-guide.md (lines 12-20), the architecture defines four distinct roles:

  • Product package (immutable code and templates)
  • User vault (mutable knowledge content)
  • Derived runtime (temporary/generated assets)
  • Public artifact (exportable outputs)

The documentation explicitly states that "an installed plugin cache never becomes the user vault." Similarly, README.md (lines 89-96) emphasizes this boundary with clear prose: "the checkout contains the product. It is not your knowledge vault."

Practical Implementation Examples

When resolving a vault from a CLI command, the system safely navigates the hierarchy:

from claude_obsidian.paths import resolve_vault_root

# Example: user runs `claude-obsidian.py wiki --vault /home/me/my-vault`

vault = resolve_vault_root(explicit="/home/me/my-vault")
print(vault.root)          # → Path('/home/me/my-vault')

print(vault.source)        # → 'explicit'

print(vault.legacy)        # → False (modern workspace config present)

Attempting to use the product repository as a vault triggers an immediate error:

from claude_obsidian.paths import resolve_vault_root, VaultSelectionError

try:
    # Suppose the current working directory is the product checkout

    vault = resolve_vault_root()
except VaultSelectionError as e:
    print(e.code, e)      
    # → PLUGIN_ROOT_IS_NOT_VAULT Refusing to write mutable vault state into the plugin installation

For path traversal safety, the assert_within() helper validates that all operations remain inside the vault boundary:

from claude_obsidian.paths import assert_within, VaultSelectionError
from pathlib import Path

vault_root = Path("/home/me/my-vault")
candidate = "/home/me/my-vault/wiki/notes/Example.md"

try:
    inside = assert_within(vault_root, candidate)
    print("OK:", inside)
except VaultSelectionError:
    print("Path outside vault!")

Summary

  • Hierarchical discovery in resolve_vault_root() prioritizes explicit user configuration over implicit directory selection, ensuring the product checkout is never accidentally chosen.
  • Filesystem guards via assert_not_plugin_tree() raise VaultSelectionError when detecting attempts to write mutable state into the immutable product directory.
  • Sentinel validation through validate_vault_root() requires specific directory markers (wiki/, .obsidian/, or .raw/) to qualify a path as a legitimate vault.
  • Explicit documentation in the Compound Vault guide and README establishes clear architectural roles that treat the product as read-only code and the vault as mutable data.

Frequently Asked Questions

What happens if I accidentally point claude-obsidian to its own installation directory?

The system raises a VaultSelectionError with the code PLUGIN_ROOT_IS_NOT_VAULT. The assert_not_plugin_tree() function in claude_obsidian/paths.py detects that the candidate path matches or descends from the repository root and immediately refuses the operation, printing a message that it is "Refusing to write mutable vault state into the plugin installation."

How does claude-obsidian detect valid vault directories?

The validate_vault_root() function checks for the presence of sentinel directories that mark a legitimate vault. A valid path must contain at least one of the following: a wiki/ directory, an .obsidian/ directory, or a .raw/ directory. If none are found, the path is rejected as "not a claude-obsidian vault."

Can I override the safety checks to use the product folder as a vault?

No. The separation is architecturally enforced and not intended to be bypassed. The documentation in docs/compound-vault-guide.md explicitly states that the product package "never becomes the user vault." This immutable boundary protects both the integrity of your knowledge base and the stability of the tool itself.

What are the four architectural roles defined in the Compound Vault guide?

According to docs/compound-vault-guide.md, the four roles are: Product package (the immutable code and templates in the repository), User vault (your mutable knowledge content), Derived runtime (temporary or generated working files), and Public artifact (final exported outputs). This taxonomy ensures clear data ownership and prevents cross-contamination between the tool's code and your content.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →