Skip to content

Control a Kali Linux Laptop from Windows 11: SSH, Hermes, Remote Desktop and Screen Mirroring

  • kali-linux
  • windows
  • openssh
  • xrdp
  • x11vnc
  • hermes-agent
  • powershell

This tutorial turns a Windows 11 laptop into the control seat for a physical Kali Linux laptop on the same home network. You get passwordless SSH, Hermes Agent running commands on Kali, a separate remote desktop with XRDP, and a live mirror of the laptop’s real screen with x11vnc.

A title card for a workspace setup guide in yellow lettering over a blurred red and blue photo, with the labels SSH, remote control, Termius, Hermes, Claude Code, Kali, Ubuntu and Windows

I use this setup on my own machines, and I repeat it every time I add a laptop. The steps below are the ones I follow, with my own addresses, names and keys replaced by example values. A companion tutorial covers the same setup for an Ubuntu laptop: Control an Ubuntu Laptop from Windows 11.

Every command in this tutorial uses the example values below, so you can copy it as it is. These are not your values. Replace each one with your own before you run a command.

What Value in this tutorial How to find yours
Home network 192.168.50.0/24 ipconfig on Windows
Router (gateway and DNS) 192.168.50.1 ipconfig → Default Gateway
Windows laptop address 192.168.50.10 ipconfig → Wi-Fi → IPv4 Address
Kali laptop address (static) 192.168.50.20 You choose it in Phase 1
Wi-Fi connection name on Kali HomeWiFi nmcli connection show --active on Kali
Kali Wi-Fi interface wlan0 ip -4 -br addr on Kali
Kali account labuser whoami on Kali
Kali hostname lab-Kali hostname on Kali
Windows account folder C:\Users\Lab User echo $env:USERPROFILE in PowerShell
SSH alias, key file and Hermes profile kali-lab, kali_lab, kali-lab You choose them in Phases 4 and 6
Hermes gateway name Kali Lab You choose it in Phase 6
LM Studio API port 1234 (LM Studio’s default) LM Studio → Developer → Local Server
Folder for the screen launcher and its icon C:\RemoteAccess You choose it in Phase 7

The example Windows account folder contains a space on purpose. Many real account folders have one, and a space breaks a path that is not in quotes.

A short Your values note marks each command that holds a value from this table.

  • The SSH alias is kali-lab (hyphen).
  • The key file is kali_lab (underscore).
  • The login user is labuser.
  • The Hermes profile that drives Kali is also named kali-lab.

These are four different things. Most errors during this setup come from mixing them up (Phase 10).

Port Service Where it listens Reached from Windows by
TCP 22 OpenSSH Kali, all interfaces ssh kali-lab, Hermes (both modes)
TCP 3389 xrdp Kali, all interfaces Remote Desktop app (mstsc)
TCP 5900 x11vnc (on demand) Kali localhost only SSH tunnel → localhost:5901 on Windows
TCP 1234 LM Studio API Windows Kali’s Hermes (Mode B model)

0.4 What you need, and the versions this was tested with

Section titled “0.4 What you need, and the versions this was tested with”
  • A Windows 11 laptop with Hermes Desktop installed and signed in to a model provider.
  • A physical Kali Linux laptop with the default Xfce desktop, and an administrator account (a member of the sudo group).
  • Both laptops on the same home Wi-Fi network, which you control.
  • LM Studio on Windows, only for Mode B.
  • PowerShell on Windows. Windows PowerShell 5.1 and PowerShell 7 both work for this tutorial.
Machine Software Tested version
Windows OpenSSH client OpenSSH_for_Windows_9.5p2
Windows Hermes Desktop (Microsoft Store) package NousResearchInc.HermesAgent, agent v0.22.5
Windows LM Studio 0.4.26
Windows TigerVNC viewer 1.16.2
Kali Kali GNU/Linux Rolling 2026.3
Kali Desktop stack Xfce 4.20, LightDM 1.33, xrdp 0.10.6, xorgxrdp 0.10.5, dbus-x11
Kali OpenSSH server 10.6p1
Kali Other x11vnc 0.9.17, tmux 3.7c, ufw installed but inactive
Kali Hermes Agent (Mode B) v0.22.5, git install in ~/.hermes/hermes-agent

Administrator-account warning: because labuser is an administrator, commands Hermes runs as labuser can reach everything this account can, and some commands can request sudo.

  • Do not configure passwordless unrestricted sudo.
  • Do not put the sudo password in Hermes prompts or configuration.
  • Keep command-approval safeguards enabled.

This tutorial uses one account for everything: SSH, Hermes, RDP and VNC all use labuser.

flowchart LR
    subgraph W["Windows 11 (192.168.50.10)"]
      HD["Hermes Desktop"]
      PA["Profile kali-lab<br/>(agent on Windows)"]
      PD["Profile default<br/>(commands on Windows)"]
      RDP["Remote Desktop app<br/>mstsc.exe"]
      VV["TigerVNC viewer<br/>localhost:5901"]
      LMS["LM Studio API :1234"]
    end
    subgraph K["Kali laptop (192.168.50.20, user labuser)"]
      SSHD["OpenSSH :22"]
      HK["Hermes on Kali<br/>(Mode B)"]
      XR["xrdp :3389<br/>separate desktop :10"]
      XV["x11vnc localhost:5900<br/>real screen :0"]
    end
    HD --> PA
    HD --> PD
    PA -- "Mode A: ssh commands (key kali_lab)" --> SSHD
    HD -. "Mode B: SSH tunnel + dashboard" .-> HK
    HK -- "model requests" --> LMS
    RDP -- "Kali password of labuser" --> XR
    VV -- "SSH tunnel (key)" --> XV

SSH, Remote Desktop, and VNC are separate paths:

  • SSH carries Hermes traffic (both modes) and the VNC tunnel.
  • XRDP gives you a separate graphical session.
  • x11vnc shows the laptop’s actual screen.

None of the graphical paths gives Hermes mouse-and-keyboard control of the GUI; Hermes works through the terminal.

flowchart TD
    Q{"Where should the Hermes agent run?"}
    Q -->|"On Windows; commands on Kali"| A["Mode A<br/>Gateway: This device<br/>Profile: kali-lab (terminal.backend = ssh)"]
    Q -->|"On Kali; Desktop is only the window"| B["Mode B<br/>Gateway: Kali Lab (registered SSH)<br/>Hermes installed on Kali"]
    A --> A2["Model + agent on the fast laptop<br/>commands run as labuser on Kali"]
    B --> B2["Agent, sessions, memory on Kali<br/>model via LM Studio on Windows"]
  • Mode A is the recommended mode when the Windows laptop is the more powerful machine: Windows is the brain, and the Kali laptop is the hands. It is configured in a separate kali-lab profile, so the default profile keeps running commands on Windows. A per-profile setup also stops commands meant for Windows from running on Kali by accident.
  • Mode B: Desktop opens the Hermes installed on Kali through the registered SSH gateway Kali Lab.
You want Use Phase
Your own separate Kali desktop (works even if nobody is at the laptop) XRDP + Windows Remote Desktop 5
The same screen that is on the laptop’s display (like a VM console) x11vnc + TigerVNC over SSH (Open-KaliScreen.ps1) 7.4
VMware Workstation console Only for Kali running as a VM on Windows, which is a different Kali 7.2

Phase 1: Confirm the LAN and set Kali’s IP address

Section titled “Phase 1: Confirm the LAN and set Kali’s IP address”

On the Windows laptop, open PowerShell and run:

ipconfig
Get-NetConnectionProfile

Find the adapter named Wi-Fi. Example output:

Wireless LAN adapter Wi-Fi:
IPv4 Address. . . . . . . . . . . : 192.168.50.10
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.50.1

Get-NetConnectionProfile must show Wi-Fi → Private. Firewall rules in this tutorial apply to the Private profile only.

Do not use the other adapters. If VirtualBox or VMware is installed, ipconfig also lists their virtual adapters: VirtualBox Host-Only (usually 192.168.56.1), and VMware’s VMnet1 and VMnet8 (VMware picks their subnets at install). The Kali laptop cannot reach these virtual networks. LM Studio, for example, can report “Reachable at 192.168.56.1:1234”; that is the VirtualBox adapter, not the LAN. Use the Wi-Fi address.

On the Kali laptop itself, open a terminal:

ip -4 -br addr
ip route
nmcli connection show --active
nmcli device status

Record the active interface, connection profile name, current subnet, default gateway, and DNS servers. To inspect connection and DNS details:

nmcli device show
resolvectl status # if unavailable: cat /etc/resolv.conf

Example output:

wlan0 UP 192.168.50.20/24
default via 192.168.50.1 dev wlan0 proto static metric 600
active connection: HomeWiFi (802-11-wireless, wlan0)

1.3 Make Kali use a static address through Kali itself (no router access needed)

Section titled “1.3 Make Kali use a static address through Kali itself (no router access needed)”

There are two methods. Use one method for the active connection, not both.

Before setting a manual address, check whether 192.168.50.20 is already assigned to Kali. If it is not, verify that no other device uses it. A ping with no reply does not prove an address is unused. A static address set on Kali is not reserved in the router. If the address falls inside the router’s DHCP range, another device could receive it later; if that ever happens, pick an address outside the DHCP pool.

  1. Open Kali’s Settings → Network (labels vary by desktop version).

  2. Select the active Wi-Fi network (HomeWiFi in this tutorial) and open its settings (gear icon).

  3. Open IPv4.

  4. Change the method from Automatic (DHCP) to Manual.

  5. Enter:

    Field Value
    Address 192.168.50.20
    Netmask 255.255.255.0
    Gateway 192.168.50.1
    DNS 192.168.50.1
  6. Save and reconnect if required. Continue to step 1.4.

Method B: Kali terminal with NetworkManager nmcli

Section titled “Method B: Kali terminal with NetworkManager nmcli”

Find the exact connection profile name, then apply the manual address:

nmcli connection show --active
CON="HomeWiFi" # use your exact connection name
nmcli connection show "$CON" # optional: review current settings first
sudo nmcli connection modify "$CON" \
ipv4.method manual \
ipv4.addresses "192.168.50.20/24" \
ipv4.gateway "192.168.50.1" \
ipv4.dns "192.168.50.1"
sudo nmcli connection up "$CON"

Confirm what is stored:

nmcli -g ipv4.method,ipv4.addresses,ipv4.gateway,ipv4.dns,connection.autoconnect connection show "$CON"
# expected: manual / 192.168.50.20/24 / 192.168.50.1 / 192.168.50.1 / yes

1.4 Verify Kali networking before continuing

Section titled “1.4 Verify Kali networking before continuing”

On Kali:

ip -4 -br addr
ip route
ping -c 4 192.168.50.1 # LAN routing
ping -c 4 1.1.1.1 # internet reachability
getent hosts kali.org # DNS

If any test fails, correct the address, prefix, gateway, or DNS before continuing. Then reboot and confirm the address persists:

sudo reboot
# after restart:
ip -4 -br addr # wlan0 must show 192.168.50.20/24

On Windows PowerShell:

ping 192.168.50.20
Test-NetConnection 192.168.50.20 -Port 22 # after Phase 3

A successful ping with TcpTestSucceeded : False on port 22 means the network is fine but SSH is not running yet. Phase 3 fixes it.

Do not create another user. On Kali, signed in as labuser:

whoami
id
sudo -v
sudo -l
getent passwd labuser | cut -d: -f7

Expected:

  • whoami → labuser.
  • id → uid 1000, groups include sudo and adm. adm lets labuser read /var/log/xrdp*.log and the system journal without sudo, which is handy for troubleshooting.
  • The login shell is /usr/bin/zsh, Kali’s default. This matters in Phase 6B: non-interactive SSH commands only read ~/.zshenv.

If your account is not already an administrator, do not change account permissions as part of this network setup; review the account first.

Phase 3: Install and start the SSH server on Kali

Section titled “Phase 3: Install and start the SSH server on Kali”

On Kali, as labuser:

sudo apt update
sudo apt install -y openssh-server
sudo systemctl enable --now ssh

Check the service and listening port:

systemctl is-enabled ssh; systemctl is-active ssh # enabled / active
ssh -V # OpenSSH_10.6p1 Debian-1
ss -ltn | grep ':22 ' # LISTEN 0.0.0.0:22 and [::]:22

The stock Kali sshd_config works unchanged (UsePAM yes, KbdInteractiveAuthentication no, X11Forwarding yes, AcceptEnv LANG LC_* COLORTERM NO_COLOR). Key login and password login are both allowed by default.

OpenSSH 10 behaviour to know: sshd penalises a source address that keeps failing authentication (PerSourcePenalties). After repeated bad logins (for example, a Hermes Desktop entry with a wrong user name), the Kali journal shows srclimit_penalise: 192.168.50.10/32: activating ipv4 penalty .... sshd then drops every new SSH connection from Windows for a while, including ones that are configured correctly. Fix the bad client and wait about a minute (Phase 10).

sudo ufw status verbose # on a default Kali: Status: inactive (ufw installed, not enabled)

With UFW inactive, nothing else is needed. If you enable UFW later, allow the Windows laptop first:

sudo ufw allow from 192.168.50.10 to any port 22 proto tcp
sudo ufw allow from 192.168.50.10 to any port 3389 proto tcp

VNC needs no rule: it listens on localhost and travels inside SSH. Do not enable a default-deny policy without reviewing existing lab rules. Never create router port-forwarding for SSH, RDP, or VNC; keep them on the trusted LAN or behind a VPN.

Phase 4: Configure Windows SSH key authentication and the kali-lab alias

Section titled “Phase 4: Configure Windows SSH key authentication and the kali-lab alias”

This phase makes this command work without a password or passphrase:

ssh kali-lab

On Windows, open PowerShell (normal, not administrator):

New-Item -ItemType Directory -Force "$env:USERPROFILE\.ssh" | Out-Null
Test-Path "$env:USERPROFILE\.ssh\kali_lab"
Test-Path "$env:USERPROFILE\.ssh\kali_lab.pub"

If both exist, keep using them. Do not overwrite an existing key. If ssh-keygen asks ...kali_lab already exists. Overwrite (y/n)?, answer n. Answering y replaces the key, and Kali rejects the new one until you install the new .pub again.

Create the key only if it does not exist:

ssh-keygen -t ed25519 -f "$env:USERPROFILE\.ssh\kali_lab"

Press Enter twice at the passphrase prompts, so the key has no passphrase. Hermes connects with BatchMode=yes and cannot type a passphrase. The Windows ssh-agent service, which could hold an unlocked key, is Disabled by default (step 4.6). The key is protected by your Windows account instead.

The files are:

  • kali_lab: private key. Never share it, copy it to Kali, or paste it into chat.
  • kali_lab.pub: public key. This is what goes on Kali.

Check its fingerprint. The output is one line like 256 SHA256:<43 characters> lab user@winpc (ED25519); the comment at the end is your Windows user and computer name:

ssh-keygen -lf "$env:USERPROFILE\.ssh\kali_lab.pub"

4.2 Add the public key to the existing account on Kali

Section titled “4.2 Add the public key to the existing account on Kali”

On Windows, print the public key:

Get-Content "$env:USERPROFILE\.ssh\kali_lab.pub"

Copy the single line starting with ssh-ed25519 and ending with your Windows user and computer name (lab user@winpc in this tutorial). On Kali, as labuser:

mkdir -p ~/.ssh && chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
nano ~/.ssh/authorized_keys # paste the line on its own line; keep any existing lines
chmod 600 ~/.ssh/authorized_keys

Verify:

ls -ld ~/.ssh # drwx------ labuser labuser
ls -l ~/.ssh/authorized_keys # -rw------- labuser labuser
awk '{print $1, $NF}' ~/.ssh/authorized_keys # ssh-ed25519 user@winpc (1 key)

If the file is group-writable (mode 664), tighten it to 600. sshd accepts 664 only while your account’s group has no other members, so do not rely on it.

4.3 Verify Kali’s SSH server fingerprint

Section titled “4.3 Verify Kali’s SSH server fingerprint”

On Kali (the .pub host key is world-readable, so no sudo is needed):

ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
# 256 SHA256:<your Kali host key fingerprint> root@lab-Kali (ED25519)

When Windows first connects, it shows ED25519 key fingerprint is SHA256:.... Accept only if it matches. If it ever changes unexpectedly, stop and investigate.

4.4 Create the Windows OpenSSH config alias

Section titled “4.4 Create the Windows OpenSSH config alias”

Open the config file:

notepad "$env:USERPROFILE\.ssh\config"

Add this block and keep any other Host entries you already have:

Host kali-lab 192.168.50.20
HostName 192.168.50.20
User labuser
Port 22
IdentityFile ~/.ssh/kali_lab
IdentitiesOnly yes

Why both names are on the Host line:

  • ssh kali-lab works as the short alias.
  • Anything that connects to the raw IP also gets user labuser and key kali_lab. That includes Hermes Desktop’s gateway, Hermes’ SSH backend, and the screen-mirror script.
  • With only the alias on the Host line, Desktop entries that use the IP fall back to the default keys and Kali rejects them.

Rules to remember:

  • Alias matching is case-sensitive. Kali-lab does not match kali-lab (ssh -G labuser@Kali-lab shows hostname kali-lab and the default keys).
  • kali_lab (underscore) is not an alias: ssh kali_lab → Could not resolve hostname kali_lab.
  • The user part is always labuser. A file path is never a user name.

Confirm how OpenSSH resolves both names:

ssh -G kali-lab | Select-String '^(user|hostname|port|identityfile|identitiesonly) '
ssh -G 192.168.50.20 | Select-String '^(user|hostname|identityfile) '

Expected for both: user labuser, hostname 192.168.50.20, port 22, identityfile ~/.ssh/kali_lab, identitiesonly yes.

ssh kali-lab "hostname && whoami && id && pwd"
ssh -o BatchMode=yes kali-lab "hostname && whoami && pwd"

Expected: lab-Kali, labuser, the groups, /home/labuser. The BatchMode=yes test is the important one: it fails instead of prompting, exactly like Hermes. If it prints the three lines, Hermes can use the key.

To see which key was accepted:

ssh -v kali-lab "hostname" 2>&1 | Select-String 'Offering|Server accepts|Authenticated'
# Offering public key: C:\Users\Lab User/.ssh/kali_lab ED25519 SHA256:...
# Server accepts key: ...kali_lab...
# Authenticated to 192.168.50.20 ([192.168.50.20]:22) using "publickey".

Interactive session: ssh kali-lab gives the ┌──(labuser㉿lab-Kali)-[~] prompt; leave with exit. Do not continue to Hermes until both tests pass.

4.6 The Windows ssh-agent (not needed here)

Section titled “4.6 The Windows ssh-agent (not needed here)”
Get-Service ssh-agent | Select-Object Status, StartType # default: Stopped / Disabled

With a passphrase-less key, no agent is needed. Running Set-Service/Start-Service ssh-agent in a normal PowerShell fails with Access is denied and Cannot start service, and ssh-add then reports Error connecting to agent. That is expected, not a fault.

Only if you later switch to a passphrase-protected key, run this in an Administrator PowerShell:

Set-Service -Name ssh-agent -StartupType Automatic
Start-Service ssh-agent

Then, in your normal PowerShell, run ssh-add "$env:USERPROFILE\.ssh\kali_lab". Hermes must run in the same Windows session that holds the agent.

Phase 5: Configure Kali’s graphical desktop for Remote Desktop (XRDP)

Section titled “Phase 5: Configure Kali’s graphical desktop for Remote Desktop (XRDP)”

XRDP gives Windows its own, separate Xfce desktop on the Kali laptop (display :10), using the same labuser account and its Linux password. It does not show what is on the laptop’s screen; for that, see Phase 7.4.

On Kali:

echo "$XDG_CURRENT_DESKTOP" # XFCE (in a local desktop terminal)
dpkg -s kali-desktop-xfce >/dev/null 2>&1 && echo "Kali Xfce is installed" || echo "Kali Xfce is not installed"

Xfce is Kali’s default desktop. Install XRDP, the Xorg backend, and the D-Bus helpers:

sudo apt install -y xrdp xorgxrdp dbus-x11

Only if Kali has no desktop at all (large download):

sudo apt install -y kali-desktop-xfce xorg xrdp xorgxrdp dbus-x11

5.2 Configure the XRDP session for your account

Section titled “5.2 Configure the XRDP session for your account”

Many recipes use a one-line .xsession that contains just startxfce4. With that file, the Remote Desktop window closes immediately after the password whenever you are also logged in on the laptop. Use this file instead (as labuser on Kali):

cat > ~/.xsession <<'EOF'
#!/bin/sh
# XRDP desktop. Unsetting the shared user-bus address makes startxfce4
# launch a private D-Bus, so this session doesn't collide with a local Xfce login.
unset DBUS_SESSION_BUS_ADDRESS
exec startxfce4
EOF
chmod 755 ~/.xsession
ls -l ~/.xsession # -rwxr-xr-x labuser labuser

Why this is needed:

  1. Kali’s systemd gives each user one shared D-Bus. /etc/X11/Xsession.d/20dbus_xdg-runtime points every X session at /run/user/1000/bus (1000 is the first user’s ID).
  2. When labuser is already logged in on the laptop’s screen, that local Xfce owns the session-manager names on the bus.
  3. A second Xfce started by XRDP finds them taken and quits within a second. /var/log/xrdp-sesman.log shows:
    Window manager (pid ..., display 10) finished normally in 1 secs
    Window manager (pid ..., display 10) exited quickly (1 secs). This could indicate a window manager config problem
    and /var/log/xrdp.log shows Xorg server closed connection.
  4. Windows then simply closes the RDP window.

Unsetting DBUS_SESSION_BUS_ADDRESS makes startxfce4 start a private D-Bus for the RDP desktop, so both desktops coexist: RDP connects while labuser stays logged in locally.

The startup chain is /etc/xrdp/startwm.sh → /etc/X11/Xsession → ~/.xsession. Leave startwm.sh unmodified.

5.3 Start XRDP and inspect the configuration

Section titled “5.3 Start XRDP and inspect the configuration”
sudo systemctl enable --now xrdp
systemctl is-enabled xrdp; systemctl is-active xrdp xrdp-sesman # enabled / active active
ss -ltn | grep ':3389' # LISTEN *:3389
id xrdp # groups include ssl-cert

Default settings (stock files, unchanged):

File Setting Value Meaning
/etc/xrdp/xrdp.ini port 3389 Standard RDP port (some Kali recipes use 3390; this one does not)
security_layer / crypt_level negotiate / high TLS negotiated with mstsc
max_bpp 32 Full colour
sessions offered [Xorg], [Xvnc], [vnc-any], [neutrinordp-any] Always pick Xorg
/etc/xrdp/sesman.ini KillDisconnected / DisconnectedTimeLimit false / 0 Closing the RDP window keeps the desktop running; reconnecting resumes it
Policy Default One session per user, resumed on reconnect
AllowRootLogin true Optional hardening: set false (sudo edit + sudo systemctl restart xrdp)

If the xrdp user is in the ssl-cert group, no TLS key-permission fix is needed. If logs ever show a certificate permission error, run sudo adduser xrdp ssl-cert && sudo systemctl restart xrdp.

5.4 Set or verify your account password for RDP

Section titled “5.4 Set or verify your account password for RDP”

XRDP uses the Linux account password, not the SSH key. To change it, run on Kali as labuser:

passwd

A wrong password shows as login failed on the xrdp screen, and as pam_authenticate failed / AUTHFAIL: user=labuser in /var/log/xrdp-sesman.log.

Test the port:

Test-NetConnection 192.168.50.20 -Port 3389 # TcpTestSucceeded : True

Then:

  1. Press Win + R, type mstsc, and press Enter.
  2. Computer: 192.168.50.20 (your Kali address). Click Connect and accept the certificate prompt (xrdp’s self-signed certificate).
  3. On the xrdp login screen, set Session: Xorg. Choosing Xvnc fails with X server failed to start (waitforx: Unable to open display :10) because no VNC server backend is installed for xrdp.
  4. Username: labuser (your Kali account). Password: the Kali account password.

For an older laptop, start at a moderate resolution: Show Options → Display, for example 1280 × 800.

5.6 Do you have to log out of the laptop before Windows connects?

Section titled “5.6 Do you have to log out of the laptop before Windows connects?”

No, not with the .xsession from step 5.2.

  • SSH sessions never require logging out; one account can have many.
  • XRDP opens a separate desktop and works while you are logged in on the laptop.
  • Without the 5.2 file, it does not: the RDP window closes right after the password.

Fallback if an RDP login ever closes immediately again: on the laptop, choose Log Out (locking the screen is not enough), then reconnect.

Ending vs. disconnecting:

  • Closing the RDP window only disconnects. The remote desktop keeps running (KillDisconnected=false), and the next connection resumes it.
  • To end it, use Log Out inside the RDP desktop.

There are two setups. Pick by where the agent should run (section 1.2), or set up both.

Option A: Agent on Windows, commands on Kali (profile kali-lab)

Section titled “Option A: Agent on Windows, commands on Kali (profile kali-lab)”

The agent and its model run on Windows; only terminal commands go to Kali as labuser. It is configured in the Hermes profile kali-lab, used from Desktop’s This device gateway.

A.1 Verify the Windows Hermes installation

Section titled “A.1 Verify the Windows Hermes installation”

Open a fresh PowerShell:

Get-Command hermes
hermes --version # Hermes Agent v0.22.5 · stable
hermes profile list
hermes doctor
ssh -V # OpenSSH_for_Windows_9.5p2

The Microsoft Store (MSIX) Desktop install keeps its data in %LOCALAPPDATA%\hermes; each profile has its own config.yaml and .env:

Profile Config Secrets/env
default %LOCALAPPDATA%\hermes\config.yaml %LOCALAPPDATA%\hermes\.env
kali-lab %LOCALAPPDATA%\hermes\profiles\kali-lab\config.yaml %LOCALAPPDATA%\hermes\profiles\kali-lab\.env

If every CLI command prints ⚠ install out of sync (venv: out of sync with uv.lock), followed by a note that this desktop-app-managed install must rebuild the artifact, it comes from the Store-managed runtime and does not block anything; updating Hermes Desktop clears it. If hermes doctor flags .env file missing for default, create it with hermes setup.

A.2 Create the kali-lab profile and set its terminal backend to SSH

Section titled “A.2 Create the kali-lab profile and set its terminal backend to SSH”
hermes profile create kali-lab --clone-from default --no-alias `
--description "Agent on Windows; terminal commands run on the Kali laptop over SSH"
hermes -p kali-lab config set terminal.backend ssh
hermes config set terminal.backend local # keep 'default' on Windows
  • --clone-from default copies your default profile’s model and settings into the new profile. Skip the first command if the profile already exists.
  • --no-alias skips the wrapper command, so you call the profile with hermes -p kali-lab ....
  • -p kali-lab (or --profile kali-lab) targets that profile’s home. Without it, the command changes default.

A.3 Add the SSH variables to the profile’s .env

Section titled “A.3 Add the SSH variables to the profile’s .env”
notepad "$env:LOCALAPPDATA\hermes\profiles\kali-lab\.env"

Add these lines, keeping any existing ones and without duplicating a variable:

# SSH terminal backend: this profile's commands run on the Kali laptop as 'labuser'
TERMINAL_SSH_HOST=192.168.50.20
TERMINAL_SSH_USER=labuser
TERMINAL_SSH_PORT=22
TERMINAL_SSH_KEY="C:/Users/Lab User/.ssh/kali_lab"

Notes:

  • Quotes are required around the key path when your Windows account folder contains a space, as Lab User does. Use forward slashes and the private key, not .pub.
  • TERMINAL_SSH_HOST and TERMINAL_SSH_USER are required. Port and key are optional, but are set explicitly here.
  • Use the IP, not the alias, in TERMINAL_SSH_HOST. The IP does not depend on alias spelling, and thanks to step 4.4 it resolves to the same user and key anyway.

Verify without exposing other secrets:

hermes -p kali-lab config get terminal.backend # ssh
hermes config get terminal.backend # local
Select-String -Path "$env:LOCALAPPDATA\hermes\profiles\kali-lab\.env" -Pattern '^TERMINAL_SSH_'

How the backend works (per the Hermes configuration docs): Hermes connects with BatchMode=yes and StrictHostKeyChecking=accept-new, captures the remote login environment once, and runs each command as a fresh bash -c in the tracked working directory. During a session it also copies a fixed set of Hermes cache folders (documents, images, audio, screenshots, attachments, and similar) to Kali, and syncs changes back. Other cache folders, such as cache/partials, are never copied.

Restart the Hermes gateway so Desktop reloads the profile:

hermes gateway restart
hermes gateway status

Test from PowerShell (one-shot, uses the profile’s model):

hermes -p kali-lab chat -Q -q "Use your terminal tool to run: hostname && whoami && pwd && grep PRETTY_NAME /etc/os-release. Reply with only the output."

Expected output:

lab-Kali
labuser
/home/labuser
PRETTY_NAME="Kali GNU/Linux Rolling"

Test in Desktop:

  1. In the Sessions sidebar, select the gateway This device.
  2. In the profile rail, select kali-lab.
  3. Ask the same question.

The same lines must come back. With profile default, the answer is the Windows host instead, which is intended.

If the agent reports an SSH error, test from the same Windows account first:

ssh -o BatchMode=yes 192.168.50.20 "hostname && whoami && pwd"

If this works but Hermes does not, check:

  • terminal.backend of the selected profile;
  • that profile’s .env;
  • the quotes around the key path;
  • whether Kali is penalising Windows after failed logins (Phase 3.1).

Option B: Agent on Kali through the registered SSH gateway Kali Lab

Section titled “Option B: Agent on Kali through the registered SSH gateway Kali Lab”

Desktop opens a tunnel to Kali, starts Kali’s Hermes dashboard, and the agent runs there. Its sessions, memory, skills, model configuration, and files belong to the Hermes home of labuser on Kali (/home/labuser/.hermes).

On Kali, as labuser (never with sudo):

curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash

Open a new terminal (or run source ~/.zshrc, because Kali’s default shell is zsh, not bash), then:

command -v hermes # /home/labuser/.local/bin/hermes
hermes --version # Hermes Agent v0.22.5 ... Install directory: /home/labuser/.hermes/hermes-agent Install method: git
hermes doctor

~/.local/bin/hermes is a small wrapper that runs ~/.hermes/hermes-agent/.hermes/bin/hermes.

B.2 Make hermes visible to non-interactive SSH (required)

Section titled “B.2 Make hermes visible to non-interactive SSH (required)”

Desktop runs its probe as a non-interactive SSH command, and for zsh that reads only ~/.zshenv. Kali adds ~/.local/bin to PATH in ~/.zprofile, which only login shells read. Without the fix below, the probe cannot find hermes, and Test fails with “Hermes is not installed on the remote host”. Fix it as labuser:

cat >> ~/.zshenv <<'EOF'
# Make ~/.local/bin (hermes, pipx tools) visible to non-interactive SSH commands too
case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) export PATH="$HOME/.local/bin:$PATH" ;; esac
EOF

Verify from Windows:

/home/labuser/.local/bin/hermes
ssh kali-lab "command -v hermes && hermes --version"
# Hermes Agent v0.22.5 ... · stable

Alternatively, leave PATH alone and enter /home/labuser/.local/bin/hermes in the connection’s Hermes path field (B.4). The ~/.zshenv line also helps every other ssh kali-lab "..." command.

B.3 Give Kali’s Hermes a model (LM Studio on Windows)

Section titled “B.3 Give Kali’s Hermes a model (LM Studio on Windows)”

Point Kali’s Hermes at LM Studio on the Windows laptop (~/.hermes/config.yaml on Kali):

model:
default: liquid/lfm2-1.2b
provider: custom:lmstudio
base_url: http://192.168.50.10:1234/v1

This works only while LM Studio serves on the LAN.

1. LM Studio (Windows):

  1. Open Developer → Local Server and check the port (1234 by default).
  2. Turn on Serve on Local Network.
  3. Load the model.
  4. Start the server, and keep it running while you use Mode B.

If LM Studio offers API authentication, enabling it is recommended; then store the key in Kali’s Hermes provider settings, never in shared files.

2. Windows firewall (Administrator PowerShell, once):

New-NetFirewallRule -DisplayName "LM Studio API - Home Wi-Fi" -Direction Inbound -Action Allow `
-Protocol TCP -LocalPort 1234 -RemoteAddress 192.168.50.0/24 -Profile Private
Get-NetFirewallRule -DisplayName "LM Studio API - Home Wi-Fi" | Format-List DisplayName, Enabled, Profile

When LM Studio first runs, Windows can also create two lm studio.exe allow rules for the Public profile. They are not needed on your Private home Wi-Fi. Disabling them is optional hardening: Get-NetFirewallRule -DisplayName 'lm studio.exe' | Disable-NetFirewallRule (Administrator).

3. Confirm LM Studio is listening on the LAN interface (Windows):

Get-NetTCPConnection -State Listen -LocalPort 1234 | Format-Table LocalAddress, LocalPort

0.0.0.0 or your Wi-Fi address (192.168.50.10) is good. 127.0.0.1 means Serve on Local Network is off.

4. Test from Kali, and use the exact model id:

curl -s http://192.168.50.10:1234/v1/models # lists loaded models; note the "id"
curl -s http://192.168.50.10:1234/v1/chat/completions -H "Content-Type: application/json" \
-d '{"model":"liquid/lfm2-1.2b","messages":[{"role":"user","content":"Reply with exactly: OK"}]}'

curl printing nothing (or HTTP 000) means LM Studio is not running or not serving on the LAN. To change Kali’s model or provider, run ssh -t kali-lab hermes model.

The model name must be an id that /v1/models returns. If Hermes asks for a model that LM Studio has not loaded, LM Studio logs Model with identifier '...' not found. Requests to /api/show in the LM Studio log mean the provider is set to Ollama, not LM Studio/custom. Hermes’ agent workflows also need a model with a large context window and real tool-calling support (check the current Hermes quickstart for the minimum context). Small models may chat fine but fail at tools.

In Hermes Desktop: Settings → Gateways → Registered gateways (also the plug button in the profile rail, or Ctrl+K → “Gateways”). Keep exactly one Kali entry:

Field Value
Kind SSH
Name Kali Lab
SSH host labuser@192.168.50.20:22
Hermes path (optional) blank: auto-detect works after B.2. Otherwise /home/labuser/.local/bin/hermes.
SSH key (if the editor shows one) ~/.ssh/kali_lab

Save connection, then Test and wait for Reachable. Test checks both the HTTP and WebSocket legs, so a pass means chat will work.

Desktop stores the entry in %APPDATA%\Hermes\connections.json. It can store a default key path such as ~/.ssh/id_rsa, even when that file does not exist on your PC. The connection still works, because the Host kali-lab 192.168.50.20 block (step 4.4) adds IdentityFile ~/.ssh/kali_lab. Keep that config block, or set the key field to ~/.ssh/kali_lab.

Remove every broken Kali entry. Typical broken entries are:

  • kali_lab: underscore, not an alias.
  • Kali-lab with user ~/ssh/config: a path in the user field, and a capitalised alias.
  • 192.168.50.20 with user ~/.ssh/kali_lab: the key path in the user field.

Desktop retries every saved entry. Their bad logins trip Kali’s OpenSSH penalty (Phase 3.1), and that breaks even the working connections. Use the trash button on each broken entry.

B.5 Which backend the Desktop window itself boots

Section titled “B.5 Which backend the Desktop window itself boots”

Separately from the registry, the connection-mode control at the top of Settings → Gateways decides which backend Desktop’s window boots into. It is stored in %APPDATA%\Hermes\connection.json.

Connection mode What happens at launch
SSH → labuser@192.168.50.20 Desktop starts Kali’s Hermes over the tunnel. Log: [ssh] connection spawned dashboard: Hermes Agent v0.22.5 ... at /home/labuser/.local/bin/hermes, then [boot] Remote Hermes backend is ready.
Local Desktop starts the Windows runtime; Kali is reached via Mode A or by switching to Kali Lab.

Either way, the Sessions sidebar’s gateway selector lets you switch between This device (Mode A with profile kali-lab) and Kali Lab (Mode B). Make primary sets the fallback gateway; At startup, return to Sessions on the last-used gateway reopens the last one used.

If the connection mode points at a bad host, or at a laptop that is off, Desktop fails at launch with Desktop boot failed: Could not reach <host> over SSH.

B.6 Understand what changes when you switch to Kali Lab

Section titled “B.6 Understand what changes when you switch to Kali Lab”

Once you open the Kali Lab gateway, you are talking to the Hermes backend installed on Kali:

  • Its sessions, memory, skills, provider configuration, files, and command execution belong to the Hermes home of labuser on Kali.
  • Its model is whatever Kali’s Hermes is configured for (B.3), not the Windows profile’s model.
  • It is not the Windows agent using a remote terminal; that is Mode A.

To return to the Windows-hosted agent, switch the gateway back to This device and select profile kali-lab.

Phase 7: Remote Desktop, VMware, and screen mirroring

Section titled “Phase 7: Remote Desktop, VMware, and screen mirroring”

This phase separates four things that are often confused:

  1. RDP to the physical Kali laptop.
  2. What VMware can and cannot do.
  3. A Kali VM inside VMware.
  4. Seeing the physical laptop’s real screen.

7.1 Kali is a separate physical laptop (this tutorial)

Section titled “7.1 Kali is a separate physical laptop (this tutorial)”

Use XRDP (Phase 5) for a separate desktop, or x11vnc (7.4) for the real screen. Quick check from Windows:

  1. Kali is powered on and on the same Wi-Fi.
  2. ping 192.168.50.20 succeeds.
  3. Test-NetConnection 192.168.50.20 -Port 3389 → TcpTestSucceeded : True.
  4. Run mstsc → 192.168.50.20 → session Xorg → labuser + Kali password.

7.2 If you actually mean Kali running as a VM inside VMware Workstation on Windows

Section titled “7.2 If you actually mean Kali running as a VM inside VMware Workstation on Windows”

Use this only for a Kali virtual machine hosted on the Windows laptop. That is a different Kali instance, not the physical laptop.

Method 1: Use the VMware console (most direct)

Section titled “Method 1: Use the VMware console (most direct)”
  1. Open VMware Workstation and select the Kali VM.

  2. Adjust VM → Settings → Display if needed, then VM → Power On.

  3. Sign in on the VM console.

  4. For auto-resize and clipboard, install the guest tools inside the VM (not on the physical laptop):

    sudo apt update && sudo apt install -y open-vm-tools open-vm-tools-desktop && sudo reboot

Method 2: Use Windows Remote Desktop to reach the Kali VM

Section titled “Method 2: Use Windows Remote Desktop to reach the Kali VM”
  1. Power off the VM, then open VM → Settings → Network Adapter.
  2. Choose the network mode:
    • Bridged: the VM gets its own LAN address from the router.
    • NAT (VMnet8) or Host-only (VMnet1): only this Windows host can reach it directly.
  3. Tick Connected and Connect at power on, then start the VM.
  4. Inside the VM: ip -4 -br addr and sudo systemctl status xrdp --no-pager.
  5. On Windows: Test-NetConnection <VM_GUEST_IP> -Port 3389, then mstsc → <VM_GUEST_IP>.

Never give a bridged VM 192.168.50.20; that address belongs to the physical laptop.

Goal Best method Address used by Windows
Separate desktop on the physical Kali laptop XRDP + mstsc 192.168.50.20
The physical laptop’s real screen x11vnc + TigerVNC via SSH (7.4) localhost:5901 (tunnel to Kali)
Console of a Kali VM on this Windows PC VMware Workstation console none
RDP into a Kali VM XRDP inside the VM + mstsc the VM’s own IP

7.4 See and control the physical laptop’s real screen (like a VM console)

Section titled “7.4 See and control the physical laptop’s real screen (like a VM console)”

This is the replacement for “VMware remote connect”. x11vnc shares Kali’s live display :0 (the screen on the laptop):

  • It listens on Kali’s localhost only.
  • It is reached through an SSH tunnel with your key, so nothing new is exposed on the network.
  • It starts on demand and stops when you close the viewer.

One-time setup:

# Kali, as labuser
sudo apt install -y x11vnc
x11vnc -storepasswd # optional. Pressing Enter (empty) stores no password.
# Windows
winget install TigerVNC.TigerVNC # installs C:\Program Files\TigerVNC\vncviewer.exe
New-Item -ItemType Directory -Force 'C:\RemoteAccess' | Out-Null
notepad 'C:\RemoteAccess\Open-KaliScreen.ps1' # paste the launcher from Appendix A.6, then save

With an empty password, ~/.vnc/passwd is not created, and the launcher automatically uses -nopw. That is acceptable because x11vnc binds to Kali’s localhost, and the only way in is the SSH key. If you later set a VNC password (VNC uses only its first 8 characters), the launcher detects ~/.vnc/passwd and requires it.

Use (whenever you are logged in on the laptop’s own screen):

  • Double-click the desktop shortcut Kali Screen (Appendix B), or
  • run powershell -ExecutionPolicy Bypass -File C:\RemoteAccess\Open-KaliScreen.ps1 (add -FullScreen for full screen).

In the viewer, F8 opens TigerVNC’s menu (full screen on/off, Ctrl-Alt-Del, disconnect).

Fit-to-screen (why a simple launcher shows only the top-left of Kali, zoomed):

The numbers below come from our example laptops: a 4K Windows laptop at 200 % scaling, and a Kali desktop of 2560×1440.

Fact Example value
Kali desktop sent by x11vnc 2560×1440
Windows display 3840×2400 at 200 % scaling
TigerVNC DPI awareness unaware: Windows enlarges its whole window 2×
Space TigerVNC sees (work area) 1920×1152 units

TigerVNC has no client-side scaling option (its -RemoteResize would change the laptop’s real resolution, so it isn’t used). Shown at 1:1 in 1920×1152 units and then enlarged 2×, Kali’s 2560×1440 screen overflows, leaving only the top-left part visible.

The launcher therefore measures the usable area in TigerVNC’s own (DPI-unaware) units and passes x11vnc -scale <fraction>, keeping the aspect ratio:

  • Windowed: min((1920−16)/2560, (1152−40)/1440) = 0.7438, so Kali arrives as 1904×1071 and the whole screen fits.
  • -FullScreen: min(1920/2560, 1200/1440) = 0.75, so 1920×1080.

x11vnc maps mouse and keyboard to the scaled image automatically. The image is resampled twice (shrunk by x11vnc, then enlarged by Windows), so it is slightly soft, but it shows UI elements at about the same size as on the Kali laptop itself.

What the launcher does (full script in Appendix A.6):

  1. One SSH call reads whether ~/.vnc/passwd exists and Kali’s screen size (xdpyinfo -display :0). If Kali is unreachable, or nobody is logged in on the laptop, it stops with a clear message.
  2. It computes the fit scale as above.
  3. It runs ssh -L 5901:localhost:5900 kali-lab "x11vnc -display :0 -auth guess -localhost -rfbport 5900 <-nopw | -rfbauth ~/.vnc/passwd> -scale <fraction> -once -timeout 60 -noxdamage".
  4. It waits for x11vnc to print PORT=5900. It deliberately does not probe the port, because a probe would use up the single -once client.
  5. It opens TigerVNC on localhost::5901 (plus -FullScreen if requested) and waits until you close it.
  6. It kills the local ssh, then stops x11vnc on Kali with pkill -u labuser -f '[x]11vnc -display :0 ...'. Killing the local ssh alone leaves x11vnc running, because there is no terminal to deliver a hang-up; the [x] keeps pkill from matching its own command line.
  7. Any failure appears in a Windows message box titled Kali Screen, because the shortcut runs without a console.

How to check that it works:

  • The viewer shows the laptop’s entire current screen, with nothing cut off at the right or bottom.
  • After you close the viewer, ssh kali-lab "pgrep -a x11vnc || echo none" prints none.

Limits:

  • If nobody is logged in on the laptop (LightDM greeter), there is no desktop to share. Use XRDP instead.
  • If the laptop’s screen is locked, you see the lock screen; unlock it with your Kali password.

Phase 8: Restrict access and preserve a safe recovery path

Section titled “Phase 8: Restrict access and preserve a safe recovery path”

8.1 Keep SSH, RDP, VNC, LM Studio, and Hermes off the public internet

Section titled “8.1 Keep SSH, RDP, VNC, LM Studio, and Hermes off the public internet”

Do not forward TCP 22, 3389, 5900, or the LM Studio port from the router. Keep them on the trusted home LAN (Windows Wi-Fi profile Private), and use a properly secured VPN for access from outside. Do not expose any Hermes web interface, gateway, or dashboard directly to the internet.

Hermes inherits all of your account’s access, so keep these boundaries:

  • Do not add labuser to new groups just to make one command work.
  • Never configure NOPASSWD: ALL for convenience.
  • Never put the Kali sudo password in prompts, skills, scripts, or .env files.
  • Review commands before execution, especially destructive ones, package installs, network changes, and scans.
  • Limit security testing to systems and targets you own or are explicitly authorised to test.
  • Never share the private key %USERPROFILE%\.ssh\kali_lab or put it in a repository; only kali_lab.pub belongs on Kali.

  • Keep private:

    • %LOCALAPPDATA%\hermes\.env
    • %LOCALAPPDATA%\hermes\profiles\kali-lab\.env
    • Kali’s ~/.hermes/.env and auth.json
    • %APPDATA%\Hermes\connection.json and connections.json, which hold adopted gateway tokens

    When you share troubleshooting output, show only TERMINAL_SSH_* lines or field names, never the whole file.

  • Stop if Kali’s host key fingerprint changes unexpectedly (4.3).

  • Mode A copies some Hermes cache folders to Kali during sessions (A.3). Treat Kali as part of your trusted environment.

Run these in order before any security work.

ipconfig # Wi-Fi 192.168.50.10
Test-NetConnection 192.168.50.20 -Port 22 # True
Test-NetConnection 192.168.50.20 -Port 3389 # True

On Kali: ip -4 -br addr → wlan0 ... 192.168.50.20/24.

ssh -o BatchMode=yes kali-lab "hostname && whoami && id && pwd && grep PRETTY_NAME /etc/os-release"

Expected: lab-Kali, labuser, groups including sudo, /home/labuser, Kali GNU/Linux Rolling.

Mode A:

hermes -p kali-lab config get terminal.backend # ssh
hermes -p kali-lab chat -Q -q "Use your terminal tool to run: hostname && whoami && pwd. Reply with only the output."

In Desktop, gateway This device, profile kali-lab: ask the same question. Expect lab-Kali, labuser, /home/labuser.

Mode B: start the LM Studio server first. In Desktop, open Kali Lab (Test → Reachable) and ask the agent to run hostname, whoami, pwd, cat /etc/os-release.

Never infer the active mode from the look of the chat window; check the command output.

9.4 Create a test results directory on Kali

Section titled “9.4 Create a test results directory on Kali”
mkdir -p ~/pentest-results
printf 'Kali remote execution is working\n' > ~/pentest-results/connection-test.txt

From Windows:

ssh kali-lab "cat ~/pentest-results/connection-test.txt"
scp kali-lab:pentest-results/report.txt "$env:USERPROFILE\Downloads\" # copy a real report back

9.5 Use tmux for long-running terminal work

Section titled “9.5 Use tmux for long-running terminal work”

Install tmux if it is missing (sudo apt install -y tmux). As labuser:

tmux new -s lab # start
# detach: Ctrl+B, then D
tmux attach -t lab # reattach later

A tmux session survives SSH disconnects; GUI programs and ordinary background processes may not.

  1. Stay logged in on the laptop. Run mstsc → 192.168.50.20 → Xorg → labuser. The Xfce desktop must open and stay open.
  2. Double-click Kali Screen on the desktop (or run C:\RemoteAccess\Open-KaliScreen.ps1). TigerVNC must show the laptop’s entire current screen, with nothing cut off at the right or bottom. Close it; afterwards ssh kali-lab "pgrep -a x11vnc || echo none" should print none.
Symptom / exact message Cause Fix
Test-NetConnection ... -Port 22 → TcpTestSucceeded : False (ping OK); ssh: connect to host 192.168.50.20 port 22: Connection refused sshd not installed or not running Phase 3.1
ssh: Could not resolve hostname kali-lab before the config exists No Host kali-lab block yet Phase 4.4
ssh: Could not resolve hostname kali_lab: No such host is known. Underscore: kali_lab is the key file, not an alias Use kali-lab
ssh -G kali_lab shows your Windows user name, hostname kali_lab, and the default identity files Checked a name that matches no Host block ssh -G kali-lab
Could not resolve hostname kali-lab for Kali-lab Alias matching is case-sensitive Lower-case kali-lab, or use the IP
...kali_lab already exists. Overwrite (y/n)? Key already present Answer n. If overwritten, reinstall the new .pub on Kali (4.2).
Set-Service ... Access is denied, Start-Service ... Cannot start service ssh-agent, ssh-add: Error connecting to agent Agent service is Disabled and needs Administrator; not needed for a passphrase-less key Ignore (4.6)
hermes doctor: ~/AppData/Local/hermes/.env file missing default profile has no .env Create it (hermes setup), or add the needed lines
CLI prefix ⚠ install out of sync (venv: out of sync with uv.lock) Store-managed Desktop runtime Harmless; update Hermes Desktop
Desktop log [ssh] connect to kali_lab:22 failed ... Could not resolve hostname kali_lab, then Desktop boot failed: Could not reach kali_lab over SSH Gateway / connection mode used kali_lab SSH host labuser@192.168.50.20:22 (B.4, B.5)
Desktop log connect to ~/.ssh/kali_lab@192.168.50.20:22 failed (kind=auth-failed) ... Permission denied (publickey,password) Key path typed into the user part User labuser; key in the key field
Desktop log connect to ~/ssh/config@Kali-lab:22 failed ... Could not resolve hostname kali-lab A path in the user field, and capital K Delete the entry
Desktop log Connection closed by 192.168.50.20 port 22; Kali journal srclimit_penalise ... attempted authentication by invalid user / drop connection ... penalty OpenSSH 10 penalty after bad logins (for example, user ~ from a path in the user field) Delete broken entries, wait about 1 min. Check: ssh kali-lab "journalctl -u ssh --since '-10 min' | grep -i penalt"
Gateway Test: “Hermes is not installed on the remote host” although it is installed zsh non-interactive SSH reads only ~/.zshenv; ~/.local/bin not on PATH B.2, or Hermes path /home/labuser/.local/bin/hermes
Desktop log Using bundled payload ..., readiness probe ... exceeded its budget (4.0s) Informational only None
Kali agent: Model with identifier '...' not found; LM Studio: unexpected /api/show Model id not loaded; Ollama-style provider Use an id from /v1/models; provider LM Studio/custom (B.3)
curl http://192.168.50.10:1234/v1/models from Kali prints nothing / HTTP 000 LM Studio server stopped, or not on the LAN interface Start the server with Serve on Local Network (B.3)
LM Studio shows “Reachable at 192.168.56.1:1234” That is the VirtualBox Host-Only adapter Use your Wi-Fi address, 192.168.50.10:1234 in this tutorial
Agent “ran commands” but output is from Windows Wrong profile or gateway selected Gateway This device + profile kali-lab (Mode A), or Kali Lab (Mode B)
RDP: password accepted, then the window closes immediately. sesman: Window manager ... exited quickly (1 secs); xrdp: Xorg server closed connection; ~/.xsession-errors: X server already running on display :10.0 Same-user local Xfce owns the shared D-Bus; the RDP Xfce quits .xsession from 5.2, or log out on the laptop
RDP: login failed; sesman pam_authenticate failed: Authentication failure, AUTHFAIL: user=labuser Wrong Kali password Use your Linux account password (5.4)
RDP: X server failed to start, waitforx: Unable to open display :10, log shows Starting X server ... Xvnc :10 Session type Xvnc selected Select Xorg
Mirror shows only the top-left ~⅓ of Kali, zoomed in TigerVNC is DPI-unaware, so Windows (200 %) enlarges it 2×; Kali’s screen doesn’t fit, and TigerVNC can’t scale Use the launcher in Appendix A.6, which passes x11vnc a -scale fit (7.4)
Message box “Kali’s screen (:0) is not available” Nobody is logged in on the laptop (greeter shown), so there is no :0 desktop for your account Log in on the laptop, or use RDP (Phase 5)
Message box “Cannot reach Kali over SSH (kali-lab)” Kali off, asleep, off the Wi-Fi, or SSH penalty Test-NetConnection 192.168.50.20 -Port 22; Phase 3.1
Message box “x11vnc/SSH stopped early” x11vnc not installed, or a stale x11vnc on port 5900 sudo apt install -y x11vnc; ssh kali-lab "pkill -u labuser -f '[x]11vnc'"
Shortcut shows a generic icon Icon file moved or deleted Keep C:\RemoteAccess\Open-KaliScreen.ico; recreate the shortcut (Appendix B)
ssh kali-lab "pkill -f 'x11vnc ...'" exits with code 255 pkill -f matched and killed its own SSH shell Use the [x]11vnc pattern
Symptom What to check
Kali no longer has its static address after reboot nmcli -g ipv4.method,ipv4.addresses connection show HomeWiFi; another active profile; IP conflict
SSH times out Same Wi-Fi? Router client isolation? Kali awake? Current Kali IP?
Permission denied (publickey) User labuser; key kali_lab; the line in ~/.ssh/authorized_keys; ~/.ssh 700 and the file 600
ssh kali-lab works but the Hermes backend fails Selected profile’s terminal.backend and .env; quoted key path; SSH penalty
RDP refused systemctl is-active xrdp; ss -ltn | grep 3389; firewall
RDP black screen Xfce installed; ~/.xsession content and mode 755; ~/.xsession-errors
VMware VM unreachable via RDP VM powered on; adapter mode; IP taken from inside the VM; xrdp active in the VM
Machine Log Command (no sudo needed for a member of adm)
Kali SSH journalctl -u ssh -n 100 --no-pager
Kali XRDP session manager tail -n 50 /var/log/xrdp-sesman.log
Kali XRDP tail -n 50 /var/log/xrdp.log
Kali RDP desktop startup tail -n 50 ~/.xsession-errors, ~/.xorgxrdp.10.log
Kali Hermes on Kali ~/.hermes/logs/ (agent.log, gateway.log, errors.log, gui.log)
Kali Listening ports ss -ltn | grep -E ':(22|3389|5900)\b'
Windows Hermes Desktop %LOCALAPPDATA%\hermes\logs\desktop.log; search [ssh] and [boot]
Windows Hermes agent/gateway %LOCALAPPDATA%\hermes\logs\agent.log, gateway.log, errors.log

Read just the Desktop SSH lines:

Select-String -Path "$env:LOCALAPPDATA\hermes\logs\desktop.log" -Pattern '\[ssh\]|\[boot\]' | Select-Object -Last 30 | ForEach-Object Line

A.1 Windows %USERPROFILE%\.ssh\config (Kali block)

Section titled “A.1 Windows %USERPROFILE%\.ssh\config (Kali block)”
Host kali-lab 192.168.50.20
HostName 192.168.50.20
User labuser
Port 22
IdentityFile ~/.ssh/kali_lab
IdentitiesOnly yes
Profile terminal.backend TERMINAL_SSH_* in its .env
default local none needed
kali-lab ssh HOST=192.168.50.20, USER=labuser, PORT=22, KEY="C:/Users/Lab User/.ssh/kali_lab"
File Content (non-secret fields)
connections.json the gateway registry: local (This device) and kali-lab → label Kali Lab, host 192.168.50.20, user labuser
connection.json the window backend: mode=local, or mode=ssh with host 192.168.50.20 and user labuser
#!/bin/sh
# XRDP desktop. Unsetting the shared user-bus address makes startxfce4
# launch a private D-Bus, so this session doesn't collide with a local Xfce login.
unset DBUS_SESSION_BUS_ADDRESS
exec startxfce4
# Make ~/.local/bin (hermes, pipx tools) visible to non-interactive SSH commands too
case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) export PATH="$HOME/.local/bin:$PATH" ;; esac

A.6 Windows C:\RemoteAccess\Open-KaliScreen.ps1 (the screen launcher, with fit-to-screen)

Section titled “A.6 Windows C:\RemoteAccess\Open-KaliScreen.ps1 (the screen launcher, with fit-to-screen)”
<#
Open-KaliScreen.ps1 - view and control the Kali laptop's REAL screen (display :0)
from Windows, like a VM console.
How it works: SSH (your kali-lab key) starts x11vnc on Kali bound to Kali's
localhost only, forwards it through the SSH tunnel to this PC, and opens
TigerVNC. Nothing is exposed on the network. x11vnc exits when you close
the viewer (or if no viewer connects within 60 s).
Fit to screen: TigerVNC has no client-side scaling and is not DPI-aware, so
Windows enlarges it by the display scale (for example 200%). The script measures
the usable screen area in those same units and asks x11vnc to shrink Kali's
picture to fit before sending it, so the whole Kali screen is always visible.
One-time prerequisites:
Kali : sudo apt install -y x11vnc
x11vnc -storepasswd (optional; empty = no VNC password)
Windows: winget install TigerVNC.TigerVNC
Requirement at use time: your Kali account must be logged in on Kali's own screen.
Usage: powershell -ExecutionPolicy Bypass -File C:\RemoteAccess\Open-KaliScreen.ps1 [-FullScreen]
(or the "Kali Screen" desktop shortcut). In the viewer, F8 opens the menu.
#>
param(
[string]$SshHost = 'kali-lab',
[string]$KaliUser = 'labuser',
[int] $LocalPort = 5901,
[int] $TimeoutSec = 30,
[switch]$FullScreen
)
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Windows.Forms
Add-Type -Namespace KaliScreen -Name Dpi -MemberDefinition @'
[DllImport("user32.dll")] public static extern IntPtr SetThreadDpiAwarenessContext(IntPtr context);
'@
try {
$viewer = @("$env:ProgramFiles\TigerVNC\vncviewer.exe",
(Get-Command vncviewer.exe -ErrorAction SilentlyContinue).Source) |
Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1
if (-not $viewer) { throw 'TigerVNC viewer not found. Install it: winget install TigerVNC.TigerVNC' }
# One SSH round-trip: is a VNC password set, and how large is Kali's screen (:0)?
$info = ssh.exe -o BatchMode=yes -o ConnectTimeout=8 $SshHost 'test -f ~/.vnc/passwd && echo pw; xdpyinfo -display :0 2>/dev/null | awk ''/dimensions/{print $2}'''
if ($LASTEXITCODE -eq 255) { throw "Cannot reach Kali over SSH ($SshHost). Is the Kali laptop on and on the Wi-Fi?" }
$dims = $info | Where-Object { $_ -match '^\d+x\d+$' } | Select-Object -First 1
if (-not $dims) { throw "Kali's screen (:0) is not available. Log in as '$KaliUser' on the Kali laptop itself first, or use Remote Desktop (mstsc) instead." }
$remoteW, $remoteH = [int[]]($dims -split 'x')
# Usable area in the same DPI-unaware units TigerVNC sees (for example 1920x1152 at 200% on a 3840x2400 display).
$prev = [KaliScreen.Dpi]::SetThreadDpiAwarenessContext([IntPtr](-1)) # DPI_AWARENESS_CONTEXT_UNAWARE
$screen = [System.Windows.Forms.Screen]::PrimaryScreen
$area = if ($FullScreen) { $screen.Bounds } else { $screen.WorkingArea }
[void][KaliScreen.Dpi]::SetThreadDpiAwarenessContext($prev)
$availW = $area.Width - $(if ($FullScreen) { 0 } else { 16 }) # window borders
$availH = $area.Height - $(if ($FullScreen) { 0 } else { 40 }) # title bar + borders
$scale = [Math]::Min(1.0, [Math]::Min($availW / $remoteW, $availH / $remoteH))
$scaleArg = if ($scale -lt 1) { '-scale ' + $scale.ToString('0.0000', [Globalization.CultureInfo]::InvariantCulture) } else { '' }
$log = Join-Path $env:TEMP "kali-x11vnc-$PID.out"
$errLog = "$log.err"
# Use the VNC password if one was set with 'x11vnc -storepasswd'; otherwise run without one.
# Either way x11vnc only listens on Kali's localhost, so the SSH key is the real gate.
$vncAuth = if ($info -contains 'pw') { '-rfbauth ~/.vnc/passwd' } else { '-nopw' }
$remote = "x11vnc -display :0 -auth guess -localhost -rfbport 5900 $vncAuth $scaleArg -once -timeout 60 -noxdamage"
$sshArgs = "-o BatchMode=yes -o ExitOnForwardFailure=yes -L ${LocalPort}:localhost:5900 $SshHost `"$remote`""
$ssh = Start-Process ssh.exe -ArgumentList $sshArgs -WindowStyle Hidden -PassThru `
-RedirectStandardOutput $log -RedirectStandardError $errLog
try {
# x11vnc prints "PORT=5900" once it is listening; wait for that instead of a fixed sleep,
# and don't probe the port (a probe would count as the single -once client).
$deadline = (Get-Date).AddSeconds($TimeoutSec)
while (-not (Select-String -Path $log -Pattern '^PORT=' -Quiet -ErrorAction SilentlyContinue)) {
if ($ssh.HasExited) { throw "x11vnc/SSH stopped early.`n$(Get-Content $errLog -Raw)" }
if ((Get-Date) -gt $deadline) { throw "x11vnc did not start within $TimeoutSec s.`n$(Get-Content $errLog -Raw)" }
Start-Sleep -Milliseconds 500
}
$viewerArgs = @("localhost::$LocalPort")
if ($FullScreen) { $viewerArgs = @('-FullScreen') + $viewerArgs }
Start-Process $viewer -ArgumentList $viewerArgs -Wait
}
finally {
if (-not $ssh.HasExited) { $ssh.Kill() }
# Killing the local ssh does not stop x11vnc on Kali (no tty, so no hangup signal); stop it explicitly.
# '[x]11vnc' matches x11vnc but not this pkill's own shell command line.
ssh.exe -o BatchMode=yes $SshHost "pkill -u $KaliUser -f '[x]11vnc -display :0 -auth guess -localhost -rfbport 5900'" 2>$null
Remove-Item $log, $errLog -ErrorAction SilentlyContinue
}
}
catch {
# The desktop shortcut runs this without a console, so show failures in a message box.
[void][System.Windows.Forms.MessageBox]::Show($_.Exception.Message, 'Kali Screen', 'OK', 'Error')
exit 1
}

Appendix B: Desktop shortcut for the screen mirror (with a Kali icon)

Section titled “Appendix B: Desktop shortcut for the screen mirror (with a Kali icon)”

A one-click Kali Screen icon on the Windows desktop that runs C:\RemoteAccess\Open-KaliScreen.ps1. Launching it opens the fitted viewer, and closing the viewer stops x11vnc on Kali.

Item Value
Shortcut file Kali Screen.lnk in your Desktop folder
Target C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Arguments -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\RemoteAccess\Open-KaliScreen.ps1"
Start in C:\RemoteAccess
Run Minimized
Icon C:\RemoteAccess\Open-KaliScreen.ico

Any .ico file works for the icon. Download a Kali icon from a source whose license lets you use it, and save it as C:\RemoteAccess\Open-KaliScreen.ico.

B.1 Create it with PowerShell (normal PowerShell, not Administrator)

Section titled “B.1 Create it with PowerShell (normal PowerShell, not Administrator)”
# 1. Keep the icon next to the script, so cleaning Downloads never breaks the shortcut
Copy-Item "$env:USERPROFILE\Downloads\kali.ico" 'C:\RemoteAccess\Open-KaliScreen.ico'
# 2. Create the desktop shortcut
$lnkPath = Join-Path ([Environment]::GetFolderPath('Desktop')) 'Kali Screen.lnk'
$sh = New-Object -ComObject WScript.Shell
$lnk = $sh.CreateShortcut($lnkPath)
$lnk.TargetPath = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$lnk.Arguments = '-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\RemoteAccess\Open-KaliScreen.ps1"'
$lnk.WorkingDirectory = 'C:\RemoteAccess'
$lnk.IconLocation = 'C:\RemoteAccess\Open-KaliScreen.ico,0'
$lnk.WindowStyle = 7 # 7 = minimized
$lnk.Description = "View and control the Kali laptop's real screen (x11vnc over SSH)"
$lnk.Save()
# 3. Check it
$c = $sh.CreateShortcut($lnkPath); $c.TargetPath; $c.Arguments; $c.IconLocation

Optional full-screen shortcut: repeat step 2 with the name Kali Screen (Full).lnk and the arguments ending in -File "C:\RemoteAccess\Open-KaliScreen.ps1" -FullScreen. In the viewer, F8 → Full screen toggles it at any time.

  1. Right-click the desktop → New → Shortcut.
  2. Type the location of the item: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\RemoteAccess\Open-KaliScreen.ps1"
  3. Click Next, name it Kali Screen, then click Finish.
  4. Right-click the new shortcut → Properties → Shortcut tab:
    • Start in: C:\RemoteAccess
    • Run: Minimized
    • Change Icon… → Browse… → C:\RemoteAccess\Open-KaliScreen.ico → OK → OK.
  5. Optional: right-click → Pin to Start or Pin to taskbar.
  • No visible console. -WindowStyle Hidden plus Run: Minimized means only the TigerVNC window appears. If something fails (Kali off, nobody logged in on the laptop, x11vnc missing), the script shows a Kali Screen message box with the reason instead (troubleshooting table, Phase 10.1).
  • Execution policy. -ExecutionPolicy Bypass applies only to this launch; the system’s PowerShell policy is unchanged.
  • Moving files. If you move Open-KaliScreen.ps1 or the .ico, update the shortcut’s target/icon, or recreate it with B.1.
  • Stale icon. If Windows keeps showing an old or blank icon after you change the .ico, run ie4uinit.exe -show to refresh the icon cache.

The setup is complete when four things work:

  1. ssh kali-lab works without prompts.
  2. The selected Hermes mode returns lab-Kali / labuser from Kali.
  3. RDP opens a desktop.
  4. The mirror shows the laptop’s real screen.

Only after these should you add penetration-testing tasks.