# Where Are Captured Content Files Stored in Claude-Obsidian?

> Find out exactly where Claude-Obsidian stores captured content files. Learn about the .raw/captured subdirectory and the logic behind file storage in this guide.

- Repository: [Agrici.Daniel/claude-obsidian](https://github.com/AgriciDaniel/claude-obsidian)
- Tags: how-to-guide
- Published: 2026-08-29

---

**Captured content files are stored in the immutable `.raw/captured` subdirectory of the vault root, with the path enforced by transaction validation logic in [`transaction.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/transaction.py) and constructed via the `_raw_captured_path()` helper in [`capture.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/capture.py).**

The `claude-obsidian` repository implements a content-addressable storage system that captures files from an inbox and moves them into a protected raw payload area. Understanding exactly where captured content files are stored in claude-obsidian is essential for vault maintenance, backup strategies, and debugging capture workflows.

## The `.raw/captured` Directory Structure

Captured files reside under the vault root in a dedicated immutable directory tree. The storage hierarchy follows this pattern:

```

<vault-root>/
 └─ .raw/
     └─ captured/
         ├─ a3f5c9e1b2.bin
         ├─ 7d4e2f9a1c.md
         └─ …

```

This location serves as the **raw payload directory** for all captured content. Once written, files in this tree become read-only for normal operations, ensuring **content immutability** after capture. The system uses content-addressable storage, meaning the filename typically derives from a hash digest of the file contents, often with an appropriate suffix to preserve the original extension.

## How the Capture Path Is Constructed

The [`claude_obsidian/capture.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/capture.py) module builds the destination path through the internal helper `_raw_captured_path()`. According to the source code at lines 872–889, this function resolves the final storage location using:

```python
vault / ".raw" / "captured" / f"{digest}{suffix}"

```

The `capture_filesystem()` function leverages this helper to move files from the inbox into the vault. It calculates the content hash, constructs the relative path, and performs the atomic move operation while preserving metadata about the original location.

## Transaction Validation Enforcement

The system strictly confines capture operations to this specific directory through validation logic in [`claude_obsidian/transaction.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/transaction.py). At line 3239, the code explicitly checks path components:

```python
or parts[:2] != (".raw", "captured")

```

This validation rejects any capture operation that does not target a path whose first two components are exactly `".raw"` and `"captured"`. This security boundary prevents capture primitives from writing to arbitrary locations within the vault, ensuring captured content remains isolated in the designated immutable zone.

## Practical Example

Below is a runnable example demonstrating how to capture a file and verify its storage location:

```python
from pathlib import Path
from claude_obsidian.capture import capture_filesystem

# Initialize vault and source paths

vault = Path("/path/to/my-vault")
source = vault / "inbox" / "notes.md"

# Execute capture operation

result = capture_filesystem(vault, source)

# Verify storage location

print("Captured file stored at:", result["stored_path"])

# Output: .raw/captured/7d4e2f9a1c.md

```

The function returns a metadata dictionary containing the `stored_path` key, which holds the relative path (e.g., `".raw/captured/7d4e2f9a1c.md"`). This path is always relative to the vault root and strictly adheres to the `.raw/captured` prefix enforced by the transaction layer.

## Summary

- **Primary storage location**: All captured files live under `<vault-root>/.raw/captured/`.
- **Path construction**: The `_raw_captured_path()` helper in [`capture.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/capture.py) (lines 872–889) builds content-addressed filenames using hash digests.
- **Validation**: [`transaction.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/transaction.py) line 3239 enforces the `(".raw", "captured")` path prefix, rejecting unauthorized destination paths.
- **Immutability**: The `.raw` tree is read-only for normal operations, guaranteeing captured files cannot be modified after storage.
- **API response**: The `capture_filesystem()` function returns the relative storage path in the `stored_path` field of its result dictionary.

## Frequently Asked Questions

### Can captured files be moved after storage?

No, the `.raw/captured` directory is designed as an immutable storage area. According to the implementation in [`claude_obsidian/capture.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/capture.py), once a file moves into the `.raw` tree, normal operations treat this path as read-only. Moving or modifying captured files would violate the content-addressable integrity guarantees and trigger validation errors in the transaction layer.

### What happens if the `.raw/captured` directory does not exist?

The `capture_filesystem()` function and its helper `_raw_captured_path()` handle directory creation automatically. When initiating a capture operation, the system ensures the full path `vault/.raw/captured/` exists before attempting to move the file, creating parent directories as needed with appropriate permissions.

### How does claude-obsidian prevent captures to arbitrary vault locations?

The transaction validation logic in [`claude_obsidian/transaction.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/transaction.py) explicitly inspects the path components of any capture operation. At line 3239, the code validates that `parts[:2] != (".raw", "captured")` returns False, meaning any path not starting with these exact components gets rejected. This sandboxing ensures capture primitives cannot escape the designated raw payload directory.

### Are file extensions preserved in the captured storage path?

Yes, the `_raw_captured_path()` implementation preserves the original file extension by appending it as a suffix to the content hash. For example, a file named [`document.md`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/document.md) with hash `7d4e2f9a1c` becomes [`7d4e2f9a1c.md`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/7d4e2f9a1c.md) within the `.raw/captured` directory, maintaining type identification while ensuring content-addressable uniqueness.