Back to blog

Building a Persistent Memory System with Claude Code and Obsidian

How I wired Claude Code's auto-memory into an Obsidian vault with symlinks, @imports, and custom agents — creating an AI assistant that remembers across sessions and syncs to my phone.

AIClaude CodeObsidianTooling

Claude Code has a built-in memory system — it can save notes about you, your projects, and your preferences that persist across conversations. Out of the box, these memories live in ~/.claude/projects/ as markdown files. That's useful, but I wanted more: full-text search, backlinks, mobile access, and the ability to manually edit what Claude remembers about me. So I wired it into Obsidian.

Here's how the system works.

The Architecture

There are three layers:

  1. Global memory — shared across all projects, symlinked into Obsidian
  2. Per-project memory — scoped to a single repo, stays in Claude's directory
  3. Obsidian vault — makes everything searchable, editable, and synced via iCloud

The key insight: Claude Code organizes memory by project path. The "global" scope — ~/.claude/projects/-Users-johncarroll/memory/ — applies to every session launched from anywhere under my home directory. That's the one worth syncing.

The Symlink

One symlink connects the two systems:

ln -s "/Users/johncarroll/Library/Mobile Documents/iCloud~md~obsidian/Documents/John's Vault/Development/Claude/Memory" \
      ~/.claude/projects/-Users-johncarroll/memory

Claude Code reads and writes to ~/.claude/projects/-Users-johncarroll/memory/. But because that path is a symlink, the actual files live inside my Obsidian vault at Development/Claude/Memory/. Obsidian indexes them, renders backlinks, includes them in search, and syncs them to iCloud.

When Claude saves a memory, it appears in Obsidian within seconds. When I edit a memory in Obsidian on my phone, Claude picks up the change in the next session.

Memory File Format

Each memory is a standalone markdown file with YAML frontmatter:

---
name: User Profile
description: Role, tools, coding style, interests
type: user
---
 
John Carroll. Software engineer focused on AI tooling
and automation. Primary stack: TypeScript, Next.js,
Supabase, Swift...

There are four memory types:

  • user — who I am, my preferences, how I like to work
  • feedback — corrections and confirmations ("don't mock the database", "yes, single PRs are fine here")
  • project — ongoing work context, deadlines, decisions
  • reference — pointers to external systems (Linear projects, Grafana dashboards, server docs)

A MEMORY.md index file links to everything with one-line descriptions. Claude loads this at the start of every session to decide which memories are relevant.

@Imports: Always-On Context

Some Obsidian notes are so frequently useful that I want them loaded into every Claude session automatically — not searched on demand, but present in context from the start.

Claude Code's @ import syntax in ~/.claude/CLAUDE.md handles this:

@/path/to/vault/Development/Tech Stack.md
@/path/to/vault/Development/Claude Agent Config.md
@/path/to/vault/Development/AI Notes.md

These notes contain my tech stack preferences, agent configuration philosophy, and AI development notes. Editing any of them in Obsidian immediately changes what Claude sees — no deploy step, no sync command, just iCloud doing its thing.

The Obsidian Agent

For deeper vault interaction, there's a dedicated agent defined in ~/.claude/agents/obsidian.md. When Claude needs context beyond what's already loaded, it can dispatch this agent to:

  • Search the vault by keyword, tag, or folder
  • Traverse backlinks to find related notes
  • Read specific notes for detailed context
  • Write new notes back to the vault

The agent uses the Obsidian CLI (requires Obsidian to be running) with a filesystem fallback for when it's not. The permission is pre-approved in settings.json so it never prompts:

{
  "allow": ["Bash(obsidian:*)"]
}

In practice, this means I can say "check my vault for notes about Supabase migrations" and Claude will search Obsidian, read the relevant notes, and use that context to inform its response — without me having to paste anything.

Per-Project Memory

Not everything belongs in the global vault. Per-project memories stay in ~/.claude/projects/{encoded-path}/memory/ and are scoped to that repository:

  • My personal site project remembers my color scheme preference (amber/gold — I explored cobalt twice and reverted both times)
  • My FitKit project remembers the Supabase migration workflow and which MCP tools are banned for DDL
  • Each project has its own MEMORY.md index

These don't sync to Obsidian. They're ephemeral context that only matters when working in that specific repo.

Why This Matters

The default Claude Code experience is already good — it remembers things within a session and can save memories for next time. But the Obsidian integration changes the relationship in a few ways:

Transparency. I can browse everything Claude remembers about me in a familiar interface. No black box. If a memory is wrong or outdated, I fix it in Obsidian and it's fixed for Claude.

Searchability. Obsidian's search, tags, and backlinks make the memory corpus navigable in ways that flat files in ~/.claude/ aren't. I can see connections between memories that Claude might not surface on its own.

Mobile access. iCloud sync means I can review and edit Claude's memory from my phone. Before a work session, I sometimes update project context or add notes that I want Claude to pick up.

Longevity. If I stop using Claude Code tomorrow, the memories are still useful markdown files in my vault. No vendor lock-in on the knowledge layer.

The Full Data Flow

  1. Session starts — Claude loads CLAUDE.md (which @imports vault notes) + the global MEMORY.md index + the current project's memory
  2. Claude learns something — writes a memory file to the global or project memory directory. Global memories land in Obsidian via symlink
  3. I need vault context — Claude dispatches the Obsidian agent to search, read backlinks, and synthesize
  4. I edit in Obsidian — changes to @imported notes appear in the next Claude session. Changes to memory files are immediate via symlink
  5. iCloud syncs — the vault syncs to my phone/tablet, so I can read and edit memory anywhere

Setup Checklist

If you want to replicate this:

  1. Create a folder in your Obsidian vault for Claude memory (e.g., Development/Claude/Memory/)
  2. Symlink Claude Code's global memory directory to that vault folder
  3. Add @ imports in your global CLAUDE.md for notes you want always loaded
  4. Install the Obsidian CLI plugin for search/read/write access
  5. Create an agent definition in ~/.claude/agents/ that knows your vault structure
  6. Pre-approve the CLI commands in settings.json so they don't prompt

The whole setup takes about 15 minutes and fundamentally changes how persistent your AI assistant feels.