Content
<p align="center"><img src="assets/wordmark.svg" alt="sealmind" height="72"></p>
<p align="center"><b>A single-binary markdown memory for AI agents. Zero third-party dependencies.</b></p>
<p align="center">Multi-file search (txt/code/csv/json/html + PDF via plugins) · first-class Bitwarden secrets · MCP + CLI</p>
---
sealmind is a local memory you own end to end: your knowledge is plain markdown
files (`domain/project/topic.md`), sealmind indexes them with a fast BM25 engine and
serves search to any harness over MCP. One binary, pure Rust std — no
Python, no vector DB, no cloud, nothing to audit but the source in this repo.
It also ingests **non-markdown** sources (code, CSV, JSON, HTML, …) into derived
searchable `.md` chunks, and resolves **secret pointers** (Bitwarden `bw`/`bws`,
SOPS) without ever printing a value.
The architecture was chosen by an **empirical bake-off**, not taste — see
[`docs/benchmark_report.md`](docs/benchmark_report.md) / [`benchmark.html`](docs/benchmark.html).
Plain-markdown + BM25 hybrid beat a ChromaDB palace on speed, footprint, *and* the
hard gates (hand-editable, Obsidian-visual) that a vector DB can't pass.
## Why
- **Find** — BM25 search with tag / domain / type / date / source-type facet filters + match snippets, sub-millisecond, no embedding downloads.
- **Multi-file** — `ingest` extracts text from `.py`/`.rs`/`.csv`/`.json`/`.html`/… into derived searchable chunks (heading-aware, determinism-checked).
- **Visual** — it's a folder of markdown; open it in Obsidian for the graph.
- **Simple** — markdown + a tiny YAML frontmatter contract. If you can `cat` it, you can read it.
- **Yours** — files are the source of truth; the index is a rebuildable cache.
- **Any harness** — one MCP server (Claude Code, Codex, …) + a plain-file fallback.
- **Secrets stay out** — knowledge holds *pointers* (`bw://` / `bws://` / `sops://`); values are resolved to a safe directive, never printed or stored.
## Install
One line — grabs a prebuilt binary (or builds from source), creates a vault, prints the config:
```bash
curl -fsSL https://raw.githubusercontent.com/FiezDev/sealmind/master/install.sh | sh
```
Or pick your method:
```bash
cargo install --git https://github.com/FiezDev/sealmind sealmind # from source
cargo binstall sealmind # prebuilt binary
# or download from https://github.com/FiezDev/sealmind/releases
```
Auto-wire the MCP server into Claude Code + Codex:
```bash
./tools/setup.sh ~/sealmind-vault --wire
```
## Use
The vault resolves from `--vault`, else `$SEALMIND_VAULT`, else `~/sealmind-vault` —
so `--vault` is optional once the env var is set.
```bash
sealmind index # report topic count
sealmind search "postgres bigint gotcha" # BM25 search (table)
sealmind search "pg" --domain work --type learning --since 2026-01-01 --json --k 8 # facets + JSON
sealmind search "deploy" --source-type py # scope to chunks from a given source type
sealmind remember --title "X" --content "..." --domain work --project svc --type learning --tag postgres
sealmind remember --title "Prod DB" --content "creds" --secret "bws://secret/abc-123" --domain work --project svc
sealmind ingest ./runbook.py --domain work --project svc # extract a non-md file → derived searchable chunks
sealmind ingest ./config.json --domain work --project svc # (txt/md/csv/tsv/json/yaml/toml/html + code)
sealmind read "postgres-bigint-gotcha" # print a topic (path or title/slug)
sealmind supersede "old-note" --successor-title "New note" --successor-content "..."
sealmind list --domain work # list topics (optionally scoped)
sealmind stats # vault overview (counts, sizes, autocommit health)
sealmind doctor # health checks: vault, sources, secrets, yield
sealmind secret resolve "Prod DB" # resolve a pointer → safe directive (NEVER the value)
sealmind secret resolve "bw://item/<id>/password" # ...or a raw pointer
sealmind --version
sealmind mcp # MCP stdio server for agents
```
Register the MCP server once and every agent shares the vault:
- **Claude Code** — `.mcp.json`:
```json
{ "mcpServers": { "sealmind": {
"command": "sealmind", "args": ["mcp", "--vault", "/abs/path/to/vault"] } } }
```
- **Codex** — `~/.codex/config.toml`:
```toml
[mcp_servers.sealmind]
command = "sealmind"
args = ["mcp", "--vault", "/abs/path/to/vault"]
```
The MCP server exposes twelve tools; you edit the same `.md` files in any editor or Obsidian:
| Tool | What it does |
|---|---|
| `search` | BM25 search with optional `domain`/`project`/`type`/`tag`/`since`/`until`/`source_type` facet filters; returns a JSON array of hits, each with a match snippet (superseded topics excluded by default) |
| `read_note` | Read a topic's full frontmatter + body (contained to the vault) |
| `remember` | Store a new topic — refuses a likely duplicate (title *or* body), honors the frontmatter contract, accepts an optional `secret:` pointer |
| `supersede` | Mark a stale topic `superseded` and link it to a corrected one — **nothing is deleted** |
| `ingest` | Extract text from a non-markdown source file (txt/md/csv/json/yaml/html/code), chunk it, write derived searchable `.md` Topics with provenance. Re-ingest of an unchanged file is a no-op |
| `secret_resolve` | Resolve a `secret:` pointer (`bw://`/`bws://`/`sops://`) to a **safe directive** — status, redacted preview, env-injection command. **Never returns the secret value** (no reveal code path exists) |
| `secret_propose` | Propose a Bitwarden write (create Login/Note/Identity/Folder, delete item) — returns a nonce + approve command. **Does not execute**; the human runs `sealmind secret approve <nonce>` on a real terminal. Secret values flow over stdin, never argv |
| `list_domains` · `list_projects` · `list_notes` | Enumerate the vault's taxonomy, so an agent reuses an existing domain/project instead of inventing one |
| `stats` | Topic counts by domain/type, active vs superseded, sizes, autocommit provenance health |
| `get_related` | Follow `[[wikilinks]]` — the inbound + outbound neighbors of a note |
Writes go through a duplicate check (sealmind searches its own index before creating) and land
as plain markdown — byte-identical to what a human writes. Corrections use `supersede`, not
overwrite, so the old fact stays in git history and the current fact is what search returns.
`remember` is reachable three ways: the MCP tool above, the `sealmind remember` CLI
subcommand (same fields as flags, same dedup gate), and `df-memory` (below).
## Auto-capture sessions with df-memory
`df-memory` is a standalone memory-capture skill that turns a coding session into
durable memory. It runs the LLM extraction **out-of-band**, proposes topics,
confirms with you (interactive) or keeps high-confidence items only (non-TTY/hook),
then writes each via `sealmind remember` — so dedup, frontmatter, and search all
work unchanged.
The skill lives in the **[DEV-FLOW](https://github.com/FiezDev/DEV-FLOW)** repo
(`skills/df-memory/`), not here — sealmind is the engine, df-memory is the
capture surface. Install it as an agent skill and it's invocable as `/df-memory`.
```bash
df-memory # capture the most-recent session for cwd
df-memory --transcript ~/.claude/projects/…/abc.jsonl # capture a specific session
df-memory --yes # non-interactive: skip the Y/n prompt
```
Auto-wire it as a **Claude Code** and **Codex** hook so sessions get captured
without you remembering to run anything:
```bash
# from the DEV-FLOW repo, after cloning
./skills/df-memory/scripts/install-hooks.sh ~/sealmind-vault
```
This installs `df-memory` to `~/.local/bin`, registers a `Stop` hook in
`~/.claude/settings.json` (Claude Code) and `~/.codex/config.toml` (Codex), and
is idempotent. The hook is **debounced** (~once per 10 min per session), so most
turns are a silent no-op rather than a per-turn cost.
The LLM backend auto-detects in this order: `claude -p` → `codex` headless →
`curl` against `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`. Override with
`DF_MEMORY_BACKEND`, pick a model with `DF_MEMORY_MODEL`, or tune the debounce
with `DF_MEMORY_DEBOUNCE` (minutes).
## The vault
```
vault/
<domain>/<project>/<topic>.md # data-type / project / one atomic knowledge doc
<domain>/<project>/_index.md # per-project index
_HOME.md # top index
```
Topic frontmatter (small, optional, agent- and Obsidian-queryable):
```markdown
---
title: Postgres BIGINT serializes oddly in GraphQL
type: learning # learning | retro | reference | decision | note
domain: work # work | personal | meta | reference
project: my-service
tags: [postgres, graphql]
created: 2026-07-04
status: active # active | superseded
secret: null # or a POINTER — bw://item/<id>/password | bws://secret/<id> | sops://secrets/x.env — never a value
---
# Postgres BIGINT serializes oddly in GraphQL
Body prose, with [[wikilinks]] to related topics.
```
Derived chunks (from `ingest`) carry extra provenance fields — `source_path`,
`source_type`, `source_sha`, `source_chunk`, `heading_path` — so you can scope
search to a source type and each chunk knows where it came from.
`vault-sample/` holds a few example topics. (Your real vault is yours — keep it in a
private repo or gitignored.)
## Layout
```
crates/sealmind-core/ zero-dep engine: vault parse + BM25 index + search + write + extract + chunk + secret-parse + doctor
crates/sealmind/ the binary: CLI (index/search/remember/ingest/secret/doctor/mcp) + MCP stdio server + shell-out liveness
vault-sample/ example topics
docs/ architecture + the benchmark that chose this design + spec/<task>/ design doc packs
tools/ bake-off harness + setup.sh (df-memory lives in DEV-FLOW)
assets/ logo
```
## Safety
A memory tool is only useful if you can trust it. sealmind is built to fail closed on
hostile or malformed input rather than crash or eat memory. The v0.9.0 release adds
Bitwarden write support behind a per-command TTY authorization gate; the v0.8.0
release was hardened through grill + steelman + redteam design gates — see the
[CHANGELOG](CHANGELOG.md) and [SECURITY.md](SECURITY.md) for the public design
record.
- **Bounded JSON parser** — the hand-rolled MCP JSON-RPC parser caps nesting depth, so a
malformed `[[[[…` message returns an error instead of overflowing the stack.
- **Bounded input + resource caps** — MCP lines over 8 MB and vault files over 4 MB are
skipped; `ingest` pre-checks source size (`MAX_SOURCE_BYTES`, 64 MB), caps chunks
(`MAX_CHUNKS_PER_SOURCE`, 50) and tokens (`MAX_TOKENS_PER_DOC`, 50k), so a 2 GB file
is refused pre-read, not OOMed. All caps are env-overridable.
- **Liveness** — every shell-out (`git`, `bw`, `bws`) and file read routes through
`run_deadline`, which **kills + reaps** the child on timeout; a stuck backend can't
block the single-threaded MCP server. A panic in any tool is caught (`catch_unwind`)
and degrades to an `is_error` result — the server stays alive.
- **Rate-limited** — `secret_resolve` (20/min) and `ingest` (30/min) are token-bucket
capped so an agent loop can't spawn thousands of subprocesses.
- **No symlink following** in the vault walk — no cycles, no re-reading the same note.
Derived-asset (`.assets/`) and cache (`.sealmind/`) dirs are skipped.
- **Agent-supplied paths are contained** — `read_note` only reads files inside the vault
(no `/etc/passwd`, no `../` escape), and `remember`'s `domain`/`project`/`type` are
charset-validated so a note can never be written outside the vault. `sops://` pointers
are containment-checked too; sealmind performs NO byte read on sops targets.
- **Frontmatter is injection-safe** — field values are escaped (newlines + control chars
included), so a crafted title, heading, or body can't break the `---` fence or forge
another note's metadata. Secret-pointer fields + ids are charset-validated.
- **Secrets never leak** — `secret_resolve` returns a safe directive (status, redacted
preview, env-injection command) and **never** the value; there is no reveal code path.
URI fields are a closed set (`bw://item/<id>/<field>`), so flag-injection
(`--session=…`) is refused at parse time. Distinct lock states
(`not_logged_in` / `locked` / `backend_error`) carry actionable hints.
- **Merge-overwrite preserves pointers** — a bare `remember` overwrite no longer
silently wipes a `secret:` pointer or a derived chunk's provenance.
- **Nothing is deleted** — sealmind writes and overwrites notes (atomic temp-file + rename, unique per process), and `supersede` only flips a note's `status`; it rewrites files but never unlinks them.
- **Loud failures** — `serve` refuses to start if the vault dir doesn't exist (no more
silent 0-topic indexing on a typo'd path); `doctor` reports vault/source/secret/yield
health as PASS/WARN/FAIL.
- **Verified downloads** — release binaries ship a `.sha256` sidecar and `install.sh` refuses a mismatch.
See [`SECURITY.md`](SECURITY.md) for the full model (including the Bitwarden resolver's
invariants + honest residual risks) and how to report a vulnerability.
## Status & roadmap
See [`CHANGELOG.md`](CHANGELOG.md) for the full release history.
- ✅ BM25 search (Thai/CJK-aware) with tag / domain / type / date / source-type **facet filters** + match snippets — zero deps.
- ✅ Agent write path: `remember` / `supersede` + `sealmind remember` CLI — dedup-gated (title *and* body), files-first, nothing deleted. `--secret` sets a pointer at write time.
- ✅ Discovery + graph: `list_domains` / `list_projects` / `list_notes`, `stats`, and `get_related` (`[[wikilink]]` neighbors).
- ✅ **Multi-file search** (`ingest`): txt/md/csv/tsv/json/yaml/toml/html + code → derived searchable chunks; Part-IR + structure-aware chunker; loud refusal floor (never silently indexes garbage).
- ✅ **First-class Bitwarden secrets** (`secret_resolve`): `bw://`/`bws://`/`sops://` pointer resolution — never returns a value, distinct lock states, env-only tokens.
- ✅ **`doctor`**: PASS/WARN/FAIL health checks (vault, sources, secrets, yield, provenance) — trust, verifiable.
- ✅ Security-first: vault-contained reads, path-traversal-guarded writes, frontmatter escaping, unique-temp atomic writes, liveness (`run_deadline`+`catch_unwind`), rate-limiting, resource caps, merge-overwrite, checksummed releases — see [`SECURITY.md`](SECURITY.md).
- ✅ Scale: parallel index build (`std::thread::scope`) + incremental update on write — still zero-dep.
- ✅ Provenance: opt-in git auto-commit (`SEALMIND_GIT_AUTOCOMMIT`, deadline-bound); `df-memory` session capture + Claude Code / Codex hooks (in [DEV-FLOW](https://github.com/FiezDev/DEV-FLOW)).
- ▢ **Plugin protocol** for external extractors (PDF / office / OCR / audio) — designed, built-ins always win; future remote-RAG bridge (embeddings + RRF) as an opt-in, not a default dep.
- ▢ `edit_note` (append/replace-section) for in-place refinement of a note.
- ▢ Git-native temporal recall: `history` / `asof` — query the vault as it was at a past commit.
- ▢ Secret-detection on write (S4) — refuse content that looks like credentials.
- ▢ WASM build (BM25 subset) for an Obsidian plugin / browser-published vault.
## Inspiration
sealmind stands on ideas from, and is grateful to:
[MemPalace](https://github.com/MemPalace/mempalace) (find-first memory),
[Obsidian](https://obsidian.md) (linked-markdown knowledge + graph),
[OKF](https://github.com/GoogleCloudPlatform/knowledge-catalog/tree/main/okf)
(plain-markdown knowledge format),
[basic-memory](https://github.com/basicmachines-co/basic-memory) and
[qmd](https://github.com/tobi/qmd) (local hybrid retrieval over markdown).
sealmind's contribution is doing it in one dependency-free Rust binary.
## License
MIT — see [LICENSE](LICENSE).
Connection Info
You Might Also Like
ai-native-pm-os
The exhaustive guide to mastering Claude for Product Managers. Build your...
ai-orchestrator
Portable multi-agent AI developer setup for Claude Code + Ollama. Role-based...
vnstock-agent
MCP server and CLI for Vietnamese stock market data (vnstock)
android-source-explorer-mcp
MCP server for exploring AOSP internals and Jetpack libraries
planq
The task graph primitive for AI agents
plandb
The task graph primitive for AI agents