# Atomic Wiki Writes in ai-memory: How tmp+rename+fsync and Git Checkpoints Ensure Data Integrity

> Learn how atomic wiki writes in ai-memory leverage tmp+rename+fsync and Git checkpoints for crash-resistant file persistence and easy version history recovery.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: internals
- Published: 2026-08-27

---

**Atomic wiki writes in ai-memory combine tmp+rename+fsync operations with Git checkpoints to guarantee crash-resistant file persistence and full version history recovery.**

The ai-memory project implements a robust durability layer for markdown-based wikis that prevents data corruption during system crashes while maintaining complete edit history. By combining POSIX-compliant atomic file writes with lightweight Git snapshots stored in [`crates/ai-memory-wiki/src/atomic.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/atomic.rs) and [`crates/ai-memory-wiki/src/git.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/git.rs), the system ensures that every page save is either fully committed or completely rolled back, with the ability to restore previous states on demand.

## How Atomic File Writes Work (tmp+rename+fsync)

### The write_atomic Implementation

The core crash-resistance mechanism resides in [`crates/ai-memory-wiki/src/atomic.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/atomic.rs), specifically the `write_atomic` function (lines 13-44). This routine implements the classic atomic write pattern using temporary files, explicit fsync operations, and atomic rename system calls.

The implementation follows a strict seven-step sequence:

1. **Create parent directories** using `std::fs::create_dir_all(parent)?` to ensure the target path exists before writing.

2. **Generate a sibling temporary file** with `tempfile::Builder::new().prefix(".ai-memory-tmp.").tempfile_in(parent)?`. Placing the temp file in the same directory guarantees that the subsequent rename operation remains atomic across POSIX and Windows filesystems.

3. **Stage the payload** by writing bytes to the temporary handle via `tmp.write_all(bytes)?`.

4. **Force data to stable storage** by calling `tmp.as_file().sync_data()?` before any rename occurs. This ensures the kernel page cache is flushed to disk.

5. **Execute the atomic rename** through `tmp.persist(path)?`, which atomically replaces the old file with the fully-written temporary file. The operation returns the persisted file handle.

6. **Sync the parent directory** using `File::open(parent).and_then(|d| d.sync_all())` as a best-effort operation to ensure the directory entry containing the new filename is durable.

7. **Return the inode** via `inode_of(path)` so that filesystem watchers can identify and ignore their own writes, preventing feedback loops.

### Platform-Specific inode Handling

The module provides platform-specific implementations of `inode_of` for Unix, Windows, and fallback environments. Each variant extracts the unique file identifier needed by the watcher to distinguish self-generated writes from external modifications, ensuring the atomic write process remains transparent to the rest of the system.

## Git Checkpoints for Version Recovery

While atomic writes prevent corruption, **Git checkpoints** provide temporal recovery capabilities. The `GitAdapter` struct in [`crates/ai-memory-wiki/src/git.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/git.rs) encapsulates repository operations that version every atomic write.

### Initializing and Committing Changes

The `open_or_init` method (lines 44-61) lazily initializes a Git repository in the wiki root, creating a fresh repo only if none exists. When pages change, the system invokes `commit_all` (lines 69-84), which stages all modifications including deletions and creates a lightweight commit with the fixed author `ai-memory <ai-memory@local>`. This method returns `None` when no changes are detected, avoiding empty commits.

### Querying Recent Checkpoints

To support history browsing and rollbacks, `recent_checkpoints` (lines 148-162) walks `HEAD` and returns a vector of `Checkpoint { oid, summary, time }` structures. These checkpoints represent consistent snapshots of the entire wiki tree at specific moments in time.

### Restoring Files from History

The `file_at_rev` method (lines 184-200) enables point-in-time recovery by reading a file's contents as they existed in any previous commit. This allows the wiki layer to restore individual pages without manual git command invocation.

## The Complete Write Flow

When a client persists a page, the `Wiki::write_page` method in [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs) (lines 45-50) orchestrates both mechanisms:

```rust
// Resolve absolute path for the workspace and project
let abs = self.abs_path(workspace_id, project_id, &path);

// 1️⃣ Atomic on-disk write guarantees crash safety
atomic::write_atomic(&abs, raw.as_bytes())?;

// 2️⃣ Git checkpoint creates versioned backup
self.git.commit_all("write page")?;

```

This sequence ensures that filesystem watchers observing the atomic rename receive the updated inode from `write_atomic`, while the subsequent Git commit preserves the state for future recovery. If the process crashes between these calls, the filesystem contains either the old or new file (never partial data), and the next successful write will establish a new checkpoint.

## Practical Implementation Examples

### Writing a Page with Atomic Guarantees

```rust
use ai_memory_wiki::{Wiki, write_atomic};
use std::path::Path;

let page_path = Path::new("project1/notes/todo.md");
let content = b"# TODO\n- Write documentation\n";

// Atomic write prevents torn writes during crashes
write_atomic(&page_path, content)?;

// Create recoverable checkpoint
wiki.git().commit_all("Add TODO page")?;

```

### Listing Available Checkpoints

```rust
let checkpoints = wiki.git().recent_checkpoints(5)?;
for cp in checkpoints {
    println!("{} – {} ({})", cp.oid, cp.summary, cp.time);
}

```

### Restoring from a Specific Revision

```rust
let rev = "a1b2c3d4"; // commit OID from checkpoint list
let rel_path = Path::new("project1/notes/todo.md");

// Retrieve historical content
let bytes = wiki.git().file_at_rev(rev, rel_path)?;

// Restore to current state (optional atomic rewrite)
write_atomic(&rel_path, &bytes)?;

```

## Summary

- **Atomic file writes** in [`crates/ai-memory-wiki/src/atomic.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/atomic.rs) implement the tmp+rename+fsync pattern to eliminate torn writes and corruption during system crashes.
- **Explicit fsync operations** on both the temporary file and parent directory ensure POSIX durability guarantees before the atomic rename commits the new data.
- **Git checkpoints** managed by `GitAdapter` in [`crates/ai-memory-wiki/src/git.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/git.rs) provide lightweight version snapshots after every successful atomic write.
- **Inode tracking** allows filesystem watchers to distinguish between their own atomic writes and external modifications, preventing infinite update loops.
- **Version recovery** is supported through `recent_checkpoints` and `file_at_rev` methods that expose Git history without requiring manual repository manipulation.

## Frequently Asked Questions

### Why does ai-memory use tmp+rename+fsync instead of writing directly to the target file?

Writing directly to the target file risks leaving partially written data on disk if the process crashes mid-operation. The tmp+rename+fsync pattern ensures that readers always see either the complete old file or the complete new file, never a torn state. By fsyncing the temporary file before renaming and the parent directory after renaming, the implementation guarantees that the filesystem metadata and data blocks are durable before acknowledging the write as complete.

### How does the Git checkpoint system handle concurrent writes?

The `GitAdapter` operates serially within the wiki instance. When `commit_all` executes, it stages the current state of the working directory and creates a single commit representing that snapshot. Concurrent file modifications are resolved by the atomic write layer first—each `write_atomic` call completes fully before Git sees the change—ensuring that each checkpoint captures a consistent, crash-proof view of the filesystem at commit time.

### Can atomic wiki writes recover from power failures during fsync operations?

Yes. The implementation in [`atomic.rs`](https://github.com/akitaonrails/ai-memory/blob/main/atomic.rs) performs data synchronization before the rename operation. If power fails during `sync_data()` on the temporary file, the temp file may be incomplete but the original target remains untouched. If power fails during the parent directory fsync, modern filesystems with journaling (ext4, XFS, APFS, NTFS) guarantee that the rename operation will either complete or roll back on recovery, preserving the atomicity guarantee.

### What happens to leftover temporary files if the process crashes?

The `tempfile` crate used in `write_atomic` creates temporary files with the prefix `.ai-memory-tmp.` that are automatically cleaned up by the operating system when the file handle is dropped. If the process crashes before `persist()` completes, the temporary file handle is released and the OS removes the incomplete data. The test suite in [`atomic.rs`](https://github.com/akitaonrails/ai-memory/blob/main/atomic.rs) specifically verifies that no orphaned temporary files remain after failed or interrupted write operations.