open-voicetyping is a Linux-first, AI-driven dictation system that turns speech into real keyboard events. Built for GNOME and designed with privilege separation, it combines Whisper-based transcription with a hardened /dev/uinput backend. Ideal for developers and power users who want local control, extensibility, and strong security boundaries. Allows to use remote providers for TTS like Groq and OpenAI - more coming soon!
Voicetyping uses a two-service design for security through privilege separation:
┌─────────────────────────────────────────────────────────────────┐
│ GNOME Shell Extension │
│ (voicetyping@cx-lab.com) │
│ Panel indicator, keyboard shortcut handling │
└───────────────────────────┬─────────────────────────────────────┘
│ DBus (system bus)
│ com.cxlab.VoiceTyping
▼
┌─────────────────────────────────────────────────────────────────┐
│ voicetyping-core.service │
│ (runs as logged-in user) │
│ • Audio recording (PyAudio + LAME) │
│ • Transcription (OpenAI Whisper / Groq API) │
│ • State machine management │
└───────────────────────────┬─────────────────────────────────────┘
│ DBus (system bus)
│ com.cxlab.VirtualKeyboard
▼
┌─────────────────────────────────────────────────────────────────┐
│ voicetyping-keyboard@voicetyping.service │
│ (runs as 'voicetyping' user) │
│ • Text → keyboard events via /dev/uinput │
│ • Isolated for security (input group access) │
└─────────────────────────────────────────────────────────────────┘
voicetyping-core.service (user service)
- Runs as the logged-in GNOME user
- Has access to audio devices (including Bluetooth)
- Handles recording, transcription, and state management
- DBus interface:
com.cxlab.VoiceTyping
voicetyping-keyboard@voicetyping.service (system service)
- Runs as dedicated
voicetypinguser ininputgroup - Has write access to
/dev/uinputfor keyboard simulation - Isolated from user session for security
- DBus interface:
com.cxlab.VirtualKeyboard
Key methods:
StartRecording()/StopRecording()- Control recording stateEmitText(text)- Send transcribed text to keyboard service
Key signals:
RecordingStateChanged(boolean)- Notifies extension of state changesErrorOccurred(category, message)- Reports errors to extension
The two-service design provides privilege separation:
/dev/uinputaccess is restricted to a dedicated system user- The main service runs with user privileges (audio access)
- No single process has both audio capture and keyboard injection capabilities
- Python >=3.12
- GNOME Shell (for extension)
Given Debian as the target system
- libportaudio2 - PortAudio runtime library
- portaudio19-dev - PortAudio development files
- libmp3lame0 - LAME MP3 encoder runtime library
- libmp3lame-dev - LAME MP3 encoder development files
- libudev-dev - udev development files (needed for building)
Use .deb package from releases or build from sources using make debian-build or perform manual installation in following way:
-
Build python wheel and install it in the system or python virtual environment
$ poetry build
Building voicetyping (0.2.1) Building sdist
- Building sdist
- Built voicetyping-0.2.1.tar.gz Building wheel
- Building wheel
- Built voicetyping-0.2.1-py3-none-any.whl
$ pip3.12 install dist/voicetyping-0.2.1-py3-none-any.whl
$ which voicetyping-server
Backend uses python-uinput for simulating keyboard from text transcript. On many linux systems /dev/uinput file is forbidden to be accessed without root privileges.
$ ls -lau /dev/input crw------- 1 root root 10, 223 Jul 27 14:47 /dev/uinput
On the other hand it's a bit problematic to give random application access to root therefore I prefer to use group
input and give it write permissions to /dev/uinput device. Therefore, my advice is to use following setup
(tailored for Debian):
-
Load the uinput kernel module (if not already loaded):
$ sudo modprobe uinput
and make this permanent across reboots:
$ echo "uinput" | sudo tee /etc/modules-load.d/uinput.conf
-
Create user who will run python backend:
sudo useradd -G input,audio -M -r voicetyping -
Allow input group write to
/dev/uinputby creating/etc/udev/rules.d/99-uinput.ruleswith following contents:
KERNEL=="uinput", GROUP="input", MODE="0660"
-
sudo udevadm control --reload-rules
-
sudo udevadm trigger
$ ls -lau /dev/uinput crw-rw---- 1 root input 10, 223 Jul 27 14:47 /dev/uinput
There is some DBus policy tweaking necessary to allow gnome shell extension to talk to python backend. There are two systemd services that need to be started:
- voicetyping-keyboard.service runs as user
voicetypingand has access to/dev/uinputdevice. - voicetyping-core.service runs as user who is logged in gnome session and has access to gnome shell DBus. More importantly it will have access to audio devices and even bluetooth ones (if "default" audio source is selected in the extension settings).
sudo cp etc/voicetyping-dbus-policy.conf /etc/dbus-1/system.d/voicetyping.conf
# !! adjust user names
sudo systemctl reload dbus
sudo cp etc/voicetyping-keyboard.service /etc/systemd/system/voicetyping-keyboard@voicetyping.service
# !! adjust python paths
sudo systemctl enable voicetyping-keyboard@voicetyping.service
sudo systemctl start voicetyping-keyboard@voicetyping.service
# run as daily driver user (logging in to gnome session) or use user-level systemd service definition
systemctl edit --force --full voicetyping-core.service
# !! paste contents of etc/voicetyping-core.service and adjust python paths and user name
systemctl enable voicetyping-core.service
systemctl start voicetyping-core.service$ cd extension
$ make install
You probably will need to restart your GNOME session i.e. logout/login
it's important to keep access to /dev/uinput very selective both for write and read permissions since that's how malware could get access to whatever user is typing
journalctl -u voicetyping-core.service --follow
journalctl -u voicetyping-keyboard@voicetyping.service --follow
gnome-extensions list
gnome-extensions disable voicetyping@cx-lab.com
gnome-extensions enable voicetyping@cx-lab.comThis project is licensed under the GNU General Public License v3.0 or later - see the LICENSE file for details.
Copyright (c) 2024-2026 Lukasz Jachym lukasz.jachym@gmail.com
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.