Skip to content

Latest commit

 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mnemo

mnemo records a voice note from your default microphone, transcribes it with ElevenLabs, and stores the resulting text in Hindsight.

Requirements

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

Platform Support

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.

Usage

Start recording a voice note:

mnemo record

Press Enter in that terminal when you are done. mnemo will stop recording, transcribe the audio, and retain the transcript in Hindsight.

Stream Decks And External Triggers

As an alternative to pressing Enter, you can stop an active recording from another process:

mnemo stop

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

Installation

Install with Homebrew:

brew tap Toady00/tap
brew install mnemo

To install the latest development build from the default branch instead:

brew install --HEAD mnemo

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

Configuration

The default config file location is:

~/.config/mnemo/config.toml

Create a starter config:

mnemo init

Or create it from the example:

mkdir -p ~/.config/mnemo
cp config.example.toml ~/.config/mnemo/config.toml

All 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

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

All three forms produce the same payload field:

"observation_scopes": [["scope:platform"], ["scope:business", "team:core"]]

macOS Keychain

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_KEY

For 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_KEY

List API keys stored in Keychain:

mnemo keychain list

Remove selected Keychain entries interactively:

mnemo keychain remove

Remove all mnemo API keys with confirmation:

mnemo keychain remove --all

Remove all mnemo API keys without confirmation:

mnemo keychain remove --all --force

Keychain 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

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 record

Use a named profile:

mnemo record --profile business

Or select a profile with an environment variable:

export MNEMO_PROFILE="business"
mnemo record

Profile 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-notes

Supported 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"]]'

Development

Check that the project builds:

cargo check

Format the code:

cargo fmt

Run the app in development mode:

cargo run -- record

mnemo defaults to record, so this is equivalent:

cargo run

Override config from the CLI:

cargo run -- --bank personal --language auto

Use a named profile:

cargo run -- record --profile business

Or with the installed binary:

mnemo record --profile business

Set the Hindsight URL from the CLI:

cargo run -- --hindsight-url https://your-hindsight-api.example.com

Use a different config file:

cargo run -- --config /path/to/config.toml

When 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 -- stop

record opens a Unix socket at:

~/.local/state/mnemo/mnemo.sock

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

Release Process

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

In 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 warnings

Scan 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 all

If 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 --release

Package 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.gz

Commit 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.Z

Release 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\`
EOF

Create 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.md

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

Build A Local Binary

Build an optimized local macOS binary:

cargo build --release

The binary will be created at:

target/release/mnemo

Install it somewhere on your PATH, for example:

mkdir -p ~/.local/bin
cp target/release/mnemo ~/.local/bin/mnemo

Then run it from anywhere:

mnemo

Make sure ~/.local/bin is on your PATH if the command is not found.

macOS Notes

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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages