Building Stratum: A Privacy-First PKM System That Doesn't Own Your Notes

I started building Stratum two weeks ago. Not because the world needs another note-taking app — but because I couldn't find one that respected my data enough.
Here's the thing about most PKM (Personal Knowledge Management) tools: they own your notes. Obsidian locks you into a proprietary format with .canvas files and a plugin ecosystem that breaks every six months. Logseq stores your blocks in a Datascript database that's opaque to anything but Logseq. Notion can't even export a proper Markdown file without losing half your content. Roam has the decency to use plain files but requires a monthly subscription and a cloud connection.
Every single one of them will either slow to a crawl when you hit 5,000 notes, requires an internet connection to work, or makes your data hostage to their format.
I got tired of choosing between my privacy and my productivity.
So I built Stratum.
The Philosophy
Plain Markdown files on disk. Everything else is a cache.
This is the core idea. Your notes are .md files. Open them in any text editor — VS Code, vim, Emacs, nano, whatever. If Stratum disappeared tomorrow, you'd lose nothing. Zero vendor lock-in. Not because I'm altruistic, but because your notes should outlive your note-taking app.
Every other feature flows from this:
- No proprietary database. Stratum uses SQLite for indexing and Tantivy for search — both are caches that can be rebuilt from your
.mdfiles at any time. The source of truth is always the files on disk. - No cloud dependency. Everything runs locally. Git sync is optional and doesn't require a proprietary server.
- No phone-home telemetry. The app doesn't call home. It's not phoning anywhere. It's an offline program that runs on your machine.
What It Does
Let me walk through the features since I'm most proud of how many shipped in such a short time.
Block Outliner (Logseq-Style)
Every paragraph is an addressable block with a UUID. Indent, outdent, drag to reorder, collapse subtrees. Type [[ to link to another page with autocomplete. Type # for tag autocomplete. Type TODO, DOING, or DONE at the start of a line to set a task marker.
The editor is built on BlockNote — a ProseMirror-based rich text editor that gives me first-class block support, drag handles, slash menus, and formatting toolbars for free. I wired it to a Rust backend that serializes blocks back to Markdown on save.
The source format is human-readable Markdown:
- Project launch plan
.deadline: 2026-07-15
- TODO Draft announcement post
- DOING Prepare demo video
.priority: A
- DONE Finalize feature list
Block properties (.deadline:, .priority:) are key-value pairs that survive round-trips through the editor and any text editor.
Wiki-Links and Backlinks
[[Page Name]] links render inline and are clickable with Ctrl+Click to navigate. A backlinks panel shows all inbound links to the current page plus unlinked mentions (pages that mention your page's title without a proper [[link]]).
The link graph is rebuilt from SQLite — no file-walking on every query.
Knowledge Graph
An interactive force-directed graph (d3-force via react-force-graph) that visualizes your entire wiki-link network. You can:
- Find connected components (groups of linked pages)
- Detect orphaned notes (pages with zero connections)
- Filter by connected/orphaned/tags
- Search for specific nodes
- Click any node to navigate to that page
- Tweak physics parameters (repulsion, link distance, alpha decay)
The graph is fast because it's served by a Rust command that builds the adjacency list directly from the SQLite BlockStore. No file I/O at render time.
Full-Text Search
Powered by Tantivy (Rust's answer to Lucene), search returns results in under 100ms even at 10,000+ notes. Tag search works with #tagname syntax. Results highlight matching blocks and clicking navigates directly to the source page.
Datalog Queries
This is the feature I'm most excited about and the one people find hardest to grasp. Datalog is a declarative query language — think SQL for your knowledge graph. You can write queries like:
:find ?page ?block ?content
:where
[?block :block/page ?page]
[?block :block/content ?content]
[(clojure.string/includes? ?content "Rust")]
Or in JSON if EDN syntax isn't your thing. The query engine runs on the Rust side and returns structured results. This means you can surface insights from your notes that full-text search can't touch — blocks with specific properties, cross-references between tags, blocks created in a date range.
Daily Journal
Auto-created daily notes with calendar navigation. Keep a running log of what you did each day. The journal calendar shows which days have entries at a glance, and clicking a date opens or creates that day's note.
Templates
Define reusable templates with variable substitution. A template is just a Markdown file with {{variable}} placeholders. When you apply a template, Stratum prompts for the variable values and creates a new page. Useful for meeting notes, project kickoffs, weekly reviews — any recurring structure.
Flashcards (Spaced Repetition)
Add question:: and answer:: markers to any block in your notes and they become flashcards. The review system uses the SM-2 algorithm (same as Anki) with configurable daily limits. You review cards in the Flashcards panel, and the scheduler handles intervals automatically.
This means you study in context — a flashcard about Rust ownership lives inside your Rust notes, not in a separate app.
Whiteboards
Powered by Excalidraw, the whiteboard panel gives you an infinite spatial canvas for visual thinking. Mind maps, system architecture diagrams, concept sketches. Whiteboards are saved as Excalidraw files alongside your notes.
Math and Diagrams
- KaTeX — inline and display LaTeX equations with live preview. Type
$E = mc^2$and it renders immediately. There's a full math editor modal with a symbol palette (Greek letters, operators, arrows, sets) for when you don't want to memorize LaTeX codes. - Mermaid — flowcharts, sequence diagrams, Gantt charts, state diagrams, pie charts, and more. Each Mermaid diagram is a custom BlockNote block with a built-in editor and live preview.
AI Assistant (Bring Your Own LLM)
Stratum integrates with any OpenAI-compatible LLM provider. The recommended setup is Ollama running locally — fully offline, free, and private. Configuration is in Settings → AI:
- Pick a provider (Ollama, OpenAI, Anthropic, Google, or custom)
- Set the endpoint or API key
- Assign capabilities to each model (chat, embedding, or both)
- Toggle RAG on for retrieval-augmented generation
Once configured, the editor gives you AI actions through a slash menu:
- Rewrite — improve clarity while preserving meaning
- Format — clean up Markdown syntax
- Structure — organize notes into hierarchical sections
- Summarize — condense text to key points
- Connect — suggest
[[wiki-links]]to related pages - Generate Mermaid — create a diagram from a text description
With RAG enabled, the AI searches your vault for relevant context before responding. The answers are grounded in your knowledge, not just the model's training data.
Web Research
Stratum can perform multi-depth web research via SearXNG (a privacy-respecting metasearch engine). Give it a research topic, and it will search the web, read the linked pages, and synthesize findings into your notes — with sources cited. Configurable search depth lets you control how deep the rabbit hole goes.
Git Sync
Git is the sync layer. Stratum uses git2 to manage the vault as a Git repository:
- Manual sync — click "Sync" to pull, merge, and push
- Auto-commit — every save commits changes (configurable interval)
- Auto-sync — auto-commit + periodic push/pull on a timer
- Conflict detection — a dedicated dialog for merge conflicts
This gives you version history for free. Every change to your knowledge base is tracked. You can git blame any line in any note. You can roll back to any point in time.
And because the vault is just a Git repo, you can use any Git hosting — GitHub, GitLab, your own server — or none at all.
Kanban Boards
Not strictly a note-taking feature, but I wanted it. Each Kanban board is backed by a page — columns become blocks with child cards. Drag-and-drop via @dnd-kit. Cards can have markers, priorities, and due dates.
CLI
There's a full terminal interface bundled as pkm-cli. You can init a vault, list pages, search, export, sync, and run graph analysis from the command line. Useful for scripting, automation, or when you're already in the terminal and don't want to leave.
The Architecture
The stack is a bit unusual, so let me explain the choices.
Tauri v2 is the desktop shell. It bundles a Rust backend with a webview-based frontend. This gives me native performance for the data layer with React's ecosystem for the UI.
The Rust side is split into 10 crates:
pkm-core — types, config, errors
pkm-block — block model, tree operations, SQLite store
pkm-markdown — block-aware Markdown parser and serializer
pkm-index — backlinks, graph, Tantivy search
pkm-query — Datalog query engine
pkm-sync — Git operations via git2
pkm-watcher — file system watcher
pkm-ai — embeddings, RAG, LLM provider abstraction
pkm-plugin — WASM plugin runtime (experimental)
pkm-cli — terminal binary
The Tauri shell (src-tauri) depends on all these crates and exposes ~70 Tauri commands to the frontend. The commands are thin glue — all business logic lives in the crates where it belongs.
The frontend is React 19 with TypeScript, using:
- Zustand for state management (4 stores: app, settings, graph, sync)
- BlockNote for the outliner editor
- MUI v9 for UI components
- react-force-graph-2d for the knowledge graph
- Excalidraw for whiteboards
- KaTeX and Mermaid for math and diagrams
Data flow is strictly unidirectional: component → Tauri invoke → Rust command → crate logic → response. No caching layer on the frontend, no optimistic updates, no sync conflicts — the Rust side is the authority.
The codebase has 258 commits, ~217 tests (and counting), and every crate is independently testable. The Markdown parser, the block engine, the Datalog query evaluator — all have dedicated test suites.
The Flutter → Tauri Story
This project started as a Flutter app. Literally. The first commit was a page-centric PKM with a Flutter frontend and a MockBackend for the UI. I knew Flutter wouldn't be right long-term — webview isolation, plugin fragility, and the sheer complexity of maintaining a Flutter + Rust bridge (flutter_rust_bridge) made me switch.
The second commit was the plan to migrate. By the third commit, I had the block engine crate. By commit 10, I had a Tauri + React frontend rendering blocks.
The migration took about 7 hours, start to finish.
Why Tauri won:
- Rust + webview — the Rust backend was already written. I just needed to swap the UI layer.
- React ecosystem — BlockNote, MUI, d3-force, Excalidraw — all available as npm packages. No Flutter ports, no plugin hell.
- Native performance — Tauri apps are tiny (~5MB bundle) and use minimal memory. The Tauri shell is written in Rust, so there's no JS runtime overhead for the backend.
- Cross-platform — desktop (Linux, macOS, Windows) and mobile (Android, iOS) from a single codebase. Android builds work. iOS is in progress.
The Numbers
After two weeks of active development:
- 10 Rust crates, one Tauri shell
- 70+ Tauri commands bridging Rust to React
- 19 feature guides in the documentation
- ~2,500+ lines of Rust business logic
- 258 commits
- AGPL-3.0 licensed, fully open source
Performance targets I'm hitting:
| Metric | Target | Status |
|---|---|---|
| Cold start (empty vault) | <500ms | ✅ |
| Search at 10k notes | <100ms | ✅ |
| Graph load at 10k notes | <2s | ✅ |
| Note save latency | <50ms | ✅ |
| Memory (idle, desktop) | <80MB | ✅ |
Why AGPL-3.0?
I chose AGPL-3.0 because knowledge management should be a public good, not a SaaS play. The AGPL ensures that anyone who uses Stratum as part of a service must release their modifications. This prevents the "open source core, proprietary cloud" model that's infected so many projects.
If you want to use Stratum, build on it, or fork it — go ahead. Just keep it free for everyone.
What's Next
Version 0.5.0 is the current release. All core features work — block editor, graph, search, Datalog, journal, templates, flashcards, whiteboards, AI, Git sync, CLI. But there's plenty more to build:
- WASM plugin runtime (experimental, needs hardening)
- iOS build (Android APK/AAB builds work)
- Performance optimization at 50k+ note scale
- Advanced query UI for the Datalog engine
- Mobile-optimized UI (responsive layouts exist, need polish)
Try It
If this resonates, here's how to get started:
# Nix (recommended)
nix develop ./nix
cargo build --workspace
npm install
cargo tauri dev
# Or manual (Rust 1.75+, Node.js 22+)
cargo build --workspace
npm install
npm run tauri:dev
The repo is at github.com/short-circuit/stratum. Docs are at short-circuit.github.io/stratum/docs.
The Bottom Line
I built Stratum because I wanted a note-taking app that respects me. One that doesn't own my data, doesn't require a subscription, doesn't phone home, and doesn't slow down as my knowledge grows.
Two weeks in, it's already my daily driver. All my notes are in it. All my research goes through it. My graph has over 200 nodes and growing.
Your notes should outlive your note-taking app. Stratum is my attempt to make that a reality.
— Davide
If you found this interesting, you could buy me a coffee and/or star the repo. Contributions welcome.