This guide starts from a fresh Omarchy machine.
sudo pacman -S --needed go jq pipewire-pulse libpulse # most of these come with Omarchy
omarchy-voxtype-install # optional: F9 dictationYou need Go 1.27 or newer. An older Go fetches the right toolchain by itself (GOTOOLCHAIN=auto,
the default). The bar plugin needs Omarchy's shell bar, and the key binds need Omarchy's Lua
Hyprland config (~/.config/hypr/bindings.lua).
vcd needs three OpenAI-compatible endpoints, set in ~/.config/voice-computer/config.json:
| Part | Route vcd calls | Local server | Hosted |
|---|---|---|---|
stt: speech to text |
POST {url}/v1/audio/transcriptions |
whisper.cpp's whisper-server |
OpenAI whisper-1 |
chat: the brain |
POST {url}/v1/chat/completions (streamed, with tools) |
llama.cpp's llama-server |
OpenAI, or any OpenAI-compatible API |
tts: text to speech |
POST {url}/v1/audio/speech (response_format: pcm, 24 kHz mono) |
Kokoro-FastAPI | OpenAI tts-1 |
url is the base URL without /v1. Each part can use a different server.
Every model runs on this machine and nothing leaves it. These are the defaults in
install/config.example.json:
# Speech to text on :8081 (whisper.cpp; a model from its models/download-ggml-model.sh)
whisper-server -m ggml-base.en.bin --port 8081 --inference-path /v1/audio/transcriptions
# The brain on :8080 (llama.cpp; any instruct GGUF that can call tools, 3-8B is plenty;
# --jinja is needed for tool calls)
llama-server -m your-model.gguf --jinja --port 8080 -c 8192
# Text to speech on :8880 (Kokoro-FastAPI, CPU image)
docker run -d --name kokoro -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-cpu:latestSmall models answer best with thinking off, or with a reasoning format that keeps the thinking out
of the answer (llama-server's default). vcd drops <think>…</think> blocks from what it speaks, but
not reasoning whose <think> the chat template put in the prompt (--reasoning-format none).
Put the key in a file only you can read, and name the file in the config. Never put the key itself in the config:
install -m 600 /dev/null ~/.config/voice-computer/openai.key
$EDITOR ~/.config/voice-computer/openai.key{
"backend": "openai",
"stt": {"url": "https://api.openai.com", "model": "whisper-1", "key_file": "~/.config/voice-computer/openai.key"},
"chat": {"url": "https://api.openai.com", "model": "gpt-4o-mini", "key_file": "~/.config/voice-computer/openai.key"},
"tts": {"url": "https://api.openai.com", "model": "tts-1", "key_file": "~/.config/voice-computer/openai.key"},
"voice": "alloy"
}With a hosted backend, everything in the README's privacy section goes to that provider.
git clone https://github.com/makefunstuff/omarchy-voice-computer
cd omarchy-voice-computer
install/install.shThe installer is safe to run again after an update. It:
- builds
~/.local/bin/vcand startsvoice-computer.service(a user service); - writes
~/.config/voice-computer/config.jsonfrominstall/config.example.jsonif you have no config yet (it never overwrites one); - adds a marked block (
>>> voice-computer…<<< voice-computer) to~/.config/hypr/bindings.luaand a Voice entry to the Omarchy menu (~/.config/omarchy/extensions/omarchy-menu.jsonc); - copies the bar plugin to
~/.config/omarchy/plugins/and puts it on the bar after the audio widget; - points voxtype (if installed) at your STT endpoint, turns its own hotkey off (Omarchy's F9 bind
drives it) and adds a drop-in for
voxtype.service.
Before it changes a file it backs it up beside the original as FILE.bak.voice-computer.<time>:
bindings.lua, the menu file, shell.json and config.json. voxtype's config.toml is backed up
once, at the first install, so that backup is your own config from before (an empty one: you had none). If bindings.lua already
binds the talk or confirm key, or SHIFT or SUPER + SHIFT with the talk key, outside the block (an o.bind("F7", …) line), it stops and changes
nothing. It doesn't look at binds in other files, or at other spellings of the key.
Then edit the config for your endpoints and restart:
omarchy-launch-editor ~/.config/voice-computer/config.json # or Voice → Edit settings in the menu
systemctl --user restart voice-computer
vc statusF9 dictation reads stt from the config when the installer runs. After you change stt, run
install/install.sh again so voxtype follows.
go run ./cmd/fakevoice starts a fake backend and prints a config for it. It hears every turn as the
same words and answers with a second of silence per sentence, so you can try the keys and the bar.
| Key | What it does |
|---|---|
| F7 (hold) | Talk; let go and the computer answers. 60 s at most. |
| F7 (tap) | Stop the answer, or say no to an open confirm |
| SUPER + ALT + F7 | Confirm the open confirm; it confirms on release, once |
| F9 (hold) | Dictation, typed at the cursor (Omarchy's own bind) |
| SHIFT + F7 | Repeat the last agent reply (only with agent replies) |
| SUPER + SHIFT + F7 | Agent replies on or off |
To use other keys, run the installer with them. The confirm key must be modifiers plus the talk key,
and the agent-reply keys follow the talk key. The installer rewrites its block in bindings.lua and
keeps the keys in the config (talk_key, confirm_key), so the prompts and the bar name them and
later runs keep them:
VC_TALK_KEY="F8" VC_CONFIRM_KEY="SUPER + ALT + F8" install/install.shDon't edit the marked block by hand: the next install puts it back. Bind anything else outside it.
~/.config/voice-computer/config.json. Missing keys keep their defaults:
| Key | Default | What it is |
|---|---|---|
backend |
openai |
openai (the three endpoints), or realtime: a Realtime-style websocket at {realtime.url}/v1/realtime?model=… answers (text in, audio out), and stt/tts are still used. Not tried against OpenAI's own Realtime API. |
stt, chat, tts, realtime |
see above | {"url", "model", "key_file"}; key_file empty means no key |
voice |
af_heart |
the TTS voice |
chat_extra |
none | extra fields for every chat request, e.g. {"temperature": 0.2} or {"chat_template_kwargs": {"enable_thinking": false}} |
tts_extra |
none | extra fields for every TTS request, e.g. {"speed": 1.1} |
instructions |
built in | the brain's standing instructions |
max_output_tokens |
1000 | the brain's answer length, sent as max_tokens; 0 leaves it out (for models that want max_completion_tokens in chat_extra) |
talk_key, confirm_key |
F7, SUPER + ALT + F7 |
the keys the prompts and the bar name (set by the installer) |
tap_ms |
300 | a press shorter than this is a tap |
max_turn_s |
60 | the mic closes after this |
idle_close_s |
120 | the session closes after this long idle; the last turns are replayed into the next one |
overlay_hide_s |
8 | the overlay hides this long after an answer |
history |
20 | turns kept in memory and replayed into a new session |
card_timeout_s |
30 | an unanswered confirm is a no after this |
notes_file |
~/Documents/notes/inbox.md |
where "note that …" writes |
terminals |
common terminals | window classes never closed |
reply_spaces |
none | agent replies: the Shepherdr spaces whose coordinators' replies are spoken; none means off |
reply_voice |
the computer's | the TTS voice for agent replies |
reply_idle_s, reply_poll_ms |
30, 2000 | how long you must be idle before a reply to news plays; how often Shepherdr is asked |
shepherdr, shepherdr_credential |
shepherdr, ~/.shepherdr/clients/vcd.credential.json |
the command and vcd's own credential |
The plugin shows a mic glyph after the audio widget:
- dim mic: ready;
Mmuted (speech off, the mic still works) - filled mic: listening,
…thinking, speaker: speaking,•needs you (a confirm is open) - crossed-out mic: off; hover or click to see why
Click it for the last turns, the model answering and the mute switch. Middle-click cancels. Its
settings in Omarchy's bar settings include the vc command it runs.
- The bar says Off. Hover it, or run
vc status, to see why: an endpoint can't be reached, a key file is missing, or a key was refused. Check each URL withcurl {url}/v1/models. - Nothing happens on F7. Is
vcrunning (systemctl --user status voice-computer)? Doeshyprctl binds | grep -A3 F7show the bind? Another app may grab F7 in its own submap. - It never hears anything. Check the default source with
wpctl status, and record a test clip withparecord --rate=16000 --channels=1 /tmp/t.wav. - The brain never acts. The chat server must support tool calls (llama.cpp needs
--jinja).VC_LIVE=1 go test ./internal/intent -run TestPhrasesLive -vruns the labelled phrase set against your backend. - Slow answers.
vc latencyshows p50/p90 for each step.journalctl --user -u voice-computer -fshows each turn's timings (never words). - F9 types nothing.
journalctl --user -u voxtype -f. vcd sends a notification when a clip is lost, and turns voxtype to local Whisper while the STT endpoint is down.
install/uninstall.shThis removes the service, vc, the bar plugin, its blocks in bindings.lua and the menu (after a
backup) and voxtype's drop-in, and sets voxtype back to local Whisper. Its other settings (the VAD,
the endpoint, its hotkey off) stay. It keeps
~/.config/voice-computer/ and the backups. To get voxtype's config exactly as it was before the
first install, copy back the oldest ~/.config/voxtype/config.toml.bak.voice-computer.*.
If you run coding agents under Shepherdr, the voice computer can speak what a space's coordinator writes to you in its chat. It stays off until you name the spaces in the config:
{ "reply_spaces": ["home", "my-project"], "reply_voice": "am_adam" }Then run install/install.sh again. It issues vcd its own credential to read those chats
(shepherdr client issue vcd, saved in ~/.shepherdr/clients/vcd.credential.json). vcd asks Shepherdr
every 2 s whether a coordinator is working. When its turn ends, vcd speaks the newest reply:
- Plain and short: no markdown, code, paths, links, IDs, hashes or log lines, and at most the first two short sentences. The overlay shows it under the space's name.
- Never over you: a reply waits while you hold the talk key, while a turn is being answered, while the computer speaks, or while you dictate, and plays after that. A tap of the talk key drops it.
- News waits: a reply to anything other than your own last message (news, a heartbeat) waits
until you have been idle for
reply_idle_s. - Quiet: say "be quiet" (or press SUPER + SHIFT + F7, or use the menu) to mute replies, and "talk to me" to hear them again. SHIFT + F7 repeats the last one.
- Privacy: the reply's text goes to your TTS endpoint and nowhere else. It never reaches the brain, and its words are never logged.
Handing work to agents by voice is planned for a later release.