# How Rowboat Implements an Obsidian-Compatible Markdown Vault and Backlink System

> Explore Rowboat's Obsidian-compatible Markdown vault and wiki-style backlink system. Learn how it stores knowledge locally and resolves links using helper utilities in wiki-links.ts.

- Repository: [RowBoat Labs/rowboat](https://github.com/rowboatlabs/rowboat)
- Tags: how-to-guide
- Published: 2026-02-16

---

**Rowboat stores its knowledge base as a local Obsidian-compatible Markdown vault in `~/.rowboat/knowledge/`, using wiki-style `[[...]]` backlinks that resolve to canonical file paths through helper utilities in [`wiki-links.ts`](https://github.com/rowboatlabs/rowboat/blob/main/wiki-links.ts).**

The `rowboatlabs/rowboat` repository maintains a fully functional Obsidian-compatible Markdown vault that enables bidirectional linking between notes. This local-first architecture organizes knowledge into categorized markdown files while preserving compatibility with standard Obsidian workflows and syntax.

## Vault Structure and Organization

### Directory Layout in `~/.rowboat/knowledge/`

Rowboat creates a dedicated workspace directory inside the user's home folder at `~/.rowboat/`. Within this workspace, the vault resides under the **`knowledge/`** subdirectory. According to the assistant instructions defined in [`apps/x/packages/core/src/application/assistant/instructions.ts`](https://github.com/rowboatlabs/rowboat/blob/main/apps/x/packages/core/src/application/assistant/instructions.ts) (lines 41-45), this folder contains "plain markdown with Obsidian-style backlinks" organized into specific categories.

### The Four Top-Level Categories

The vault enforces a strict four-folder taxonomy that maps directly to markdown sub-folders:

- **People** (`knowledge/People/`) — One markdown file per person (e.g., `John Smith.md`)
- **Organizations** (`knowledge/Organizations/`) — One file per company or team
- **Projects** (`knowledge/Projects/`) — Files describing ongoing initiatives
- **Topics** (`knowledge/Topics/`) — Subject-matter notes and recurring themes

Each note is a standard `.md` file that may contain **Obsidian-style wiki links** (`[[…]]`) pointing to other notes in the same vault.

## Backlink Implementation Architecture

### Wiki-Link Normalization Utilities ([`wiki-links.ts`](https://github.com/rowboatlabs/rowboat/blob/main/wiki-links.ts))

The core backlink logic resides in [`apps/x/apps/renderer/src/lib/wiki-links.ts`](https://github.com/rowboatlabs/rowboat/blob/main/apps/x/apps/renderer/src/lib/wiki-links.ts). This module provides canonicalization functions that ensure every wiki link resolves to a valid vault path:

- **`stripKnowledgePrefix`** — Removes the leading `knowledge/` prefix if present (lines 3-5)
- **`normalizeWikiPath`** — Trims leading slashes or `./` and then strips the prefix (lines 6-9)
- **`ensureMarkdownExtension`** — Guarantees the path ends with `.md`, adding the extension if missing (lines 11-15)
- **`toKnowledgePath`** — Transforms a raw wiki link (e.g., `[[People/John]]`) into a full workspace path ([`knowledge/People/John.md`](https://github.com/rowboatlabs/rowboat/blob/main/knowledge/People/John.md)). Returns `null` for illegal paths (empty, containing `..`, or trailing slash) (lines 17-21)
- **`wikiLabel`** — Generates a human-readable label from a link path (file name without extension) (lines 23-27)

These utilities guarantee that any backlink is **always a valid, markdown-backed, Obsidian-compatible reference** within the vault.

### Serializing ProseMirror Nodes to Markdown

When the rich-text editor serializes its ProseMirror document, the [`markdown-editor.tsx`](https://github.com/rowboatlabs/rowboat/blob/main/markdown-editor.tsx) component (located at [`apps/x/apps/renderer/src/components/markdown-editor.tsx`](https://github.com/rowboatlabs/rowboat/blob/main/apps/x/apps/renderer/src/components/markdown-editor.tsx)) converts `wikiLink` nodes into literal Obsidian syntax:

```tsx
else if (node.type === 'wikiLink') {
  const path = (node.attrs?.path as string) || ''
  blocks.push(`[[${path}]]`)
}

```

This logic (lines 33-36) ensures that **backlink data is stored exactly as Obsidian expects** — a double-bracket link referencing another markdown file inside the vault.

### Runtime Link Resolution and File Creation

The application handles backlink interactions through two primary flows in [`App.tsx`](https://github.com/rowboatlabs/rowboat/blob/main/App.tsx):

**Opening an existing link** — When a user clicks a wiki link, the application resolves the clicked text using `toKnowledgePath`, then ensures the target file exists via `ensureWikiFile` before opening it in the editor (lines 2020-2027).

**Creating a new note** — The *Create* option in the wiki-link command palette invokes `wikiLinks.onCreate`, which downstream calls `workspace-writeFile` with the computed knowledge path.

UI components such as [`mention-popover.tsx`](https://github.com/rowboatlabs/rowboat/blob/main/mention-popover.tsx) (line 71) utilize `wikiLabel` to render human-readable link titles without file extensions.

## Practical Usage Examples

### Creating New Notes Programmatically

To create a new person note from a React component:

```tsx
// Creates knowledge/People/Aria Lee.md if it doesn't exist
wikiLinks?.onCreate?.('People/Aria Lee')

```

The `onCreate` callback internally invokes `toKnowledgePath('People/Aria Lee')` to generate `knowledge/People/Aria Lee.md`, ensures the `.md` extension is present, and writes the file via the workspace API.

### Writing Backlinks in Markdown

Inside any vault note (e.g., [`knowledge/Projects/Alpha.md`](https://github.com/rowboatlabs/rowboat/blob/main/knowledge/Projects/Alpha.md)):

```markdown
We discussed the timeline with [[People/John Smith]] and [[Organizations/Acme Corp]].

```

When the editor serializes, each `[[…]]` reference becomes a `wikiLink` node that preserves the original syntax, maintaining full Obsidian compatibility.

### Resolving Links in Application Code

```ts
import { toKnowledgePath, wikiLabel } from '@/lib/wiki-links'

const raw = 'People/John Smith'               // user input from command palette
const fullPath = toKnowledgePath(raw)          // → 'knowledge/People/John Smith.md'
const label = wikiLabel(raw)                   // → 'John Smith'

```

The `fullPath` variable can be passed to `workspace-readFile` to fetch note contents, while `label` provides UI display text.

## Key Source Files and Responsibilities

| File | Role |
|------|------|
| [`apps/x/packages/core/src/application/assistant/instructions.ts`](https://github.com/rowboatlabs/rowboat/blob/main/apps/x/packages/core/src/application/assistant/instructions.ts) | Defines the vault layout conventions and required path structures for the assistant. |
| [`apps/x/apps/renderer/src/lib/wiki-links.ts`](https://github.com/rowboatlabs/rowboat/blob/main/apps/x/apps/renderer/src/lib/wiki-links.ts) | Core utilities for normalizing, validating, and translating wiki links to canonical vault paths. |
| [`apps/x/apps/renderer/src/components/markdown-editor.tsx`](https://github.com/rowboatlabs/rowboat/blob/main/apps/x/apps/renderer/src/components/markdown-editor.tsx) | Serializes ProseMirror documents, converting `wikiLink` nodes into `[[…]]` markdown syntax. |
| [`apps/x/apps/renderer/src/App.tsx`](https://github.com/rowboatlabs/rowboat/blob/main/apps/x/apps/renderer/src/App.tsx) | Orchestrates opening and creating wiki-linked notes, ensuring target files exist before editing. |
| [`apps/x/apps/renderer/src/components/mention-popover.tsx`](https://github.com/rowboatlabs/rowboat/blob/main/apps/x/apps/renderer/src/components/mention-popover.tsx) | Renders human-readable link labels using `wikiLabel` for command palette and mention interfaces. |
| `apps/x/packages/core/src/knowledge/*` | Implements read/write operations for the knowledge base used by the assistant's memory agents. |

## Summary

- Rowboat maintains an **Obsidian-compatible Markdown vault** in `~/.rowboat/knowledge/` with four top-level categories: People, Organizations, Projects, and Topics.
- **Backlinks use standard Obsidian wiki-link syntax** (`[[Category/Note Name]]`) stored as plain text in markdown files.
- The **[`wiki-links.ts`](https://github.com/rowboatlabs/rowboat/blob/main/wiki-links.ts) utility library** canonicalizes paths, ensures `.md` extensions, and prevents directory traversal attacks by rejecting paths containing `..`.
- **ProseMirror serialization** in [`markdown-editor.tsx`](https://github.com/rowboatlabs/rowboat/blob/main/markdown-editor.tsx) converts rich-text wiki-link nodes into literal `[[…]]` strings for persistent storage.
- **Runtime resolution** in [`App.tsx`](https://github.com/rowboatlabs/rowboat/blob/main/App.tsx) automatically creates missing target files when users click backlinks, ensuring the graph remains navigable even for dangling references.

## Frequently Asked Questions

### Where does Rowboat store the Obsidian-compatible vault locally?

Rowboat stores the vault inside the user's home directory at `~/.rowboat/knowledge/`. This location is treated as the workspace root, with four subdirectories (People, Organizations, Projects, Topics) organizing the markdown files according to the conventions defined in [`apps/x/packages/core/src/application/assistant/instructions.ts`](https://github.com/rowboatlabs/rowboat/blob/main/apps/x/packages/core/src/application/assistant/instructions.ts).

### How does Rowboat handle wiki-links that reference non-existent notes?

When a user clicks a wiki-link in the UI, [`App.tsx`](https://github.com/rowboatlabs/rowboat/blob/main/App.tsx) invokes `toKnowledgePath` to resolve the link to an absolute vault path, then calls `ensureWikiFile` (lines 2020-2027) to verify the file exists. If the target is missing, Rowboat automatically creates an empty markdown file at the resolved path before opening it in the editor, ensuring seamless navigation even for new or "dangling" references.

### What are the four main categories in the Rowboat knowledge vault?

The vault organizes content into four top-level folders under `knowledge/`: **People** (individuals), **Organizations** (companies and teams), **Projects** (ongoing initiatives), and **Topics** (subject-matter notes and recurring themes). Each category contains individual markdown files representing specific entities within that classification.

### Which source file contains the core logic for normalizing wiki-link paths?

The primary normalization logic resides in [`apps/x/apps/renderer/src/lib/wiki-links.ts`](https://github.com/rowboatlabs/rowboat/blob/main/apps/x/apps/renderer/src/lib/wiki-links.ts). This module exports `toKnowledgePath`, `normalizeWikiPath`, `ensureMarkdownExtension`, and `wikiLabel`, which collectively handle path canonicalization, extension enforcement, security validation (rejecting `..` traversal), and human-readable label generation for the Obsidian-compatible backlink system.