From f9f6ddc670aee5f20554dbfe8a683e9301859be9 Mon Sep 17 00:00:00 2001 From: Scott Scoble Date: Thu, 4 Jun 2026 11:44:32 -0700 Subject: [PATCH] WGU prep: Windows VM setup, trackpad/trackpoint tweaks, aliases - Add WGU Windows VM helper script + virtualization module (KVM/libvirt) - Fix create-wgu-vn: mkdir before use, CPU topology, detect OS variant - Add Webex, ZapZap, qalculate-gtk, libqalculate, tigervnc packages - Add n97-mini VNC alias, switch nr to local flake path - Tune trackpad (0.44) and TrackPoint (160/120) sensitivity - Reduce mouse sensitivity to -0.2 - Move zapzap from desktop.nix alphabetically - Remove OVMFFull from virtualisation (handled by swtpm/OVMF default) - Bump flake inputs (home-manager, nixos-hardware, nixpkgs, pi-mono) --- flake.lock | 44 ++++++---- flake.nix | 1 + home/desktop.nix | 4 +- home/hyprland.conf | 2 +- home/scripts/create-wgu-vm | 167 +++++++++++++++++++++++++++++++++++++ home/sscoble.nix | 13 ++- home/wgu.nix | 11 +++ modules/t490.nix | 4 +- modules/virtualization.nix | 38 +++++++++ 9 files changed, 263 insertions(+), 21 deletions(-) create mode 100644 home/scripts/create-wgu-vm create mode 100644 modules/virtualization.nix diff --git a/flake.lock b/flake.lock index d074b52..f7f59b2 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1778954430, - "narHash": "sha256-oaNyOr05lblaQdtbkbN1wO0b2KLIL2O1LkmwDgdQp4I=", + "lastModified": 1779969295, + "narHash": "sha256-HwIJ3tOcwSMiV75L7KqJXciXR9UfT+d7rwOZMX7cTnA=", "owner": "nix-community", "repo": "home-manager", - "rev": "26aaab785b0bab4af60a2c42b22760fa906ef22a", + "rev": "61e2c9659324181e0f0ed911958c536333b1d4f6", "type": "github" }, "original": { @@ -21,12 +21,15 @@ } }, "nixos-hardware": { + "inputs": { + "nixpkgs": "nixpkgs" + }, "locked": { - "lastModified": 1778945272, - "narHash": "sha256-Aipz0UiBhE2a1FYJrNc2y+5vKWo5QVkhmaIJk3/ls+g=", + "lastModified": 1780065812, + "narHash": "sha256-SCSLUKBmwlSLGQ8Xbr8PjRFtiHNk0l9ktqkcmqdBkfE=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "379c1f274f0fa354d012f0600806de54e79f29b5", + "rev": "b76b5639c0593e0aeb0b5879ad62d4b30596c144", "type": "github" }, "original": { @@ -38,11 +41,24 @@ }, "nixpkgs": { "locked": { - "lastModified": 1778869304, - "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", + "lastModified": 1767892417, + "narHash": "sha256-8bW3q88CEg2u4hSP66Vf4lpbLonHz7hqDNBMcCY7E9U=", + "rev": "3497aa5c9457a9d88d71fa93a4a8368816fbeeba", + "type": "tarball", + "url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre924538.3497aa5c9457/nixexprs.tar.xz" + }, + "original": { + "type": "tarball", + "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1779560665, + "narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d233902339c02a9c334e7e593de68855ad26c4cb", + "rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786", "type": "github" }, "original": { @@ -55,11 +71,11 @@ "pi-mono": { "flake": false, "locked": { - "lastModified": 1778886387, - "narHash": "sha256-uEMGoJ8kNmqbXRyVgY9M42XxMLw6js2PhVlgF0JMD40=", + "lastModified": 1780010803, + "narHash": "sha256-x12xBzirmsSdHjqEXe2CQJE1OVimPMrRUyxBUsSYz3o=", "ref": "refs/heads/main", - "rev": "b4ee3aaeea183b793d320d71eb91ee80c6a1c583", - "revCount": 4102, + "rev": "7be8a10d2358fe60f1cf4507140aa9cfa81682ee", + "revCount": 4340, "type": "git", "url": "https://os.scoble.me/forgejo/sscoble/pi-mono.git" }, @@ -72,7 +88,7 @@ "inputs": { "home-manager": "home-manager", "nixos-hardware": "nixos-hardware", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", "pi-mono": "pi-mono" } } diff --git a/flake.nix b/flake.nix index 976f3fc..ad50a75 100644 --- a/flake.nix +++ b/flake.nix @@ -31,6 +31,7 @@ home-manager.nixosModules.home-manager ./modules/laptop.nix ./modules/hyprland.nix + ./modules/virtualization.nix ./modules/t490.nix nixos-hardware.nixosModules.lenovo-thinkpad-t490 { nixpkgs.config.allowUnfree = true; } diff --git a/home/desktop.nix b/home/desktop.nix index f2e5857..5173564 100644 --- a/home/desktop.nix +++ b/home/desktop.nix @@ -23,6 +23,9 @@ fuzzel # Screen recording / streaming obs-studio + # Communication + webex + zapzap # Lock + idle hyprlock hypridle @@ -57,7 +60,6 @@ beeper discord slack - zapzap nmap tcpdump speedtest-cli diff --git a/home/hyprland.conf b/home/hyprland.conf index 365c562..80b980d 100644 --- a/home/hyprland.conf +++ b/home/hyprland.conf @@ -25,7 +25,7 @@ input { kb_layout = us kb_options = caps:escape follow_mouse = 1 - sensitivity = 0 + sensitivity = -0.2 natural_scroll = true } diff --git a/home/scripts/create-wgu-vm b/home/scripts/create-wgu-vm new file mode 100644 index 0000000..cdb7386 --- /dev/null +++ b/home/scripts/create-wgu-vm @@ -0,0 +1,167 @@ +#!/usr/bin/env bash +# Create a Windows VM for WGU Guardian browser (proctored testing) +# Usage: create-wgu-vm [windows-iso-path] +# +# If no ISO path given, scans ~/Downloads for Windows ISOs automatically. +# If none found, downloads Windows 10 22H2 from Microsoft. +# Always downloads VirtIO drivers. +# +# After creation, manage the VM: virt-manager +# Connect to the console: virt-viewer wgu-win + +set -euo pipefail + +VM_NAME="wgu-win" +RAM_MB=4096 +CPUS=2 +DISK_GB=64 +VM_DIR="${HOME}/vms/${VM_NAME}" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +info() { echo -e "${GREEN}==>${NC} $*"; } +warn() { echo -e "${YELLOW}==>${NC} $*"; } +err() { echo -e "${RED}==>${NC} $*" >&2; } + +# --- Prerequisites --- +if ! command -v virsh &>/dev/null; then + err "libvirt not found. Run: nr" + err "Then log out and back in (for libvirtd group)." + exit 1 +fi + +if ! groups | grep -q libvirtd; then + warn "You're not in the libvirtd group. Run: nr" + warn "Then log out and back in." +fi + +# --- Check if VM already exists --- +if virsh dominfo "$VM_NAME" &>/dev/null; then + info "VM '$VM_NAME' already exists." + echo "" + echo " Start it: virsh start $VM_NAME" + echo " Console: virt-viewer $VM_NAME" + echo " Manage: virt-manager" + exit 0 +fi + +# --- Find or download Windows ISO --- +WINDOWS_ISO="${1:-}" + +# If no path given, scan ~/Downloads for Windows ISOs +if [ -z "$WINDOWS_ISO" ]; then + FOUND_ISOS=() + while IFS= read -r -d '' f; do + FOUND_ISOS+=("$f") + done < <(find "${HOME}/Downloads" -maxdepth 1 -iname 'Win*.iso' -print0 2>/dev/null) + + if [ "${#FOUND_ISOS[@]}" -gt 0 ]; then + if [ "${#FOUND_ISOS[@]}" -eq 1 ]; then + WINDOWS_ISO="${FOUND_ISOS[0]}" + info "Using existing ISO: $(basename "$WINDOWS_ISO")" + else + echo "" + echo "Multiple Windows ISOs found in ~/Downloads:" + for i in "${!FOUND_ISOS[@]}"; do + echo " [$i] $(basename "${FOUND_ISOS[$i]}")" + done + read -rp "Choose one [0]: " CHOICE + CHOICE="${CHOICE:-0}" + WINDOWS_ISO="${FOUND_ISOS[$CHOICE]}" + fi + fi +fi + +# Still nothing? try VM_DIR cache, then download Win10 +if [ -z "$WINDOWS_ISO" ]; then + mkdir -p "$VM_DIR" + WINDOWS_ISO="${VM_DIR}/Win10_22H2_English_x64.iso" + if [ ! -f "$WINDOWS_ISO" ]; then + info "No local ISO found. Downloading Windows 10 22H2 from Microsoft..." + info "URL: https://go.microsoft.com/fwlink/?LinkID=2151180" + warn "This is ≈6 GB — go make some coffee." + curl -L -o "$WINDOWS_ISO" \ + "https://go.microsoft.com/fwlink/?LinkID=2151180&clcid=0x409&culture=en-us&country=us" \ + --progress-bar + fi +fi + +if [ ! -f "$WINDOWS_ISO" ]; then + err "Windows ISO not found at: $WINDOWS_ISO" + exit 1 +fi + +# --- Detect Windows version for os-variant --- +IS_NAME=$(basename "$WINDOWS_ISO") +case "$IS_NAME" in + *1[0-9]*|*Win10*|*win10*|*WINDOWS_10*) + OS_VARIANT="win10" ;; + *1[12]*|*Win11*|*win11*|*WINDOWS_11*) + OS_VARIANT="win11" ;; + *) + OS_VARIANT="win10" ;; +esac +info "Detected OS variant: $OS_VARIANT" + +# --- Ensure VM directory exists --- +mkdir -p "$VM_DIR" + +# --- VirtIO drivers ISO --- +VIRTIO_ISO="${VM_DIR}/virtio-win.iso" +if [ ! -f "$VIRTIO_ISO" ]; then + info "Downloading VirtIO drivers..." + curl -L -o "$VIRTIO_ISO" \ + "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" \ + --progress-bar +fi + +# --- Disk image --- +DISK_IMG="${VM_DIR}/disk.qcow2" +if [ ! -f "$DISK_IMG" ]; then + info "Creating ${DISK_GB}G qcow2 disk image..." + qemu-img create -f qcow2 "$DISK_IMG" "${DISK_GB}G" +fi + +# --- Create VM --- +info "Creating VM '$VM_NAME' (${OS_VARIANT})..." +sudo virt-install \ + --name "$VM_NAME" \ + --ram "$RAM_MB" \ + --vcpus $CPUS,sockets=1,cores=$CPUS,threads=1 \ + --cpu host-passthrough \ + --os-variant "$OS_VARIANT" \ + --disk path="$DISK_IMG",format=qcow2,bus=virtio \ + --cdrom "$WINDOWS_ISO" \ + --disk path="$VIRTIO_ISO",device=cdrom \ + --network network=default,model=virtio \ + --graphics spice,listen=none \ + --video virtio \ + --sound ich9 \ + --channel spicevmc \ + --tpm backend.type=emulator,model=tpm-crb,version=2.0 \ + --boot uefi \ + --noautoconsole + +echo "" +info "VM created! Next steps:" +echo "" +echo " 1. Connect to the VM console:" +echo " virt-viewer $VM_NAME" +echo "" +echo " 2. During Windows install, when it can't find drives:" +echo " Browse → Load Driver → virtio-win ISO → vioscsi → ${OS_VARIANT} → amd64 → OK" +echo " Then select the visible disk and continue installation." +echo "" +echo " 3. After Windows installs, install Spice Guest Tools:" +echo " Open the virtio-win ISO in Windows → run virtio-win-guest-tools.exe" +echo " This gives you clipboard sharing, auto-resize, and better performance." +echo "" +echo " 4. Install the Guardian browser inside the VM for proctored WGU tests." +echo "" +echo " Manage: virt-manager" +echo " Start: virsh start $VM_NAME" +echo " Stop: virsh shutdown $VM_NAME" +echo " Console: virt-viewer $VM_NAME" diff --git a/home/sscoble.nix b/home/sscoble.nix index 76cbb8b..b02d901 100644 --- a/home/sscoble.nix +++ b/home/sscoble.nix @@ -35,6 +35,7 @@ nodejs bun git + tigervnc ]) else []); # === Home directory (conditional) === @@ -157,22 +158,28 @@ la = "ls -A"; l = "ls -CF"; nix-clean = "sudo nix-collect-garbage -d && nix-collect-garbage -d"; + n97-mini = "vncviewer 100.104.70.88:5900"; }; initContent = '' # Nix flake shortcuts (adapts to platform) + # Uses local checkout so uncommitted changes are picked up immediately. + # Push to the remote repo separately when you want to share/sync. if command -v nixos-rebuild &>/dev/null; then - alias nr="sudo nixos-rebuild switch --flake git+https://os.scoble.me/forgejo/sscoble/laptop-config.git#laptop" + alias nr="sudo nixos-rebuild switch --flake ~/laptop-config#laptop" elif command -v home-manager &>/dev/null; then - alias nr="home-manager switch --flake git+https://os.scoble.me/forgejo/sscoble/laptop-config.git#sscoble" + alias nr="home-manager switch --flake ~/laptop-config#sscoble" fi # Pi / QMD (work from homebrew or nixpkgs) alias vq="qmd query" 2>/dev/null || alias vq="echo 'qmd not found — install with brew install qmd or bunx @tobilu/qmd'" + + # VNC into n97-mini server + alias n97-mini="vncviewer 100.104.70.88:5900" ''; envExtra = '' export EDITOR=nvim export VISUAL=nvim - export PATH=$HOME/.npm-global/bin:$PATH + export PATH=$HOME/.local/bin:$HOME/.npm-global/bin:$PATH ''; }; } diff --git a/home/wgu.nix b/home/wgu.nix index c802ccf..66e258c 100644 --- a/home/wgu.nix +++ b/home/wgu.nix @@ -20,6 +20,10 @@ tinymist # LSP (tinymist binary speaks typst-lsp protocol) pandoc + # Precalculus calculator (GUI + CLI: `qalculate-gtk` for graphs, `qalc` in terminal) + qalculate-gtk + libqalculate # provides the `qalc` CLI binary + # Password manager (LastPass has no Linux native client — use Bitwarden) bitwarden-desktop bitwarden-cli @@ -47,4 +51,11 @@ }, } ''; + + # === Windows VM helper (WGU Guardian browser for proctored tests) === + home.file.".local/bin/create-wgu-vm" = { + force = true; + executable = true; + source = ./scripts/create-wgu-vm; + }; } diff --git a/modules/t490.nix b/modules/t490.nix index 6d50f29..e1534f8 100644 --- a/modules/t490.nix +++ b/modules/t490.nix @@ -191,7 +191,7 @@ touchpad = { tapping = true; naturalScrolling = true; - accelSpeed = "0.3"; + accelSpeed = "0.44"; disableWhileTyping = true; }; }; @@ -199,6 +199,6 @@ # TrackPoint sensitivity services.udev.extraRules = '' ACTION=="add", SUBSYSTEM=="input", ATTR{name}=="*TrackPoint*", \ - ATTR{device/sensitivity}="200", ATTR{device/speed}="150" + ATTR{device/sensitivity}="160", ATTR{device/speed}="120" ''; } diff --git a/modules/virtualization.nix b/modules/virtualization.nix new file mode 100644 index 0000000..18cdc2a --- /dev/null +++ b/modules/virtualization.nix @@ -0,0 +1,38 @@ +{ config, pkgs, lib, ... }: + +{ + # === KVM / QEMU / libvirt — Windows VM for WGU Guardian browser === + + # Enable KVM — kernel module + group + boot.kernelModules = [ "kvm-amd" "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + # libvirt daemon + virtualisation.libvirtd = { + enable = true; + qemu.swtpm.enable = true; # TPM emulation (required for Windows 11) + }; + + # User in libvirt group (manages VMs without sudo) + users.users.sscoble.extraGroups = [ "libvirtd" ]; + + environment.systemPackages = with pkgs; [ + # VM manager (GUI — launch from rofi) + virt-manager + virt-viewer # Spice client (better than virt-manager's built-in) + spice-gtk # Spice protocol (clipboard, USB, audio) + + # Software TPM + swtpm + + # Guest tools + spice-vdagent # Clipboard sync, auto-resize + ]; + + # Enable Spice USB redirection service + services.spice-vdagentd.enable = true; + + # Firewall: allow VNC/Spice from localhost only (safe for guest display) + networking.firewall.allowedTCPPorts = [ ]; + networking.firewall.allowedUDPPorts = [ ]; +}