How `inspect_bundle` Validates Write Bundles Without Mutation in Claude-Obsidian

inspect_bundle guarantees zero side-effects by performing all validation steps—including write preparation and cryptographic hashing—inside isolated temporary directories while capturing and verifying vault identity snapshots before and after processing.

The inspect_bundle function in the AgriciDaniel/claude-obsidian repository serves as the read-only entry point that validates transaction bundles before any filesystem mutations occur. This deterministic inspection pipeline computes the complete execution plan—including metadata expansion and content hashing—without creating or modifying files in the target vault. By operating on temporary copies and verifying vault identity through inode and device tracking, the function ensures safe dry-run validation for CI checks and pre-flight verification.

Vault Path Resolution and Security Validation

The inspection begins with strict path sanitization and security guards that prevent traversal attacks.

Canonical path verification occurs first in claude_obsidian/transaction.py (lines 4365–4372), where the function resolves the supplied vault_root to an absolute path and verifies it exists as a directory. If the path is not a directory, the engine raises [VAULT_NOT_DIRECTORY]; if the parent is missing for a new vault scenario, it raises [VAULT_PARENT_MISSING].

Immediately following on line 4373, the _assert_no_portable_vault_root_alias guard prohibits "portable" aliases that could obscure the vault's real location. This prevents attackers from exploiting symbolic links or mount point confusion to redirect writes to unintended directories.

Schema and Operation Type Verification

Once the vault path is secured, the function validates the bundle structure and operation semantics.

In lines 4374–4378, _load_bundle ingests the input—whether a Python dict or a JSON file path—and verifies the top-level schema compliance. The function then sanitizes the operation_id and validates the operation_type against the allowed set defined in OPERATION_TYPES (lines 4379–4384). This ensures the engine only processes recognized transaction types such as "write" before proceeding to resource allocation.

Secure Directory Descriptors and Identity Snapshots

To prevent time-of-check-to-time-of-use (TOCTOU) attacks, the function captures a cryptographic baseline of the vault state.

On platforms supporting confined directory access, lines 4385–4392 open the vault using os.open(..., directory_open_flags()). Failure to obtain this file descriptor triggers an UNSAFE_VAULT_IDENTITY error. With the descriptor secured, line 4393 calls _vault_object_identity(vault, root_fd=root_fd) to capture a collision-resistant snapshot comprising the inode, device ID, and UID. This vault_identity fingerprint is stored for later comparison to detect concurrent mutations.

Metadata Expansion and Isolated Write Preparation

All computationally intensive validation happens in a temporary sandbox, ensuring the real vault remains untouched.

Lines 4396–4397 invoke _expand_managed_metadata to compute the concrete bundle state that the executor would see, expanding auto-generated front-matter, timestamps, and aliases through pure computation without disk writes. The expanded bundle then passes to _prepare_writes (lines 4399–4407), which creates a tempfile.TemporaryDirectory sandbox and resolves every write entry to a PreparedWrite object containing the target path, content hash, and file mode. This temporary directory is the only filesystem location touched during inspection.

Integrity Verification and Return Payload

Before returning results, the function verifies the vault remained stable throughout the inspection process.

Lines 4408–4411 perform a vault-identity re-check by comparing the current vault state against the initial snapshot. If the inode or device changed—indicating concurrent mutation—the engine raises VAULT_IDENTITY_CHANGED. After confirming integrity, lines 4413–4415 close any opened directory file descriptors to prevent resource leaks.

The function then computes deterministic hashes for reproducibility. Line 4416–4429 calls _canonical_json_hash on the expanded bundle and computes a plan_approval_sha256 from the vault identity, expanded bundle, prepared writes, and vault root. Finally, lines 4429–4440 return a read-only summary dictionary containing:

  • valid: True
  • changed_paths: List of affected files
  • Per-path content hashes and desired file modes
  • bundle_sha256 and expanded_bundle_sha256
  • vault_identity and approval_sha256

No file in the real vault is created or modified during this entire sequence.

Usage Examples

The following examples demonstrate how to call inspect_bundle for safe pre-validation:

from pathlib import Path
from claude_obsidian.transaction import inspect_bundle

# Example 1 – Inspect a bundle file on disk

bundle_path = Path("/tmp/my-transaction-bundle.json")
summary = inspect_bundle(
    vault_root="/home/user/my-vault", 
    bundle_or_path=bundle_path
)
print(summary["valid"])               # → True

print(summary["changed_paths"])       # → ['wiki/Note.md', 'wiki/Folder/Info.md']

# Example 2 – Inspect an in-memory bundle dict

bundle = {
    "schema": "claude-obsidian.transaction-bundle.v1",
    "operation_id": "add-note",
    "operation_type": "write",
    "writes": [
        {"path": "wiki/NewNote.md", "mode": "create", "content": "# New Note\n"},

    ],
}
summary = inspect_bundle(
    vault_root="/home/user/my-vault", 
    bundle_or_path=bundle
)
print(summary["approval_sha256"])      # deterministic hash for later approval

Both invocations are strictly read-only and safe to execute against production vaults.

Source File Reference

Understanding the validation pipeline requires familiarity with these key files:

File Role
claude_obsidian/transaction.py Core implementation of inspect_bundle, apply_bundle, and all validation helpers referenced in the pipeline steps.
claude_obsidian/_runtime.py Internal runtime providing _RuntimeStore and low-level directory file descriptor handling for secure vault access.
tests/test_transaction.py Unit tests verifying inspect_bundle validation logic and strict zero-mutation guarantees.
tests/test_vault_ops.py Integration tests combining inspection with actual bundle application workflows.

Summary

  • inspect_bundle validates write bundles through a 12-step deterministic pipeline that never touches the real vault.
  • Security checks include canonical path resolution, portable-alias guards, and secure directory file descriptors (lines 4365–4392).
  • Zero-mutation guarantee is achieved by processing all writes inside tempfile.TemporaryDirectory sandboxes (lines 4399–4407).
  • Integrity verification uses pre- and post-processing vault identity snapshots to detect concurrent modifications (lines 4393, 4408–4411).
  • Deterministic output includes cryptographic hashes for the bundle, expanded metadata, and a plan approval fingerprint suitable for CI/CD gates.

Frequently Asked Questions

How does inspect_bundle prevent accidental file modification in the vault?

The function routes all write preparation through _prepare_writes, which creates a temporary directory via tempfile.TemporaryDirectory and resolves all file paths and content hashes within that isolated sandbox. By computing the full execution plan—including metadata expansion and content hashing—without referencing the real vault directory for write operations, the function guarantees zero filesystem side-effects according to the implementation in claude_obsidian/transaction.py (lines 4396–4407).

What happens if the vault changes while inspect_bundle is running?

The function captures a vault identity snapshot (inode, device, UID) before processing using _vault_object_identity (line 4393) and compares it against the current state after preparation completes (lines 4408–4411). If the identity differs—indicating a concurrent modification or external mutation—the engine raises VAULT_IDENTITY_CHANGED and aborts, preventing validation against a stale vault state.

Can inspect_bundle handle both file paths and Python dictionaries as input?

Yes. The internal _load_bundle helper (lines 4374–4378) accepts either a file path pointing to a JSON bundle or an in-memory Python dictionary. The function normalizes both inputs into a validated bundle structure before proceeding with schema and operation type verification, making it flexible for CLI tools and programmatic API usage.

What is the plan_approval_sha256 returned by inspect_bundle?

The plan_approval_sha256 is a deterministic cryptographic hash computed from the vault identity, expanded bundle contents, prepared write objects, and vault root path (lines 4416–4429). This fingerprint allows teams to store a pre-computed approval checksum and verify later that the exact same bundle is being applied to the exact same vault state, enabling secure multi-stage deployment pipelines.

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 →