mnemo records a voice note from your default microphone, transcribes it with
ElevenLabs, and stores the resulting text in Hindsight.
- A working macOS microphone input
- An ElevenLabs API key with Speech-to-Text access
- A running Hindsight API endpoint
Rust and Cargo are only required if you build from source or install the latest
development build with brew install --HEAD mnemo.
mnemo currently publishes prebuilt releases for macOS Apple Silicon only.
Supported target:
aarch64-apple-darwin
Other platforms may work from source, but are not currently tested or packaged.
Start recording a voice note:
mnemo recordPress Enter in that terminal when you are done. mnemo will stop recording,
transcribe the audio, and retain the transcript in Hindsight.
As an alternative to pressing Enter, you can stop an active recording from
another process:
mnemo stopThis is useful for external triggers like a Stream Deck button. Start recording
with mnemo record, then have the button run mnemo stop to signal that
recording process to finish.
Install with Homebrew:
brew tap Toady00/tap
brew install mnemoTo install the latest development build from the default branch instead:
brew install --HEAD mnemoOr install from a GitHub release by downloading the
mnemo-aarch64-apple-darwin.tar.gz asset, extracting it, and moving mnemo to
a directory on your PATH.
The default config file location is:
~/.config/mnemo/config.tomlCreate a starter config:
mnemo initOr create it from the example:
mkdir -p ~/.config/mnemo
cp config.example.toml ~/.config/mnemo/config.tomlAll configuration lives under a named profile. The default profile is
default, and it is used when no profile is specified.
Supported config keys:
[profiles.default]
hindsight_url = "https://your-hindsight-api.example.com"
bank = "personal"
language = "eng"
model = "scribe_v2"
context = "user recorded voice memo"
# Optional Hindsight retain fields. Leave unset to send null.
# metadata = { source = "mnemo" }
# tags = ["voice-note"]
# observation_scopes = [["scope:platform"]]
# strategy = "append"
# Defaults to ~/.local/state/mnemo/mnemo.sock
# socket_path = "/Users/you/.local/state/mnemo/mnemo.sock"
# Prefer macOS Keychain or environment variables for secrets, but config keys are supported.
# elevenlabs_api_key = "..."
# hindsight_api_key = "..."Config precedence is:
built-in defaults < config.toml < environment variables < CLI flags
hindsight_url is required for mnemo record. Set it in the config file,
MNEMO_HINDSIGHT_API_URL, or --hindsight-url.
bank and an ElevenLabs API key are also required for mnemo record. Set
bank in the selected profile, MNEMO_BANK_ID, or --bank.
API key precedence for ElevenLabs and Hindsight keys is:
CLI flag > MNEMO_* environment variable > macOS Keychain > config.toml
In practice, prefer macOS Keychain for normal use and
MNEMO_ELEVENLABS_API_KEY or MNEMO_HINDSIGHT_API_KEY for one-time setup or
development overrides.
observation_scopes is a list of scopes, where each scope is a list of tags.
Hindsight receives it as an array of arrays. It is sent as null unless you
configure it, and it is never derived from tags.
In the config file, write the nested array directly:
observation_scopes = [["scope:platform"], ["scope:business", "team:core"]]As an environment variable, use JSON:
export MNEMO_OBSERVATION_SCOPES='[["scope:platform"],["scope:business","team:core"]]'On the command line, repeat --observation-scope once per scope and
comma-separate the tags within a scope:
mnemo record \
--observation-scope scope:platform \
--observation-scope scope:business,team:coreAll three forms produce the same payload field:
"observation_scopes": [["scope:platform"], ["scope:business", "team:core"]]mnemo can store API keys in macOS Keychain so GUI wrappers and
plugin-launched processes do not need shell environment variables.
First-time setup:
export MNEMO_ELEVENLABS_API_KEY="your-elevenlabs-key"
export MNEMO_HINDSIGHT_API_KEY="your-hindsight-key"
mnemo keychain sync
unset MNEMO_ELEVENLABS_API_KEY
unset MNEMO_HINDSIGHT_API_KEYFor a named profile:
export MNEMO_ELEVENLABS_API_KEY="your-business-elevenlabs-key"
export MNEMO_HINDSIGHT_API_KEY="your-business-hindsight-key"
mnemo keychain sync --profile business
unset MNEMO_ELEVENLABS_API_KEY
unset MNEMO_HINDSIGHT_API_KEYList API keys stored in Keychain:
mnemo keychain listRemove selected Keychain entries interactively:
mnemo keychain removeRemove all mnemo API keys with confirmation:
mnemo keychain remove --allRemove all mnemo API keys without confirmation:
mnemo keychain remove --all --forceKeychain entries use service mnemo-secrets and account names scoped by
profile and key name:
profile:default:elevenlabs-api-key
profile:default:hindsight-api-key
profile:business:elevenlabs-api-key
profile:business:hindsight-api-key
Keychain operations shell out to /usr/bin/security so access is tied to
Apple's stable system tool rather than mnemo's binary signature.
Profiles let you keep separate Hindsight destinations and retain settings in
one config file. Each profile lives under [profiles.<name>].
Example:
[profiles.default]
hindsight_url = "https://your-hindsight-api.example.com"
bank = "personal"
context = "user recorded voice memo"
[profiles.business]
hindsight_url = "https://your-hindsight-api.example.com"
bank = "business"
context = "user recorded business voice memo"
tags = ["business", "voice-note"]
strategy = "append"
[profiles.family]
hindsight_url = "https://your-hindsight-api.example.com"
bank = "family"
context = "user recorded family voice memo"
metadata = { source = "mnemo", category = "family" }Use the default profile:
mnemo recordUse a named profile:
mnemo record --profile businessOr select a profile with an environment variable:
export MNEMO_PROFILE="business"
mnemo recordProfile values can still be overridden by MNEMO_* environment variables or
CLI flags. For example, this records to the business profile but overrides
the bank for one run:
mnemo record --profile business --bank temporary-business-notesSupported environment variables:
export MNEMO_ELEVENLABS_API_KEY="your-elevenlabs-key"
export MNEMO_HINDSIGHT_API_KEY="your-hindsight-key-if-needed"
export MNEMO_HINDSIGHT_API_URL="https://hindsight-api.example.com"
export MNEMO_PROFILE="default"
export MNEMO_BANK_ID="personal"
export MNEMO_ELEVENLABS_LANGUAGE="eng"
export MNEMO_ELEVENLABS_MODEL="scribe_v2"
export MNEMO_SOCKET_PATH="$HOME/.local/state/mnemo/mnemo.sock"
export MNEMO_CONTEXT="user recorded voice memo"
export MNEMO_TAGS="voice-note,personal"
export MNEMO_STRATEGY="append"
export MNEMO_METADATA='{"source":"mnemo"}'
export MNEMO_OBSERVATION_SCOPES='[["scope:platform"]]'Check that the project builds:
cargo checkFormat the code:
cargo fmtRun the app in development mode:
cargo run -- recordmnemo defaults to record, so this is equivalent:
cargo runOverride config from the CLI:
cargo run -- --bank personal --language autoUse a named profile:
cargo run -- record --profile businessOr with the installed binary:
mnemo record --profile businessSet the Hindsight URL from the CLI:
cargo run -- --hindsight-url https://your-hindsight-api.example.comUse a different config file:
cargo run -- --config /path/to/config.tomlWhen running record, mnemo starts recording immediately. Press Enter to
stop recording, then it will transcribe and retain the note.
You can also stop a running recording from another shell:
cargo run -- stoprecord opens a Unix socket at:
~/.local/state/mnemo/mnemo.sockOnly one mnemo record process can run at a time. If the socket belongs to a
live process, a second record command exits instead of trying to record from
the microphone at the same time. If the socket is stale, mnemo removes it and
continues.
The socket stays open for the full mnemo record lifecycle and uses
newline-delimited JSON. New clients receive the current status immediately after
connecting, and connected clients receive future status changes.
Status messages look like:
{"type":"status","state":"recording","startedAt":"2026-05-09T19:24:18.248Z"}
{"type":"status","state":"processing","startedAt":"2026-05-09T19:24:18.248Z"}
{"type":"status","state":"complete","startedAt":"2026-05-09T19:24:18.248Z"}
{"type":"status","state":"error","startedAt":"2026-05-09T19:24:18.248Z","message":"failed to call ElevenLabs STT"}Clients can request the current status or stop an active recording:
{"type":"status"}
{"type":"stop"}No socket means mnemo is idle. recording means stop is valid.
processing, complete, and error are terminal or near-terminal states where
the socket will close shortly after the final status is published.
mnemo releases are currently built locally for macOS Apple Silicon and
published through GitHub Releases.
Feature work is committed first, with its changelog entries under
## [Unreleased]. The release itself is then a separate commit that only bumps
the version and promotes those entries under a new version heading.
Bump version in Cargo.toml, then refresh the lockfile:
cargo update --workspaceIn CHANGELOG.md, add a ## [X.Y.Z] - YYYY-MM-DD heading directly below
## [Unreleased], leaving the existing entries beneath it.
Verify the project:
cargo fmt --check
cargo test
cargo clippy -- -D warningsScan for known vulnerabilities, leaked secrets, and misconfiguration. Address anything it reports before tagging:
trivy fs --scanners vuln,secret,misconfig --skip-dirs target --skip-dirs dist .target and dist are skipped because they hold build output rather than
sources. The release binary is not built with cargo-auditable, so scanning it
directly yields nothing regardless.
Addressing a finding does not always mean upgrading. Cargo.lock records the
resolved union of optional dependencies, including ones no enabled feature ever
activates, and Trivy reads the lockfile without knowing which features were
selected. Confirm a flagged crate is actually reachable before acting on it:
cargo tree -i <crate> --target all -e allIf that prints nothing to print, the crate is compiled into nothing and the
finding does not apply to the shipped binary. Upgrade anyway when a fixed
version is available, since it costs nothing and keeps later scans quiet, but
record the reachability result so the severity is not misread. If the crate is
reachable, treat the finding as a release blocker.
Build the release binary:
cargo build --releasePackage the release asset and note the checksum:
mkdir -p dist
cp target/release/mnemo dist/mnemo
tar -czf dist/mnemo-aarch64-apple-darwin.tar.gz -C dist mnemo
shasum -a 256 dist/mnemo-aarch64-apple-darwin.tar.gzCommit the version bump, then create and push a signed release tag:
git commit -m "Prepare release X.Y.Z" Cargo.toml Cargo.lock CHANGELOG.md
git push origin master
git tag -s vX.Y.Z -m "Release vX.Y.Z"
git push origin vX.Y.ZRelease notes cover only the version being released, so they are written
separately rather than taken from CHANGELOG.md as a whole. Reuse that
version's changelog entries and close with the asset checksum:
cat > /tmp/mnemo-notes.md << EOF
## Added
- Entries for this version only, copied from CHANGELOG.md.
## Asset Checksum
\`$(shasum -a 256 dist/mnemo-aarch64-apple-darwin.tar.gz | awk '{print $1}') mnemo-aarch64-apple-darwin.tar.gz\`
EOFCreate the GitHub release:
gh release create vX.Y.Z \
dist/mnemo-aarch64-apple-darwin.tar.gz \
--title "mnemo vX.Y.Z" \
--notes-file /tmp/mnemo-notes.mdAfter the release asset is published, update Formula/mnemo.rb in
Toady00/homebrew-tap with the new version url and sha256, and commit it
as Update mnemo to X.Y.Z. Confirm the formula resolves by upgrading, which
re-verifies the checksum against the published asset:
brew update && brew upgrade mnemo && mnemo --versionBuild an optimized local macOS binary:
cargo build --releaseThe binary will be created at:
target/release/mnemoInstall it somewhere on your PATH, for example:
mkdir -p ~/.local/bin
cp target/release/mnemo ~/.local/bin/mnemoThen run it from anywhere:
mnemoMake sure ~/.local/bin is on your PATH if the command is not found.
The first time you run mnemo, macOS may ask for microphone permission for
your terminal app. Approve microphone access, then run the command again if the
first attempt fails.