# How the Claude-Obsidian Transaction System Ensures Data Integrity

> Discover how the Claude-Obsidian transaction system guarantees data integrity with locking, cryptographic verification, atomic replacements, and rollback journals. Prevent data loss.

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

---

**The Claude-Obsidian transaction system ensures data integrity through a multi-layered, deterministic model that combines process-level locking, cryptographic verification of file states, atomic file replacements, and deterministic rollback journals to prevent partial writes and race conditions.**

The open-source tool **AgriciDaniel/claude-obsidian** manages Obsidian vault modifications through a rigorously designed transaction engine. This system guarantees that every operation on your knowledge base remains **atomic, consistent, isolated, and durable** (ACID), even when multiple processes attempt concurrent access. Understanding how the transaction system in claude-obsidian ensures data integrity reveals why it is safe to automate vault modifications without risking file corruption or data loss.

## Exclusive Process Locking

At the core of thread safety is a process-held mutation lock that serializes all write operations to a single vault.

### MutationLock Implementation

The `apply_bundle` function in [`claude_obsidian/transaction.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/transaction.py) (around lines 4450-4458) wraps every mutation inside a `MutationLock` context manager. This lock guarantees that only one operation can mutate a vault at any given time. The system enforces this by creating a dedicated lock file and requiring exclusive access before proceeding with any disk writes.

### Platform Confinement Enforcement

Before acquiring the lock, the system calls `_require_write_platform` and `_require_lock_dirfd_support` (lines 6060-6070). On platforms lacking directory-descriptor (`dirfd`) support, the transaction aborts immediately rather than falling back to unsafe implementations. This prevents race conditions on filesystems that cannot guarantee atomic directory operations.

## Cryptographic State Verification

Before applying any changes, the system captures cryptographic fingerprints of the vault and target files to detect external modifications.

### Stable Vault Identity Checks

The function `_vault_object_identity` (lines 1212-1221) captures the vault root's device and inode pair at the start of a transaction. Before any write occurs, the system re-verifies this identity. If the vault has been moved, replaced, or mounted elsewhere, the transaction aborts to prevent writes to an unexpected location.

### Pre-Condition Hash Validation

For every file targeted by the operation, the system computes a SHA-256 hash using `_safe_hash` and captures the file state via `_safe_file_state` (lines 6662-6674). During the application phase, these hashes are re-checked. If any file has changed between planning and execution, the system raises a `TransactionConflict` and aborts, eliminating "write-after-read" corruption bugs.

## Safe Path Handling and Alias Prevention

The system validates all paths to prevent directory traversal, illegal characters, and case-insensitive filesystem collisions.

### Portable Path Validation

The function `_assert_portable_write_path` (lines 896-913) normalizes every write path and checks for illegal characters, trailing spaces or dots, and reserved device names. This ensures compatibility across Windows, macOS, and Linux while preventing malicious path construction.

### Alias Collision Detection

On case-insensitive filesystems, the system calls `_assert_no_existing_portable_alias` to reject paths that might collide with existing files under different casing. This prevents accidental overwrites when "Note.md" and "note.md" reference different logical files but the same physical storage.

## Atomic Write Operations and Journaling

Once validation passes, files are written using atomic operations that guarantee readers never see partial data.

### Atomic File Replacement

The `_atomic_vault_write` function (lines 428-485) writes data to a temporary file with a `.txn-` prefix, calls `fsync` to flush buffers to disk, then performs an atomic rename using `os.replace`. This ensures that a file is either fully updated or remains in its previous state, with no possibility of observing a half-written buffer. The system also normalizes file mode bits via `_portable_file_mode` (lines 1415-1424) to ensure permissions are reproducible across operating systems.

### Durable Rollback Journals

Every operation records its intended changes to a [`journal.json`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/journal.json) file before any permanent modifications occur. If a crash or error interrupts the transaction, the `_recover_incomplete_locked` mechanism (invoked throughout `apply_bundle` at lines 4580-4595) reads this journal to deterministically undo any partial changes, restoring the vault to its original state.

## Immutable Plan Approval

To prevent unauthorized or accidental execution of operations, the system implements cryptographic plan verification.

The `plan_approval_sha256` function (lines 1287-1298) computes a SHA-256 hash over the vault identity, the expanded operation bundle, and the list of prepared writes. When calling `apply_bundle`, you must supply this exact hash as the `approved_plan_sha256` parameter. The transaction aborts if the computed hash differs from the approved value, ensuring that only the exact plan that was reviewed and authorized gets executed.

## Implementing Safe Vault Mutations

The following examples demonstrate how to use the transaction API correctly.

### Planning a Bundle and Obtaining Approval

```python
from claude_obsidian.transaction import plan_approval_sha256

# `bundle` follows the claude-obsidian.transaction.v1 schema

# `prepared_writes` comes from the planner

approval_hash = plan_approval_sha256(
    vault_root="/path/to/vault",
    expanded_bundle=bundle,
    prepared_writes=prepared_writes,
)
print("Approve this hash before applying:", approval_hash)

```

### Applying an Approved Transaction

```python
from claude_obsidian.transaction import apply_bundle

result = apply_bundle(
    vault_root="/path/to/vault",
    bundle_or_path="/tmp/my_operation.bundle.json",
    approved_plan_sha256="e3b0c44298fc1c149afbf4c8996fb924...",
    timeout=30.0,
)
print("Transaction completed:", result)

```

### Handling Conflicts Gracefully

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

try:
    apply_bundle(
        vault_root="/path/to/vault",
        bundle_or_path="/tmp/updates.bundle.json",
        approved_plan_sha256="abc123...",
    )
except TransactionConflict as e:
    # Vault changed externally – retry or abort

    print("Conflict detected, transaction rolled back:", e)

```

## Summary

The Claude-Obsidian transaction system protects vault integrity through these key mechanisms:

- **Process-exclusive locking** via `MutationLock` prevents concurrent write corruption
- **Cryptographic verification** of vault identity and file hashes detects external changes before they cause conflicts
- **Path normalization** and alias rejection prevent filesystem-specific vulnerabilities
- **Atomic file replacement** with `fsync` and `os.replace` guarantees readers never see partial writes
- **Durable journaling** enables deterministic rollback of incomplete transactions
- **Plan approval hashes** ensure only authorized, reviewed operations execute

## Frequently Asked Questions

### What happens if two processes attempt to modify the same vault simultaneously?

The `MutationLock` mechanism in [`claude_obsidian/transaction.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/transaction.py) creates an exclusive lock on the vault directory. The second process blocks until the first transaction completes or times out, ensuring that only one mutation runs at a time and preventing interleaved writes that could corrupt the vault state.

### How does the system detect if a file changed between planning and execution?

Before writing, the system recomputes SHA-256 hashes of all target files using `_safe_hash` and compares them against the values recorded during the planning phase. If any hash differs, the system immediately aborts with a `TransactionConflict` exception, preventing the application of stale assumptions to modified files.

### Can a transaction be safely interrupted or does it require manual cleanup?

Every transaction writes a [`journal.json`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/journal.json) file before modifying any data. If the process crashes or receives a signal, the `_recover_incomplete_locked` function uses this journal to automatically restore all modified files to their original state on the next startup, making the system self-healing without manual intervention.

### Why must I provide an approval hash when applying a bundle?

The `plan_approval_sha256` parameter ensures that the exact plan that was reviewed—capturing the vault state, intended writes, and file contents—is the only one that can execute. This prevents time-of-check to time-of-use (TOCTOU) attacks and accidental execution of modified or malicious bundles between planning and application.