This guide and software was made using AI: Claude Opus 5.5 / DeepSeek Flash
Overview
This guide shows how to add AI assistance to the dom0 terminal in Qubes OS. The AI runs in a dedicated AppVM, and dom0 talks to it with qvm-run. dom0 itself never gets network access.
The setup uses qai, which has two parts:
- A PTY wrapper that buffers the terminalβs input and output.
- A command-line tool, qai, that sends your question to the AI.
When you run qai, it automatically includes your last command and its output. The AI sees exactly what went wrong without you copying and pasting anything.
You donβt have to use this code. It is small and simple, so you can also hand it to your own AI and have it write a version that fits your setup.
Example
Here the user tries to start a qube that is already running, then asks why it failed:
user@dom0:~$ qvm-start sys-net
Error: domain sys-net is already running
user@dom0:~$ qai why didnt the command work?
qai: sending 174 bytes to ai-llm ...
ββ answer from ai-llm ββββββββββββββββββββββββββββββββββββββ
The command didnβt βworkβ because sys-net was already running. qvm-start only starts a qube that is currently stopped; if itβs already running, Qubes returns an error and exit status 1:
Error: domain sys-net is already running
This is not a failure of Qubes or of sys-netβit just means there was nothing to start.
To check its state:
qvm-ls sys-net
If you need to restart it, shut it down first and start it again:
qvm-shutdown sys-net && qvm-start sys-net
Or, depending on your Qubes version/tools:
qvm-restart sys-net
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
user@dom0:~$
The user only typed the question. qai added the failed command and the error message on its own.
Before you start: security
dom0 is the most trusted part of Qubes, so think through what this setup changes:
- Your terminal output leaves dom0. Every qai call sends the last command and its output to the AppVM, and from there to the AI provider (DeepSeek in this guide). Donβt use it on output that contains secrets.
- The AppVMβs answer is printed in dom0. Treat the AIβs suggestions as advice, not commands. Read every command before you run it in dom0.
- Code you install in dom0 must be trusted. Read the qai scripts before installing them, or write your own version.
If you want nothing to leave your machine, you can run a local model in the AppVM instead. The dom0 side stays the same.
Step 1: Create the template
Start from a minimal Debian template so the AI qube contains as little as possible. In dom0, clone it:
qvm-clone debian-13-minimal debian-13-llm
Step 2: Configure the template
The minimal template lacks networking and passwordless sudo. Open a root terminal in the template from dom0:
qvm-run --user root debian-13-llm xterm
In that terminal, install the two packages:
apt install qubes-core-agent-passwordless-root
apt install qubes-core-agent-networking
Then shut the template down, so the changes are saved. Run this in dom0:
qvm-shutdown debian-13-llm
Step 3: Create the AppVM
Create an AppVM named ai-llm based on the new template. In dom0:
qvm-create --class AppVM --template debian-13-llm --label red ai-llm
βlabel sets the window color; pick any color you like. The AppVM gets your default NetVM, which it needs to reach the AI provider.
Step 4: Configure the AppVM
The AppVM runs Simon Willisonβs llm command-line tool with a DeepSeek plugin. It is installed in a Python virtual environment in the home directory, so it survives reboots.
Open a terminal in the AppVM from dom0:
qvm-run ai-llm xterm
Install pip and venv:
sudo apt install python3-pip python3-venv
Create the virtual environment and install the DeepSeek plugin:
python3 -m venv llm
./llm/bin/pip install llm-deepseek-ya
Save your DeepSeek API key. The command prompts you to paste it:
./llm/bin/llm keys set deepseek
Test that the AppVM can reach the model:
./llm/bin/llm 'What is the capital of Germany?' -m deepseek-flash
If you get an answer, restart the AppVM before moving on.
Step 5: Verify from dom0
Check that dom0 can send a question to the AppVM and read the answer. In a dom0 terminal:
echo "What is the capital of Germany?" | qvm-run -p ai-llm '~/llm/bin/llm -m deepseek-flash -o show_reasoning false'
qvm-run -p pipes standard input into the command in the AppVM and prints its output in dom0. This is the same mechanism qai uses. If the answer appears, the AI side is ready.
Step 6: Install qai in dom0
dom0 has no network, so download the repo in a qube first and copy it over. For example, clone it in a disposable or work qube:
git clone https://github.com/renehoj/qubes-ai
tar czf qubes-ai.tar.gz qubes-ai
Then pull the archive into dom0 (replace work with the qube you used):
qvm-run -p work 'cat ~/qubes-ai.tar.gz' > qubes-ai.tar.gz
tar xzf qubes-ai.tar.gz
Read through the scripts, then run the install script from the repo. Finally, close and reopen your dom0 terminal so the PTY wrapper starts.
Run a command that fails, then type qai followed by your question. Use qai --new to start a fresh conversation.