case study · since 2026-08
Anotadinho
"I like it this way."
A native, offline-first Markdown note editor inspired by Obsidian and Logseq. A personal project I use to really learn Rust: crate-based architecture, tests, a CLI, a TUI and an MCP server for AI agents.
- lines of Rust
- 55,328
- automated tests
- 851
- commits
- 345
- crates
- 6
Decisions
Why Rust?
I wanted to leave my Python/TypeScript comfort zone and truly understand memory, ownership and concurrency. A note editor has a parser, I/O, an index and a UI — it covers almost everything.
Why Tauri + Yew?
~1 MB bundle vs ~100 MB for Electron, and the UI is Rust too (compiled to WASM), with React-like components and hooks. I evaluated Dioxus, Leptos and Svelte before deciding.
Why .md files?
Offline-first with no lock-in: the vault is a plain folder, syncable by USB stick or any copy, readable in another editor ten years from now.
Architecture
A Cargo workspace of isolated crates. The Yew UI talks to the core only through Tauri IPC commands; the CLI and TUI reuse the same core.
↓ tauri::command / direct calls ↓
↓
core
block model, Markdown parser, embeds, index, queries
vault
file I/O, watcher, locks
search
full-text search with FTS5
Embeds that stay Markdown
Everything lives in .md files with YAML frontmatter. The 9 interactive embed types use a {{ type: "X" }} syntax that never collides with CommonMark, so files stay readable in any editor.
- kanban
- calendar
- table
- callout
- columns
- gallery
- query
- timeline
- actions
{{ type: "kanban" }}
columns:
- name: A fazer
cards:
- title: Revisar parser de embeds
checklist: [testes, docs]
- name: Feito
cards:
- title: Servidor MCP
{{ /kanban }}AI proposes, human decides
The MCP server (JSON-RPC over stdio) gives agents like Claude Code direct read access to the vault — but the only write tool is "propose". Changes land on a review screen and only hit disk after a human click.
//! As ferramentas de LEITURA são diretas. A de ESCRITA é `propor`,
//! não `escrever`: o agente não tem como gravar uma página, só
//! sugerir. O que ele propõe aparece na tela de revisão e só vira
//! arquivo depois de um clique humano.
//!
//! JSON-RPC 2.0 em linhas, sobre stdin/stdout: uma requisição por
//! linha, uma resposta por linha. É o transporte stdio do MCP.
use serde_json::{json, Value};
use std::io::{BufRead, Write};
/// Versão do protocolo que este servidor fala.
const VERSAO_PROTOCOLO: &str = "2024-11-05";crates/cli/src/mcp.rs
Cycle-driven development
The project evolves in cycles, not loose commits: each cycle reads a task, implements it, runs tests, checks acceptance criteria and records status. That keeps 800+ tests green across 55k lines.
One core, three interfaces
The same Rust core powers the desktop app (Tauri + Yew/WASM, ~1 MB vs ~100 MB for Electron), the CLI and the terminal TUI.