# Transaction System in claude-obsidian: Architecture and Implementation

> Explore the claude-obsidian transaction system. Discover its architecture and implementation, focusing on schema validation, file locking, atomic writes, and rollback for reliable vault updates. Learn more!

- Repository: [Agrici.Daniel/claude-obsidian](https://github.com/AgriciDaniel/claude-obsidian)
- Tags: architecture
- Published: 2026-08-26

---

**The transaction system in claude-obsidian guarantees recoverable, operation-level vault updates through schema validation, process-wide file locking, atomic write operations, and deterministic journal-based rollback mechanisms.**

The claude-obsidian repository implements a robust transaction layer designed to manage Obsidian vault modifications with ACID-like properties. At its core, the **transaction system in claude-obsidian** coordinates every write operation through a strict lifecycle: pre-flight validation, exclusive locking, durable journaling, atomic execution, and post-operation verification. This architecture ensures that crashes or conflicts never leave the vault in an inconsistent state.

## Core Transaction Schemas and Validation

Every transaction begins with strict schema enforcement. The system defines three primary schemas in [`claude_obsidian/transaction.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/transaction.py) to standardize operation envelopes and audit trails.

### Transaction Schemas

The module declares `BUNDLE_SCHEMA`, `RESULT_SCHEMA`, and `JOUNRAL_SCHEMA` (lines 41-44) to validate the structure of operation bundles, their execution results, and the durable journal entries. These schemas enforce type safety and prevent malformed operations from reaching the execution phase.

### Error Hierarchy

The system centralizes exception handling through a dedicated error hierarchy. `TransactionError` serves as the base exception (lines 54-61), carrying a machine-readable `code` and human-readable message. Specializations include:

- **TransactionConflict**: Raised when concurrent modifications violate consistency
- **TransactionValidationError**: Signals schema or path validation failures
- **TransactionRecoveryError**: Aggregates errors during rollback operations

This hierarchy allows callers to distinguish between recoverable validation issues and critical system failures.

## Safe Path Handling and Security

Before any file operation executes, the system validates all paths through a suite of security functions in [`claude_obsidian/transaction.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/transaction.py).

### Path Normalization and Validation

The `_normalize_vault_path` function (lines 54-71) canonicalizes vault-relative paths, while `_assert_portable_write_path` (lines 88-103) rejects illegal characters, symlink traversal attempts, and case-fold aliases that could enable directory traversal attacks. Supporting utilities like `_safe_vault_path` and `_safe_directory` ensure that every write target remains strictly within the vault boundary, preventing escape from the designated root directory.

## Atomic Operations and Concurrency Control

The transaction system prevents race conditions and partial writes through process-wide locking and atomic file operations.

### MutationLock for Process-Wide Exclusivity

The `MutationLock` class (lines 145-185) implements exclusive access control by acquiring an on-disk lock at `.vault-meta/mutation.lock`. When `apply_bundle` executes, it instantiates this lock with a configurable timeout, blocking until the vault becomes available. The lock guarantees that only one process can modify the vault root and metadata namespace at any given time.

### Atomic File Writes

To eliminate the "partial-write" problem, the system uses `atomic_write` and `_atomic_vault_write` (around line 1249). These functions write data to a temporary file and perform a guaranteed `os.replace` (atomic rename) to move the file into its final position only after the complete content is persisted to disk.

## Journal-Based Recovery System

Durability and recoverability depend on a write-ahead journal that records every intended modification before execution.

### Journal Creation and Verification

When `apply_bundle` initiates, the `_write_journal` function (around line 4000) persists a [`journal.json`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/journal.json) file containing the complete operation plan. After execution, `_validate_journal` compares the journal against the actual filesystem state, ensuring every expected modification occurred. The journal schema tracks file creation, modification, and deletion operations with sufficient metadata to enable reconstruction.

### Deterministic Rollback Mechanism

If validation fails or a crash interrupts execution, `_restore_journal` (lines 4022-4086) executes a deterministic rollback. This function walks the journal in reverse order, utilizing `_confined_vault_unlink` and `_atomic_vault_write` to undo changes safely. Errors during recovery are aggregated and reported as `TransactionRecoveryError`, ensuring administrators receive complete diagnostic information.

## Public API and Workflow

The transaction system exposes a streamlined interface through `apply_bundle`, which orchestrates the entire lifecycle.

### Bundle Planning and Approval

Before execution, clients generate a deterministic approval hash using `plan_approval_sha256` (lines 126-136). This function computes a SHA-256 hash of the expanded bundle combined with the vault identity and prepared write list, creating an auditable fingerprint that must be reviewed before the operation proceeds.

```python
from pathlib import Path
from claude_obsidian.transaction import plan_approval_sha256, safe_operation_id

vault = Path("/path/to/vault")
bundle = {
    "schema": "claude-obsidian.operation.v1",
    "operation_type": "save",
    "payload": {"pages": ["foo.md", "bar.md"]},
}

op_id = safe_operation_id("my-save-run")
approval_hash = plan_approval_sha256(vault, bundle, [])
print(f"Approve this bundle with hash: {approval_hash}")

```

### Applying Transactions

The `apply_bundle` function (lines 4433-4500) serves as the primary entry point for the CLI, SDK, and test suites. It validates the bundle, acquires the `MutationLock`, writes the journal, performs atomic writes, verifies the final state against the approval hash, and cleans up the lock.

```python
from claude_obsidian.transaction import apply_bundle, MutationLock

lock = MutationLock(vault_root=vault, timeout=30.0)
if not lock.acquire():
    raise RuntimeError("Could not obtain mutation lock.")

result = apply_bundle(
    vault_root=vault,
    operation=bundle,
    approved_plan_sha256=approval_hash,
)
print("Transaction result:", result["status"])

```

### Manual Recovery Operations

While recovery typically executes automatically, administrators can trigger manual rollback using `_restore_journal` when holding an active lock:

```python
from claude_obsidian.transaction import _restore_journal
import json

journal_path = vault / ".vault-meta/transactions" / "my-save-run" / "journal.json"
journal = json.loads(journal_path.read_text())

_restore_journal(vault_root=vault, lock=lock, runtime=None, journal=journal)

```

## Implementation Files and Dependencies

The transaction system spans several modules within the repository:

- **[`claude_obsidian/transaction.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/transaction.py)**: Core implementation containing schemas, the `MutationLock` class, atomic I/O primitives, and the `apply_bundle` entry point
- **[`claude_obsidian/paths.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/paths.py)**: Low-level helpers for canonical path resolution, directory file descriptor flags, and platform-specific safety checks
- **[`claude_obsidian/json_utils.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/json_utils.py)**: Provides `parse_finite_json_float` for strict JSON parsing within transaction boundaries
- **[`claude_obsidian/cli.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/cli.py)**: Command-line interface that constructs bundles, displays approval hashes, and invokes the transaction system

## Summary

- **Schema Validation**: Uses `BUNDLE_SCHEMA`, `RESULT_SCHEMA`, and `JOURNAL_SCHEMA` to enforce structure and prevent malformed operations
- **Security**: Implements `_normalize_vault_path` and related functions to block directory traversal and symlink attacks
- **Concurrency**: The `MutationLock` class provides process-wide exclusive access through filesystem-based locking
- **Atomicity**: File writes occur via temporary files and atomic rename operations to eliminate partial writes
- **Durability**: Write-ahead journaling with `_write_journal` enables `_restore_journal` to perform deterministic rollback of failed operations
- **Entry Point**: The `apply_bundle` function orchestrates validation, locking, execution, and verification as the primary public API

## Frequently Asked Questions

### How does claude-obsidian prevent partial writes during transactions?

The system uses atomic file operations implemented in `atomic_write` and `_atomic_vault_write`. These functions write content to temporary files and perform an atomic `os.replace` only after the complete data is persisted. Combined with the `MutationLock` that prevents concurrent modifications, this ensures that no observer ever sees partially written content.

### What happens if a transaction fails midway through execution?

If execution interrupts after the journal is written but before completion, the system detects the incomplete state during the next operation or through explicit validation. The `_restore_journal` function reads the durable [`journal.json`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/journal.json) and rolls back changes in reverse order, using `_confined_vault_unlink` to remove created files and `_atomic_vault_write` to restore original content.

### How does the MutationLock ensure cross-process safety?

The `MutationLock` class creates a dedicated lock directory at `.vault-meta/mutation.lock` and verifies ownership on every operation. Because the lock relies on the filesystem's atomic directory creation semantics and is checked by all transaction entry points, it guarantees that only one process can hold write access to the vault root and metadata namespace at any time, regardless of process boundaries.

### What is the purpose of the plan_approval_sha256 hash?

The `plan_approval_sha256` function generates a deterministic cryptographic hash of the expanded bundle, vault identity, and prepared write list. This creates an immutable audit trail and requires explicit user or system approval before `apply_bundle` executes. The approval mechanism prevents unauthorized or accidental modifications by ensuring the executed operation matches the reviewed plan exactly.