The personal OS your agent runs on. Give your agent a memory it can't hallucinate: plain markdown + SQLite full-text search + a typed contract it already knows how to operate. No LLM inside. Your agent brings the intelligence; Granite holds the ground truth.
npm install -g granite-mem
Jump to: The 60-second setup · Install it yourself · How it works · Why not Obsidian, Notion, or a vector store? · See it · Types are contracts · The MCP server · What Granite will never do · Beyond one machine · The full CLI · FAQ · Philosophy
Paste this into Claude Code, Cursor, or any MCP-capable agent:
Install Granite as my personal OS.
1. `npm install -g granite-mem`
2. `granite init --template founder-os` (vault at ~/.granite)
3. `claude mcp add granite -- granite mcp --vault ~/.granite`
4. Restart yourself so the MCP server loads.
5. Call `granite_wakeup`, then propose three notes you would write
first based on what you know about me so far. Capture them as
drafts with --source agent.
Sixty seconds later: a live vault, an agent that knows how to operate it, and its first three notes on disk in ~/.granite/notes/. No system prompt. No config. No cloud.
That's the thesis of this project. No agent handy? Granite is a complete tool on its own:
npm install -g granite-mem
granite init # note / source / synthesis / output
granite new "Ideas worth stealing"
granite search "steal"
granite serve # web UI + constellation graphAdd --template founder-os to init when you want a full personal OS from the start: person, organization, meeting, and learning on top of the four defaults — eight types already wired with hooks, indexed fields, and lifecycles, in 150 lines of pure YAML.
Everything Granite writes is a plain .md file with YAML frontmatter. Close the laptop, git init the vault, open the folder in any editor — it's just files.
Granite is a deterministic substrate for knowledge: markdown files as the source of truth, a SQLite index as derived state, and one fixed loop imposed on top. The intelligence is not in Granite — it's in whatever agent (or human) operates it.
┌──────────────────────────────────────────┐
│ your agent · the brains │
└────────────────────┬─────────────────────┘
│ MCP · CLI · web UI
┌────────────────────▼─────────────────────┐
│ GRANITE │
│ │
│ capture ─▶ compile ─▶ query ─▶ output │
│ ▲ │ │
│ └──────────── lint ◀──────────┘ │
│ │
│ .md files SQLite FTS5 wikilinks │
│ (truth) (derived) (graph) │
└──────────────────────────────────────────┘
- Markdown is truth. Every note is
<folder>/<slug>.mdwith YAML frontmatter. Nothing you can't read incat. - The index is disposable. Full-text search, backlinks, and typed queries live in
.granite/index.db— rebuilt from the files at any time. - Wikilinks are the graph.
[[a-note]]in any body resolves slug → title → alias, and the backlink graph falls out for free.
Karpathy asked for "an incredible new product instead of a hacky collection of scripts" for LLM knowledge bases.
This is our answer.
Granite doesn't compete with your note app. It competes with the pile of scripts you were about to write.
| Granite | Obsidian | Notion | Vector memory | Plain files | |
|---|---|---|---|---|---|
| Plain markdown on disk | ✅ | ✅ | ❌ | ❌ | ✅ |
| Typed schemas with hooks & lifecycles | ✅ | plugins | databases | ❌ | ❌ |
| Agent-native MCP workflow | ✅ | community | limited | ✅ | ❌ |
| Deterministic retrieval (no embeddings) | ✅ | ✅ | ❌ | ❌ | grep |
| Structured queries over indexed fields | ✅ | plugins | ✅ | ❌ | ❌ |
| Provenance on every note | ✅ | ❌ | ❌ | partial | ❌ |
| Offline, no account, no telemetry | ✅ | ✅ | ❌ | ❌ | ✅ |
| Git-friendly | ✅ | ✅ | ❌ | ❌ | ✅ |
Obsidian is a great editor for humans. Vector memory is a great cache for agents. Granite is the shared substrate both can operate.
This is what makes an agent feel native rather than bolted-on. Every note type in granite.yml is an executable contract:
note_types:
meeting:
folder: notes/meetings
fields:
date: { type: date, required: true }
organization: { type: wikilink, target_types: [organization] }
attendees: { type: wikilink, target_types: [person] }
on_create:
- { action: set_default, field: date, value: "${today}" }
- { action: resolve_wikilinks, fields: [organization, attendees], auto_stub: true }
indexed_fields: [date, organization]set_default— fills${today}automaticallyresolve_wikilinks + auto_stub— turnsorganization: Acme Corpinto the slugacme-corp, creating the org note if missing (with a globally-unique slug so nothing gets silently overwritten)indexed_fields— makesgranite_query { type: meeting, where: { date: { gte: "2026-01-01" } } }fast and deterministiclifecycle— declare states and stale-days transitions, andgranite doctorsurfaces drift before it rots
On top of its type, every note carries five protocol fields, so humans and agents share ground truth:
| Field | Values | Purpose |
|---|---|---|
status |
inbox · active · archived |
operational state |
source |
human · agent · extraction |
who wrote it |
review_state |
draft · reviewed · locked |
editorial state |
durability |
canonical · working · ephemeral |
keep / may drift / throwaway |
derived_from |
[slug, …] |
provenance for syntheses and outputs |
Your agent reads these before writing and sets them as it works. You inherit a fully auditable trail. Add a type when your life grows a new shape — the core stays small. For the formal protocol, see docs/GRANITE_OBJECT_STANDARD.md.
"A thin MCP server exposes capabilities. A strong MCP server shapes behavior."
One line connects any MCP-capable agent to your vault:
claude mcp add granite -- granite mcp --vault ~/.graniteThe surface is intention-first — tools organized around the workflow, not around files:
| Intent | Tools |
|---|---|
| Orient | granite_wakeup · granite_vault_types · granite_research_topic · granite_resolve |
| Read | granite_read_resource · granite_query · granite_compile_context · granite_understand_note · granite_extract_document |
| Write | granite_capture_knowledge · granite_import_document · granite_revise_note · granite_dispose_note |
| Garden | granite_plan_garden · granite_adjudicate_garden_opportunity · granite_list_garden_adjudications |
For clients that only expose tools, call granite_vault_types for the full vault type contract (including body sections, required fields, and indexed_fields), or granite_read_resource({ uri }) for any resource: granite://vault/types, granite://notes/{slug}, or granite://assets/{filename}. Substitute and percent-encode template parameters. Both tools return embedded MCP resources with the same URI, MIME type, and text or base64 blob as the resource interface; the existing resources remain available.
Plus three prompts for the higher-level workflows (granite_refine_note, granite_process_inbox, granite_compile_topic), and resources for raw note and type-contract access. Start the server with --role read when an agent should inspect without mutating. An HTTP transport with bearer-token auth is available for remote setups — see docs/DEPLOY.md.
The point is not to give an agent a file browser. The point is to give it a workflow it can follow.
Granite will never:
- run a generated word, plan a step, or loop on its own
- compute embeddings or ship a vector store
- run background agents or a scheduler
- phone home — no telemetry, no account, no cloud dependency of its own
- add overlapping CLI/MCP endpoints that blur the loop
This is why your agent can be trusted with write access. The vault is a deterministic substrate. The intelligence is yours (or Claude's, or GPT's, or whoever you pay this quarter).
One exception, and it is not a language model. Granite's MCP layer calls Jev, TypeSafe's System One classifier, so it can bound a candidate set deterministically and then delegate the judgment of which candidate answers a question — instead of leaving that assembly work to your agent. Jev returns a choice, a score, or a probability over a set Granite chose; it never writes prose, never plans, and never decides what to look at.
Jev is required. TYPESAFE_API_KEY must be set or Granite refuses to start, because a
semantic layer that silently degrades produces answers that look right and are not. Every
capture is judged too, and the proposals it returns are yours to apply — Jev never writes a
link.
Cloud, if you want it. One command deploys a personal serverless Granite on Fly.io Sprites: wakes on request in 100–500 ms, sleeps when idle, costs cents per month at rest. You own the sprite — there is no Granite cloud, no central admin, no relay.
granite deploy login --token <sprites-token> # or export SPRITES_TOKEN=…
granite deploy # prints an MCP URL + bearer token
claude mcp add --transport http granite https://<your-sprite>.sprites.app/mcp \
--header "Authorization: Bearer <token>"Railway: deploy your own private read/write MCP vault with persistent storage and an automatically generated access token. Enter your TypeSafe key in Railway; the template handles the infrastructure. See the deployment guide.
For an optional personal read-only dashboard in the same HTTP MCP process, set
GRANITE_UI_PASSWORD in your Railway service variables to a unique strong password
of at least 16 non-padding characters, then open https://<your-domain>/ui after
your normal deployment update. Keep GRANITE_MCP_TOKEN and TYPESAFE_API_KEY as
configured; the UI password is separate and can be rotated without changing MCP
clients. Without a UI password, all /ui routes return 404.
The dashboard supports browsing, full-text search, type/tag filters, rendered notes and clickable links/backlinks. It is read-only, single-user and HTTPS-only. Sessions expire after 12 hours; sign out to revoke one immediately. Rotate the UI password and restart to invalidate all sessions. See the dashboard security and setup notes and environment placeholders.
To move an existing vault, preview with granite migrate railway --vault ~/.granite --project PROJECT_ID --service SERVICE_ID --environment ENVIRONMENT_ID. After backups and stopping all writers, add --apply --writers-stopped to upload, verify and submit activation. The migration guide covers attachment folders, rollback and live verification.
Multiple named instances, bulk upgrades, token rotation, and self-hosting the HTTP MCP server (a generic Dockerfile is included) are covered in docs/DEPLOY.md.
Sync, without a relay. Direct machine-to-machine over LAN, Tailscale, or a private DNS name — with per-device read/write tokens:
granite sync access grant ipad --role read # on the serving machine
granite sync serve --host 0.0.0.0 --port 8765
granite sync remote add macbook http://100.x.y.z:8765 --token <read-token>
granite sync watch macbook --direction pull --interval 30Conflict policies, push/pull details, and access management live in docs/SYNC.md.
Every command, one line each
| Layer | Command | What it does |
|---|---|---|
| setup | granite init |
create a vault (optionally from a --template) |
| setup | granite status |
vault health and what to do next |
| capture | granite new <title> |
create a typed note |
| capture | granite add [text] |
quick raw capture (arg or stdin) into the inbox |
| capture | granite attach <file> |
attach an image/video/PDF and get markdown to embed |
| capture | granite extract <file> |
raw text from PDF/DOCX/XLSX/PPTX without importing |
| capture | granite import <file> --content <text> |
attach a document and create a linked source note |
| query | granite list |
browse notes by type, status, source, date |
| query | granite show <slug> |
read a full note |
| query | granite search <query> |
full-text search across the vault |
| query | granite open <slug> |
open a note in $EDITOR |
| query | granite wakeup |
compact vault snapshot for loading agent context |
| compile | granite edit <slug> |
update fields, body, tags, protocol state |
| compile | granite backlinks <slug> |
inbound links to a note |
| compile | granite suggest-links <slug> |
unlinked mentions worth linking |
| compile | granite recommend <slug> |
what to link, tag, or write next |
| lint | granite doctor |
broken links, missing fields, stale notes, line violations |
| lint | granite types |
show note types and the flow between them |
| serve | granite serve |
local web UI with the constellation graph (port 4321) |
| serve | granite mcp |
MCP server (stdio or HTTP; --role read|write) |
| serve | granite daemon start |
MCP + web UI as one background process |
| cloud | granite deploy … |
serverless instances on Fly.io Sprites (docs) |
| sync | granite sync … |
direct multi-device sync (docs) |
Run granite --help for every flag.
Is this an Obsidian replacement?
No. Your vault is plain markdown with [[wikilinks]] — Obsidian opens it just fine. Granite adds the typed contracts, the deterministic index, and the MCP surface on top of files any editor can read.
Where's the AI?
In your agent. Granite is deliberately deterministic — that's precisely why an agent can be trusted with write access to it.
No embeddings — how does search work?
SQLite FTS5 for full text, typed queries over indexed fields, and the wikilink graph for structure. Deterministic, explainable, and rebuildable from the files.
Can I use it without an agent?
Yes. The full CLI and the web UI work standalone. The MCP server is one door among three.
What happens to my data if Granite disappears?
Nothing. It's a folder of markdown files. The index is derived and disposable; git init the vault and you have versioning and backup for free.
Does anything phone home?
No. No telemetry, no account, no network calls — unless you explicitly deploy to your own sprite, sync to your own machines, or run granite serve with cloud credentials configured (use --no-cloud to stay fully offline). Granite makes one outbound call on your behalf: the Jev judgment, from the MCP layer, which requires a TYPESAFE_API_KEY you set yourself. Without it Granite refuses to start rather than degrade.
- local-first beats cloud dependence for personal memory
- plain markdown beats proprietary formats
- types as active contracts beat types as folders
- tools for humans should also be legible to agents
- protocol belongs in the core; agent policy belongs outside it
- a personal OS is a thing you own — not a thing you rent
Granite is pre-1.0 and moving fast — see CHANGELOG.md for release history. The product boundary stays fixed: Granite stores and indexes local knowledge; agents bring the intelligence.
Issues and focused PRs are welcome. For local development, read CLAUDE.md. The key product rule is simple: no embedded LLM, no vector store, no autonomous scheduler inside Granite — the single permitted model is Jev, a classifier Granite requires for its semantic layer.
Ship your agent a home. Then give it the keys.



