Skip to content

Repository files navigation

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.

CI Release License: MIT Rust Tauri macOS


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

  • 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.

Install

From a release

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 --version

From source

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.

# 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
Full 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 root

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.

Global capture & desktop app
  • 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.

The vault

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.

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 — 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
Loading
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.

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:
    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: truedrop
  3. Fetch changed bodies in bulk (use --ids-file/--ids-stdin past ARG_MAX):
    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.

Development

# 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:

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 for the full workflow, and 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 first.

By contributing, you agree your contributions will be licensed under the MIT License.

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.

About

Fast, minimal, card-based note capture for macOS (Apple Silicon). Rust + Tauri 2; plain .md files as the source of truth, CLI/agent parity.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages