A local-first developer journal with semantic search and AI synthesis.
Plain-markdown notes in git. Frictionless capture, local RAG retrieval, on-demand synthesis — local Ollama, OpenAI-compatible, or cloud Claude.
journal turns a folder of plain-markdown notes into a searchable, AI-queryable
corpus — no server, daemon, or cloud store. Capture is frictionless and
append-only; retrieval is a fully local RAG stack (Ollama embeddings + optional
reranking, vectors in sqlite-vec); synthesis is on-demand — a fully local
Ollama model (zero egress, no API key), any OpenAI-compatible endpoint
(OpenRouter, Groq, …), or cloud Claude (the code default, requires
ANTHROPIC_API_KEY) — your choice.
Markdown in git is the single source of truth — the vector index is a disposable, rebuildable cache and is never committed. It all ships as one static binary.
- 📝 Frictionless capture — append a timestamped note inline, in your editor, or from stdin; auto-committed.
- ✅ Todos that close the loop —
@todoin any note becomes a tracked item:journal todoslists them,journal donechecks them off (also via MCP, so Claude can too). - 🔎 Local semantic search — Ollama +
sqlite-vec, optional LLM reranking; with synthesis configured (cloud or local), a grounded AI answer on top. Notes never leave your machine for retrieval. - 📺 A daily home —
journal today(day at a glance),journal tui(interactive dashboard: notes, todos, search, meetings, stats),journal stats(streaks & volume). - 🤖 AI synthesis — daily/weekly rollups and decision digests in your own voice. Pick your
synth_provider: a fully local Ollama model (zero egress, no API key), any OpenAI-compatible endpoint (OpenRouter, Groq, …), or cloud Claude (the code default). See AI Synthesis · Going Fully Local. - 🎙️ Meeting transcripts — pull Quill meetings into the same local index (
journal quill-sync); or ingest any recording viajournal transcribe(WhisperX → summarized, indexed transcript — see Meetings & Transcripts). Search, list, and digest them all. (v2.0; Quill is macOS/Windows.) - 🗣️ Voice notes —
journal log --text "..."runs shape→assemble→land→index: the LLM cleans the text, extracts markers (@todo/@decision/@question), and writes a structured note tologs/;--source voicescopes search to voice chunks. - 💾 Backup & sync — opt-in
journal synckeeps a git remote in step, off-machine. - 🔌 Integrations — an MCP server (
journal mcp) exposes 15 tools (search,capture,todos,synth,journal_log_text,journal_log_audio, and more), read-only resources (journal://today,journal://recent, …), and pre-built prompts to Claude Desktop and Claude Code over MCP.
- git — notes live in a git repo; capture/index auto-commit.
- Ollama + an embedding model — for indexing and search
(all local). Default:
ollama pull qwen3-embedding:4b. Verify withjournal doctor. Optional reranker:ollama pull qwen3:4b+reranker: qwen3:4bin config — precision boost for search, off by default. - A synthesis provider (only for
journal synth+ grounded search answers) — cloud Claude (default; setANTHROPIC_API_KEY), an OpenAI-compatible endpoint (synth_provider: openai+OPENAI_API_KEY— OpenRouter, Groq, …), or a fully local Ollama model (synth_provider: ollama— no key, nothing leaves your machine). Keys are read from the env, never stored in config. Skip all of them if you don't synthesize. - Quill (optional, macOS/Windows) — only for
journal quill-syncto pull meeting transcripts. Everything else works without it.
Capture, the watcher, and backup work without Ollama; only the embedding-backed features (index, search, synth) need it.
Homebrew (macOS) — the cask clears the quarantine flag, so it runs without an "Allow Anyway" prompt:
brew install ericmann/tap/journalInstall script (Linux/macOS) — downloads the latest release, verifies its
SHA-256, installs on your PATH:
curl -fsSL https://raw.githubusercontent.com/ericmann/journal/main/install.sh | shMore options — Linux packages, signed archives, build from source
Linux packages — each release attaches .deb/.rpm/.apk (amd64/arm64),
with shell completions bundled in:
sudo dpkg -i journal_*_linux_amd64.deb # Debian/Ubuntu
sudo rpm -i journal_*_linux_amd64.rpm # Fedora/RHEL
sudo apk add --allow-untrusted journal_*_linux_amd64.apkDirect archive (any platform) — grab the tar.gz from the
releases page and verify it
against checksums.txt, which is cosign-signed
(keyless):
cosign verify-blob --signature checksums.txt.sig --certificate checksums.txt.pem \
--certificate-identity-regexp 'https://github.com/ericmann/journal' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com checksums.txtFrom source (Go 1.26+):
git clone https://github.com/ericmann/journal.git && cd journal
make install # build + put a version-stamped static binary on PATH (PREFIX=/usr/local)The install script takes JOURNAL_VERSION= to pin and PREFIX= to relocate.
Release artifacts are produced by GoReleaser on each
tag; build them locally with make snapshot.
Shell completions: package installs wire them up automatically; otherwise
journal completion bash|zsh|fish — see Capturing Notes.
journal init # scaffold a repo in the cwd
ollama pull qwen3-embedding:4b # one-time: the embedding model
journal capture "fallback isn't triggering on OOM #cabot #litellm"
journal capture "declare the dev-fund payment as income" --project canton --marker decision
journal index # embed new notes
journal search "how did we handle the OOM fallback" # askjournal init scaffolds:
.journal/config.yaml # committed: models, excludes (no secrets)
.journal/index/ # gitignored: the disposable sqlite-vec database
daily/ # the firehose: daily/YYYY/MM/YYYY-MM-DD.md
projects/ # long-lived threads: projects/<slug>/
reflections/ # synthesis output (drafts)
docs/ # your voice profile + the cron/launchd guide
Never commit .journal/index/ (it's a rebuildable binary blob; init gitignores
it). Run journal doctor anytime to check Ollama, models, and the index.
| Command | What it does |
|---|---|
journal init [path] |
Scaffold (or upgrade) a journal repo |
journal capture [text] |
Append a timestamped note (inline / editor / stdin) |
journal index [--watch] |
Embed changed notes; --watch runs continuously |
journal log --text "..." |
Capture a voice note: shape (LLM) → assemble → land to logs/ → index as source=voice |
journal search <query> |
Semantic search with citations (+ a grounded AI answer when synthesis is configured); --source notes|transcript|voice|all |
journal recent · decisions · threads · meetings |
Metadata views (newest-first, @decision, project activity, transcripts) |
journal todos · journal done <ref> |
List open @todo items; check one off (rewrites it to @done <date>) |
journal today · show · edit |
Day at a glance; render any note; open a daily file in your editor |
journal tui |
Interactive dashboard: today, todos, semantic search, recent, meetings, stats |
journal stats |
Capture volume, streaks, marker counts, top tags |
journal tags |
List #tags with usage counts; tags rename <old> <new> renames across all notes |
journal quill-sync |
Pull Quill meeting transcripts into transcripts/ (Meetings & Transcripts) |
journal transcribe <whisperx.json> |
Ingest a non-Quill recording: render + summarize + index (Meetings & Transcripts) |
journal synth weekly|daily|meetings|decisions|stale |
AI synthesis — cloud Claude, OpenAI-compatible (OpenRouter/…), or local Ollama (synth_provider) |
journal sync |
Back up to / pull from a git remote (opt-in) |
journal doctor |
Health-check Ollama, models, the index |
journal mcp |
MCP server for Claude Desktop / Claude Code |
Every command takes a global --journal-dir (or JOURNAL_DIR env) to operate
on a journal from any directory — handy for an alias like
alias jc='journal capture --journal-dir ~/Projects/devnotes'.
Full flags, the note format, and search internals are in the user documentation.
📖 Full user documentation → journal.eamann.com
Getting Started
| Guide | Contents |
|---|---|
| What is journal? | Local-first design, synthesis options, the one-binary model |
| Installation | Homebrew, install script, Linux packages, build from source |
| Your First Day | Init, capture, index, search — the 5-minute intro |
Using Journal
| Guide | Contents |
|---|---|
| Capturing Notes | Capture conventions, the watcher, auto-commit, note format |
| Tracking Todos | @todo / @done, listing, checking off |
| Searching Your Journal | Semantic search, filters, grounded AI answers |
| AI Synthesis | journal synth, choosing a provider, writing in your voice |
| Meetings & Transcripts | Quill sync, journal transcribe, searching meetings |
Configuration
| Guide | Contents |
|---|---|
| Configuration Reference | Every config.yaml key, defaults, and secrets |
| Going Fully Local | Zero-egress stack: local_only, Ollama synthesis, local MCP clients |
| Backup & Sync | journal sync: enabling, conflict modes, cron/launchd/systemd |
Integrations
| Guide | Contents |
|---|---|
| Claude Code | MCP server setup for Claude Code |
| Claude Desktop | MCP server setup for Claude Desktop |
| Local MCP Clients | Jan, LM Studio, and other local chat clients |
Contributor / internal reference (repo-relative, not end-user docs):
docs/DECISIONS.md — ADRs and design rationale ·
docs/LEARNINGS.md — agent failure modes and lessons learned
make build # go build -o journal .
make test # unit + integration tests (no network; fakes for Ollama/Anthropic)
make lint # gofmt check + go vet (+ golangci-lint if installed)
make snapshot # build the full release artifact set locally (no publish)CI runs build, gofmt, go vet, tests (serial, -p 1 — see docs/DECISIONS.md
for why), race-enabled tests on internal/embed only (see .github/workflows/ci.yml),
and golangci-lint on every push and PR. Tests never touch the network — the Ollama and
Anthropic clients sit behind interfaces with deterministic fakes, and integration tests use
a temp-file sqlite-vec database.
Changes are tracked in CHANGELOG.md.
MIT © Displace Technologies, LLC (displace.tech). See
LICENSE. The original technical design and build prompt live in
docs/_internal/.