If you've used Obsidian, you know the magic of wikilinks โ type [[filename]] and suddenly your notes are connected. What if you could have that for cloud storage?
This week we built R2 Vault โ an Obsidian-style knowledge graph that runs entirely on Cloudflare's edge, turning scattered R2 buckets into a connected second brain.
The Problem: Islands of Information
When you have multiple AI agents working on different domains (development, security, social media, tutoring), each accumulates its own context and memory. We were storing markdown files across several R2 buckets:
- A collaboration bucket for cross-agent handoffs
- Individual workspace buckets per agent
- Project documentation scattered across repos
The files would reference each other informally ("see the protocol doc" or "check the skills audit"), but there was no way to actually navigate those relationships. Classic information silo problem.
Obsidian solved this for local files. We wanted the same for cloud storage.
The Solution: Wikilinks + D1 + Workers
R2 Vault brings the Obsidian workflow to R2:
- Parses wikilinks in the format
[[bucket/path/to/file.md]] - Indexes relationships into a D1 database (Cloudflare's edge SQLite)
- Exposes a graph API for visualization and queries
- Resolves backlinks so you can see what references any given file
The Wikilink Parser
The parser handles the same patterns you'd use in Obsidian:
// Standard link
[[atlas-collab-pub/protocol.md]]
// Link with display text (pipe syntax)
[[atlas-collab-pub/protocol.md|The Protocol]]
// Relative links (within same bucket)
[[./related-doc.md]]
// Cross-bucket references
[[devflo-workspace-prod/memory/2026-02-24.md]]
The parser extracts source file, target file, link text, and line number โ everything needed to build a proper graph structure.
The Schema
The D1 schema is intentionally simple:
-- Nodes (files)
CREATE TABLE files (
id INTEGER PRIMARY KEY,
bucket TEXT NOT NULL,
path TEXT NOT NULL,
title TEXT,
last_indexed INTEGER,
UNIQUE(bucket, path)
);
-- Edges (links between files)
CREATE TABLE links (
id INTEGER PRIMARY KEY,
source_id INTEGER REFERENCES files(id),
target_bucket TEXT NOT NULL,
target_path TEXT NOT NULL,
display_text TEXT,
line_number INTEGER
);
Views provide convenient access patterns:
v_graph_nodesโ Files with link counts (in/out)v_graph_edgesโ Resolved links with source/target infov_backlinksโ What files link TO a given filev_broken_linksโ Links pointing to non-existent files
That last one is gold for maintenance. Obsidian users know the pain of broken internal links โ now we catch them automatically.
The API Surface
R2 Vault exposes REST endpoints:
| Endpoint | Purpose |
|---|---|
POST /api/index |
Trigger full re-index of all buckets |
GET /api/graph |
Full graph data for visualization |
GET /api/file/:bucket/:path |
Single file with its links |
GET /api/backlinks/:bucket/:path |
What references this file |
GET /api/search?q=term |
Full-text search across indexed files |
GET /api/stats |
Index statistics |
Indexing Strategy
Full re-indexing isn't cheap, so we track last_indexed timestamps and support incremental updates. The indexer:
- Lists objects in each configured R2 bucket
- Filters to
.mdand.jsonfiles - Fetches content and parses for wikilinks
- Upserts file records and link relationships
- Prunes orphaned links from deleted files
One gotcha: R2's list operation is paginated (max 1000 objects per call). The indexer handles pagination correctly, which seems obvious but bit us during initial testing.
Graph Visualization: The Obsidian Experience
The graph view is what makes Obsidian magical. We're using D3.js for a force-directed layout:
- Nodes = files (sized by connection count)
- Edges = wikilinks between files
- Colors = different buckets
- Clusters = densely connected file groups
Click a node to see its content and backlinks. Zoom and pan to explore. It's not as polished as Obsidian's graph yet, but it's functional.
Wildlife Research: An Unexpected Use Case
While building R2 Vault, we also shipped a wildlife research pipeline that demonstrates the multi-agent coordination pattern.
The Setup
We have a trail camera in southeast Oklahoma that syncs photos to an API. A dashboard Worker pulls those events into D1 with full metadata: species, weather conditions, moon phase, barometric pressure.
Two Agents, One Data Source
The same camera data feeds two different agents:
Security Agent (6:00 AM): Looks for unknown vehicles, suspicious activity, unfamiliar people. Posts to a security channel.
Wildlife Researcher (6:15 AM): Analyzes animal behavior. Identifies species, tracks individual deer by antler configuration, correlates activity with weather patterns.
Same data, different lenses. The 15-minute offset ensures they don't step on each other's API calls.
Lessons from the Week
1. Workers Hate new Function()
We tried using Handlebars.js for template rendering. Cloudflare's runtime blocks dynamic code evaluation for security reasons.
The fix: Replace Handlebars with simple regex interpolation.
// Before (broken)
const template = Handlebars.compile(source);
// After (works)
const html = source.replace(/\{\{(\w+)\}\}/g, (_, key) => data[key] ?? '');
2. Incremental > Batch for Indexing
Our first indexer did full re-indexes on every run. With thousands of files across multiple buckets, that's slow. Tracking change timestamps and doing incremental updates cut indexing time by 80%.
3. Graph Visualization is Its Own Project
We have the API. We have the data. Making it look good in a browser is a whole separate challenge. Force-directed layouts, zoom/pan, node clustering โ there's a reason Obsidian spent years on their graph view.
What's Next for R2 Vault
- Auto-linking suggestions โ Surface files that mention similar concepts but aren't explicitly linked
- Embedding search โ Vector similarity for semantic search, not just keywords
- Daily notes โ Auto-create timestamped notes like Obsidian's daily notes plugin
- Publish to web โ One-click publish selected files as a static site
The knowledge graph pattern generalizes well. Any time you have scattered documentation, wikilinks + a graph database give you discoverability for free.
Obsidian changed how we think about personal knowledge management. R2 Vault brings that same connected thinking to cloud storage โ because your AI agents deserve a second brain too.
Ship it. Link it. Graph it.