VM + Daemon Setup
This guide walks through running the SysKnife daemon inside a virtual machine and connecting Claude Code (or any MCP client) to it from the host.
Two transport options are covered:
| Transport | Best for | Requirement |
|---|---|---|
| SSH socket tunnel | Any VM, any hypervisor | SSH access to the guest |
| virtio-vsock | KVM/QEMU guests on the same host | vhost_vsock kernel module |
Both transports produce the same .mcp.json — only the SYSKNIFE_SOCKET value
and the optional SYSKNIFE_TOKEN differ.
1. Install and start the daemon in the VM
SSH into the guest and run:
# Clone the repo (or copy a pre-built binary)
git clone https://github.com/lacs-project/sysknife
cd sysknife
# Build and install (requires Rust stable)
make build
sudo make install
# Enable and start the daemon
sudo systemctl enable --now sysknife-daemon
# Confirm it is running
systemctl status sysknife-daemon
The daemon listens on /run/sysknife/daemon.sock by default when managed by
systemd. You can verify the socket exists:
ls -la /run/sysknife/daemon.sock
Silverblue / Kinoite (rpm-ostree hosts):
make installuses the correct rpm-ostree override flags automatically. No extra steps needed.
2a. SSH socket tunnel (simplest)
This approach works on any Linux VM regardless of hypervisor. It forwards the daemon's Unix socket to a local path on your host.
On the host — open the tunnel
# Replace <user>, <vm-host-or-ip>, and <port> for your setup.
# QEMU user-mode networking typically maps port 22 → 2222 on localhost.
ssh -fN \
-L /tmp/sysknife-vm.sock:/run/sysknife/daemon.sock \
-p 2222 <user>@localhost
For a network-accessible VM (libvirt bridge, cloud VM):
ssh -fN \
-L /tmp/sysknife-vm.sock:/run/sysknife/daemon.sock \
<user>@<vm-ip>
-fNforks the SSH process and keeps the tunnel open without running a remote command. The local socket/tmp/sysknife-vm.sockis created on the host and forwards transparently to the daemon inside the guest.
Configure and connect (SSH tunnel)
Run the setup wizard on the host and choose the integration you want:
npx sysknife-setup
Use --claude, --cursor, --codex, or --all if you want a direct
path without the picker.
When prompted for the daemon socket, enter /tmp/sysknife-vm.sock.
Or set SYSKNIFE_SOCKET manually in .mcp.json:
{
"mcpServers": {
"sysknife": {
"command": "/path/to/sysknife",
"args": ["mcp-server"],
"env": {
"SYSKNIFE_SOCKET": "/tmp/sysknife-vm.sock",
"SYSKNIFE_LLM_PROVIDER": "anthropic",
"ANTHROPIC_API_KEY": "<your-api-key>"
}
}
}
}
Test the SSH tunnel connection
sysknife --dry-run "check disk usage"
2b. virtio-vsock (faster, no SSH required)
virtio-vsock is a kernel-level channel between the host and KVM/QEMU guests. It requires no network stack and survives network changes inside the guest.
Prerequisites
# On the host — load the vhost_vsock module
sudo modprobe vhost_vsock
lsmod | grep vhost_vsock # confirm it loaded
# Persist across reboots
echo vhost_vsock | sudo tee /etc/modules-load.d/vhost_vsock.conf
Your VM must be started with a vsock device. With libvirt add this to the domain XML:
<devices>
<vsock model='virtio'>
<cid auto='yes'/>
</vsock>
</devices>
With QEMU directly:
qemu-system-x86_64 \
... \
-device vhost-vsock-pci,guest-cid=10
Find the guest CID
The Context ID (CID) is the vsock address of the guest. It is assigned by the hypervisor and visible in two ways:
From the host (libvirt):
virsh dominfo <vm-name> | grep -i cid
# or
virsh dumpxml <vm-name> | grep cid
From inside the guest:
cat /sys/class/vsock/local_cid # e.g. prints "10"
Generate and distribute the pre-shared token
vsock connections require a pre-shared token to authenticate the host to the daemon. This prevents other processes on the host from connecting.
The token file holds only the trimmed token itself — no role: prefix.
The role granted to a token-authenticated connection comes from a separate
env var, SYSKNIFE_TOKEN_ROLE (default Dev if unset), read by the daemon
process — not from the file contents.
The default token path is ~/.config/sysknife/token (the daemon's XDG config
directory, next to prefs.md; respects $XDG_CONFIG_HOME if set), not
/etc/sysknife/token.
# On the host — generate a token
openssl rand -hex 32
# e.g.: a3f8c2d1e4b7a0f9...
# On the guest — write the token to the daemon's token file
mkdir -p ~/.config/sysknife
echo "a3f8c2d1e4b7a0f9..." > ~/.config/sysknife/token
chmod 600 ~/.config/sysknife/token
# Grant Admin to token-authenticated vsock connections (default is Dev)
sudo systemctl edit sysknife-daemon
# Add under [Service]:
# Environment=SYSKNIFE_TOKEN_ROLE=admin
# Restart the daemon to pick up the token and role
sudo systemctl restart sysknife-daemon
The token file contains just the raw token, with no role prefix:
# ~/.config/sysknife/token
<paste-token-here>
Configure and connect (vsock)
Run the setup wizard on the host and choose the integration you want:
npx sysknife-setup
Use --claude, --cursor, --codex, or --all if you want a direct
path without the picker.
When prompted for the daemon socket, enter vsock://<CID>:9734 — for example
vsock://10:9734. The wizard detects the vsock:// prefix and prompts for
the token.
Or configure .mcp.json manually:
{
"mcpServers": {
"sysknife": {
"command": "/path/to/sysknife",
"args": ["mcp-server"],
"env": {
"SYSKNIFE_SOCKET": "vsock://10:9734",
"SYSKNIFE_TOKEN": "<your-hex-token>",
"SYSKNIFE_LLM_PROVIDER": "anthropic",
"ANTHROPIC_API_KEY": "<your-api-key>"
}
}
}
}
Test the vsock connection
sysknife --dry-run "check disk usage"
3. Reload the MCP client
In Claude Code, run /reload-plugins to pick up the new .mcp.json. The
sysknife_plan and sysknife_execute tools should appear in the tool list.
Troubleshooting
"connection refused" or "no such file or directory"
- SSH tunnel: confirm the tunnel is running:
ls -la /tmp/sysknife-vm.sock - vsock: confirm
vhost_vsockis loaded (lsmod | grep vhost_vsock) and the CID is correct (cat /sys/class/vsock/local_cidinside the guest) - Confirm the daemon is running:
systemctl status sysknife-daemoninside the guest
"SYSKNIFE_TOKEN is not set; vsock connections require a pre-shared token"
Set SYSKNIFE_TOKEN in the env block of .mcp.json (see above).
"authentication failed" on vsock
The token on the host (SYSKNIFE_TOKEN in .mcp.json) does not match the
contents of ~/.config/sysknife/token on the guest. Verify both match
exactly (the file should hold just the raw token, no role: prefix).
SSH tunnel drops when the terminal closes
Use ssh -fN (forks, no command) or run it via a systemd user unit or
tmux/screen session. Alternatively switch to vsock which does not depend
on SSH.
vsock not available in the guest
Check: ls /dev/vsock inside the guest. If absent, the VM was started without
a vsock device — add the <vsock> element to the libvirt XML (see above) and
restart the VM.
Security notes
- SSH tunnel: the tunnel is authenticated by your SSH keypair. No extra secrets are needed.
- vsock: the pre-shared token (
SYSKNIFE_TOKEN) prevents other host processes from connecting. Keep it out of version control — add.mcp.jsonto.gitignore. - Socket file permissions: the daemon socket at
/run/sysknife/daemon.sockis owned byroot:sysknifewith mode0660— only members of thesysknifegroup can connect locally. The SSH tunnel and vsock bypass this by terminating at the daemon's authenticated IPC layer.