Capture a thought before it's gone.
A fast, minimal, card-based memo app for macOS (Apple Silicon).
Where a human hits Option twice and a coding agent reads the same vault over a CLI — with parity, no cloud, and plain-text files as the source of truth.
oximemo is optimized for the speed of catching a thought. Every memo is a card; cards live on a grid. There is no AI summary, no auto-tagging, no chatbot — those trade away the capture speed and reliability this project exists to protect.
Two core scenarios, one vault:
- A human double-taps
Optionanywhere on macOS, types one line, and disappears back into their work. - An agent (coding agent, local script) reads and writes those same notes over the
oximemoCLI — safely, with no duplicates.
- Files are the source of truth. Notes are plain
.mdfiles with TOML frontmatter.grepandcatwork. The index is just a cache — rebuildable at any time withoximemo reindex. - Three-tier storage, pure Rust. Plain files + a
redbmetadata index + atantivyBM25 full-text index. No SQLite, no C dependencies in the index layer. - Capture that doesn't make you wait. The overlay window is warmed up off-screen so it appears in a single frame (target ≤ 16 ms) on trigger.
- Human/agent parity. Every GUI operation is a CLI operation. Agent-facing commands default to JSON / NDJSON for clean streaming and scripting.
- Hash-based sync.
oximemo exportemits a body-less manifest of{id, hash, updated_at, deleted}; diff the hashes, fetch only what changed, advance your cursor. HandlesARG_MAXwith--ids-file/--ids-stdin. - OKLCH colors. Perceptually uniform, CSS-native color labels that look right in both light and dark mode.
- Hardened against external writes. The file watcher debounces, retries partial writes (editors, iCloud), and never crashes the indexer.
- System requirements
- Install
- Quick start (CLI)
- The vault
- Architecture
- Project structure
- Synchronization for agents
- Development
- Roadmap
- Contributing
- License
- macOS 14+ on Apple Silicon (
aarch64-apple-darwin). - Rust 1.89+ (edition 2024) to build from source.
Windows, Linux, and mobile are intentionally out of scope for the MVP. See the design doc.
Download the prebuilt oximemo binary and .dmg from the
latest release, then:
tar -xzf oximemo-aarch64-apple-darwin.tar.gz
sudo install -m 0755 oximemo /usr/local/bin/oximemo
oximemo --versiongit clone https://github.com/project-oxi/oximemo.git
cd oximemo
cargo build --release -p oximemo-cli
# binary: target/release/oximemoA Homebrew tap (
brew install) is planned. For now, use a release tarball or build from source.
The CLI is the authoritative interface — the same oximemo-core the desktop app uses.
# Capture a thought (text arg, or omit to read stdin)
oximemo new "Ship the redb bump before the freeze" --tag backlog --category todo
# List recent notes — table for humans (default), JSON/NDJSON for agents
oximemo list --limit 10
oximemo list --favorites --format ndjson
# Read one memo (JSON by default; --md for the raw file)
oximemo get 019fa927-a897-7e12-9102-8a8c7ebbb594 --md
# Full-text search (BM25 over body + tags)
oximemo search "redb upgrade" --limit 5 --format ndjson
# Edit a memo (favorite / category / body) and manage categories
oximemo update 019fa927-a897-7e12-9102-8a8c7ebbb594 --favorite --category idea
oximemo category list
oximemo category new research --color "oklch(0.72 0.15 310)"
# Where does my vault live?
oximemo vault pathFull command reference
oximemo new [TEXT] [--tag TAG]… [--category ID] # arg or stdin; empty rejected
oximemo list [--limit N] [--tag T] [--category ID] [--favorites] [--format table|json|ndjson]
oximemo get <ID> [--md]
oximemo update <ID> [--body T | --body-stdin] [--favorite] [--unfavorite] [--category ID]
oximemo search <QUERY> [--limit N] [--format json|ndjson]
oximemo stats # live memo counts (JSON)
oximemo export [--since RFC3339] [--ids a,b,c | --ids-file PATH | --ids-stdin]
[--full] [--format ndjson|json]
oximemo delete <ID> # soft-delete → .trash/
oximemo restore <ID> # un-delete a trashed memo
oximemo purge [--older-than 30d]
oximemo category list [--format table|json|ndjson]
oximemo category new <ID> [--color "oklch(...)"]
oximemo category recolor <ID> <COLOR> | --none # set or clear a category's color
oximemo category rename <OLD> <NEW> # moves memos; prints count
oximemo category delete <ID> # inbox cannot be deleted
oximemo reindex # rebuild indexes from files
oximemo doctor [--fix] # audit / safe-repair
oximemo vault path # print the vault rootGlobal: --vault <PATH> (or OXIMEMO_VAULT) selects a non-default vault. Output formats: table (human), json (single array), ndjson (one value per line, the default for export/search). Timestamps are RFC 3339.
Global capture & desktop app
- Capture overlay: double-tap
Option(needs Accessibility / Input Monitoring permission), or the always-availableCmd+Shift+N, or the menu-bar icon.Entersaves & dismisses,Shift+Enternewline,Esccancels. - Card grid: search, tag/favorite filters, OKLCH color labels, virtualized for large vaults.
- Light/dark follows the macOS system appearance.
Notes are plain text — humans and agents can read them with anything.
vault/
├── memos/
│ └── 2026/07/
│ ├── 01991a2e-7c3f-7c91-9f3e-6b1a2e8f9c10.md
│ └── 01991a31-9b10-70aa-8c2e-4f0a1d2b3c44.md
├── .trash/ # soft-deleted memos
└── config.toml # optional vault settings
Each memo is one file with TOML frontmatter delimited by +++:
+++
id = "01991a2e-7c3f-7c91-9f3e-6b1a2e8f9c10"
created_at = "2026-07-28T10:15:03+09:00"
updated_at = "2026-07-28T10:15:03+09:00"
hash = "b3:6f2a9e1d4c7b8a90f1e2d3c4b5a6978…"
favorite = false
category = "inbox"
tags = ["idea", "oximemo"]
+++
The capture overlay must appear in under one frame.The id is a time-sortable UUIDv7; the hash is b3: + BLAKE3 over the normalized body, tags, favorite flag, and category — so a pure metadata edit (add a tag, change a category) bumps the hash and is detected by sync. Full parsing rules and the safe-writing guide are in doc/DESIGN.md §5 and skills/oximemo/SKILL.md.
oximemo-core is a pure-Rust library that owns the file store, indexes, file-watching, and sync. The desktop app (Tauri) and the CLI are thin adapters over oximemo_core::Vault — so the GUI and CLI always behave identically and can share one live vault (guarded by an fs2 advisory lock).
flowchart TB
subgraph Native["macOS native"]
CAP["oximemo-capture\nobjc2 global flagsChanged monitor\n(Option double-tap)"]
MENU["Menu-bar NSStatusItem"]
end
subgraph App["Tauri desktop app (apps/desktop)"]
RUST["Tauri Rust backend"]
UI["React 19 frontend\ncard grid + overlay"]
end
subgraph CLI["oximemo-cli"]
BIN["clap subcommands\nnew / list / search / export …"]
end
subgraph Core["oximemo-core (pure Rust)"]
FILES[("Files (*.md)\nsource of truth")]
LOCK["fs2 advisory lock"]
REDB[("redb metadata\nindex")]
TANT[("tantivy\nBM25 search")]
WATCH["notify watcher"]
SYNC["hash dedup / export"]
end
AGENT["External agent\n(coding agent / script)"]
CAP --> RUST
MENU --> RUST
RUST <--> UI
RUST --> Core
BIN --> Core
FILES --> WATCH --> REDB
WATCH --> TANT
LOCK -. guards .-> REDB
REDB --> SYNC
AGENT -- "CLI call" --> BIN
| Layer | Role | Tech |
|---|---|---|
| Source of truth | Human-readable memo bodies | .md files + TOML frontmatter |
| Metadata index | Fast pagination, filters, sync cursor | redb |
| Full-text index | BM25 keyword search | tantivy |
The index layers are 100% derivable from the files — corrupt or stale? One oximemo reindex restores them.
oximemo/
├── crates/
│ ├── oximemo-core/ # Pure-Rust core: store, index, search, watcher, sync
│ ├── oximemo-cli/ # `oximemo` binary — clap adapter over oximemo-core
│ └── oximemo-capture/ # macOS global Option double-tap monitor (objc2)
├── apps/desktop/ # Tauri 2 + React 19 desktop app
│ ├── src-tauri/ # Rust backend
│ └── src/ # React frontend (Tailwind v4, Base UI, TanStack)
├── skills/oximemo/ # SKILL.md — agent-facing CLI guide
└── doc/DESIGN.md # Full design document
The manifest is cheap on purpose — bodies are omitted, so it stays light for tens of thousands of notes.
- Fetch the manifest since your cursor:
oximemo export --since "$CURSOR" --format ndjson > manifest.ndjson
- Diff against your local
id → hashcache (in your code):idunseen → fetchhashdiffers → fetch (covers tag/favorite/color edits too)deleted: true→ drop
- Fetch changed bodies in bulk (use
--ids-file/--ids-stdinpastARG_MAX):oximemo export --ids-file ids.txt --full --format ndjson - Advance your cursor to the max
updated_atseen. Repeat.
The full procedure, output schemas, and the safe direct-write rules are in skills/oximemo/SKILL.md.
# Rust
cargo fmt
cargo clippy -p oximemo-core -p oximemo-cli -p oximemo-capture --all-targets -- -D warnings
cargo test -p oximemo-core -p oximemo-cli -p oximemo-capture
# Desktop frontend
cd apps/desktop
bun install
bun run buildA scratch vault is handy for manual testing:
cargo run -p oximemo-cli -- --vault /tmp/oximemo-test new "hello" --tag dev
cargo run -p oximemo-cli -- --vault /tmp/oximemo-test listSee CONTRIBUTING.md for the full workflow, and doc/DESIGN.md for the design authority.
- v0.3+ — MCP server mode (
oximemo mcp serve), multiple vaults, iCloud-Drive vault auto-detection, optional wikilinks/backlinks. - Deferred by design — AI summaries, auto-tagging, chatbot, and embedding-based semantic search. BM25 keeps the capture loop fast; an offline embedding path (Rust
candle, Metal-accelerated) stays a possibility if real demand appears.
Contributions are welcome! Please read CONTRIBUTING.md first.
By contributing, you agree your contributions will be licensed under the MIT License.
Licensed under the MIT License.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you shall be licensed under the MIT License, without any additional terms or conditions.