<div align="center">
# oximemo
**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.
[](https://github.com/project-oxi/oximemo/actions/workflows/ci.yml)
[](https://github.com/project-oxi/oximemo/releases)
[](LICENSE)
[](https://www.rust-lang.org)
[](https://v2.tauri.app)
[](#system-requirements)
</div>
---
> **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:
1. **A human** double-taps `Option` anywhere on macOS, types one line, and disappears back into their work.
2. **An agent** (coding agent, local script) reads and writes those same notes over the `oximemo` CLI — safely, with no duplicates.
## Highlights
- **Files are the source of truth.** Notes are plain `.md` files with TOML frontmatter. `grep` and `cat` work. The index is just a cache — rebuildable at any time with `oximemo reindex`.
- **Three-tier storage, pure Rust.** Plain files + a `redb` metadata index + a `tantivy` BM25 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 export` emits a body-less manifest of `{id, hash, updated_at, deleted}`; diff the hashes, fetch only what changed, advance your cursor. Handles `ARG_MAX` with `--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.
## Table of contents
- [System requirements](#system-requirements)
- [Install](#install)
- [Quick start (CLI)](#quick-start-cli)
- [The vault](#the-vault)
- [Architecture](#architecture)
- [Project structure](#project-structure)
- [Synchronization for agents](#synchronization-for-agents)
- [Development](#development)
- [Roadmap](#roadmap)
- [Contributing](#contributing)
- [License](#license)
## System requirements
- **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](doc/DESIGN.md).
## Install
### From a release
Download the prebuilt `oximemo` binary and `.dmg` from the
[latest release](https://github.com/project-oxi/oximemo/releases), then:
```bash
tar -xzf oximemo-aarch64-apple-darwin.tar.gz
sudo install -m 0755 oximemo /usr/local/bin/oximemo
oximemo --version
```
### From source
```bash
git clone https://github.com/project-oxi/oximemo.git
cd oximemo
cargo build --release -p oximemo-cli
# binary: target/release/oximemo
```
> A Homebrew tap (`brew install`) is planned. For now, use a release tarball or build from source.
## Quick start (CLI)
The CLI is the authoritative interface — the same `oximemo-core` the desktop app uses.
```bash
# 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 path
```
<details>
<summary><strong>Full command reference</strong></summary>
```bash
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 delete <ID> # inbox cannot be deleted
oximemo reindex # rebuild indexes from files
oximemo doctor [--fix] # audit / safe-repair
oximemo vault path # print the vault root
oximemo upgrade [--check] # self-update from GitHub Releases
```
Global: `--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.
</details>
<details>
<summary><strong>Global capture & desktop app</strong></summary>
- **Capture overlay:** double-tap `Option` (needs Accessibility / Input Monitoring permission), or the always-available `Cmd+Shift+N`, or the menu-bar icon. `Enter` saves & dismisses, `Shift+Enter` newline, `Esc` cancels.
- **Card grid:** search, tag/favorite filters, OKLCH color labels, virtualized for large vaults.
- Light/dark follows the macOS system appearance.
</details>
## The vault
Notes are plain text — humans and agents can read them with anything.
```plain
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 `+++`:
```markdown
+++
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`](doc/DESIGN.md) §5 and [`skills/oximemo/SKILL.md`](skills/oximemo/SKILL.md).
## Architecture
`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`](crates/oximemo-core/src/vault.rs) — so the GUI and CLI always behave identically and can share one live vault (guarded by an `fs2` advisory lock).
```mermaid
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
```
| 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.
## Project structure
```
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
```
## Synchronization for agents
The manifest is cheap on purpose — bodies are omitted, so it stays light for tens of thousands of notes.
1. **Fetch the manifest since your cursor:**
```bash
oximemo export --since "$CURSOR" --format ndjson > manifest.ndjson
```
2. **Diff against your local `id → hash` cache** (in your code):
- `id` unseen → **fetch**
- `hash` differs → **fetch** (covers tag/favorite/color edits too)
- `deleted: true` → **drop**
3. **Fetch changed bodies in bulk** (use `--ids-file`/`--ids-stdin` past `ARG_MAX`):
```bash
oximemo export --ids-file ids.txt --full --format ndjson
```
4. **Advance your cursor** to the max `updated_at` seen. Repeat.
The full procedure, output schemas, and the safe direct-write rules are in [`skills/oximemo/SKILL.md`](skills/oximemo/SKILL.md).
## Development
```bash
# 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 build
```
A scratch vault is handy for manual testing:
```bash
cargo run -p oximemo-cli -- --vault /tmp/oximemo-test new "hello" --tag dev
cargo run -p oximemo-cli -- --vault /tmp/oximemo-test list
```
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the full workflow, and [`doc/DESIGN.md`](doc/DESIGN.md) for the design authority.
## Roadmap
- **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.
## Contributing
Contributions are welcome! Please read [`CONTRIBUTING.md`](CONTRIBUTING.md) first.
By contributing, you agree your contributions will be licensed under the
[MIT License](LICENSE).
## License
Licensed under the [MIT License](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.