Releases: arkenoi/qubes-win-idd-driver
Release list
QWT-NG 4.3.17 (agent 45e788d)
QWT-NG 4.3.17
A bugfix release. MSI ProductVersion 4.3.17 — a real bump over 4.3.16, so an in-place
MajorUpgrade lands on an existing guest without an uninstall.
What changed since 4.3.16
Dragging a window by its Windows title bar no longer wobbles. 4.3.16 shipped a regression, and
this is the retraction of a claim made in that release's own notes: "It also releases a drag latch
that could otherwise stick if a button release was lost." That latch release is the defect.
The drag latch (g_InputDragWindow) is what selects the fixed input-translation law for a
guest-native drag — dom0 sends window-relative pointer motion and withholds the root coordinate, so
the agent must add an origin, and translating against the live window origin closes a gain-1
feedback loop through the announce path. Four separate shipped fixes gate on that latch: the
interpolated origin, InputDragFreezeContent, DragEventPriority, and the announce pacing.
4.3.16's HandleCrossing released the latch on a LeaveNotify. Measured on a hand drag
(2026-09-01, agent EB25802E85D471A8, shipped tuning asserted from the log banner):
| motion events | translation law used | window-path direction reversals | |
|---|---|---|---|
| first 569 ms | 50 | 50/50 interpolated origin | 4/50 = 8 % |
| after the crossing | 490 | 489/490 live origin | 99/490 = 20 % |
569 ms into a 5.0 s drag, dom0 delivered LeaveNotify mode=0 (NotifyNormal) detail=3 (NotifyNonlinear) and the latch went with it. Announced positions then swung 867 ↔ 2475 px at
~15 Hz. 16–19 % reversals is the wobble exactly as it was first characterised, so that single event
restores it in full and it never re-arms, because only a new button press arms the latch.
The premise was wrong: in a guest-native drag the window moves, not the pointer, and the button
stayed down for another 4.4 s with 490 further motion events for that same window arriving after
the "leave". The hole it claimed to close — a lost Button1 release freezing a window — had
already been closed 17 days earlier by INPUT_DRAG_STUCK_MS and the settle-sweep disarm.
Fix: the latch release is deleted, not guarded. HandleCrossing now receives the message,
records it under the drag trace, and touches no state. The one real defect that handler was written
for — a LogWarning per crossing event on the input path, ~10 lines/s — stays fixed.
App-menu shortcuts launch. From core-agent b2ccd83: get-appmenus.ps1 emits the two fixed
desktop-entry ids dom0's launchers are actually wired to, so Windows Run Terminal and File
Explorer start something instead of nothing; and start-app.ps1 opens the interactive user's
folder rather than the service account's, and returns in 4–5 s instead of blocking. Verified on a
live guest: both entries emit, both launch, Explorer opens the right folder.
Fault injection cannot be in a release build. QgaFaultInjection was flipped to default 1 on a
diagnostics branch whose own comment reads "NEVER merge this to a release branch", and those
commits reached the branch this release is cut from. The default is back to 0 and a fault-capable
agent must now be asked for explicitly (-p:QgaFaultInjection=1). No published release ever
carried it — the flip is newer than 4.3.16 — but every developer build on that branch did.
New, opt-in, and off by default: a drag trace. ProtoTraceDrag=1 (DWORD under the gui-agent
key) emits only the input-rate protocol messages — motion, buttons, the drag latch, outgoing
position announces, inbound configures — at roughly 44 lines/s. Unlike full ProtoTrace it does not
carry the per-damage-rect lines that multiply the frame-walk tail, so latency and mechanism can be
judged from the same run. msg=MOTION now reports br=, which of the translation laws each event
actually took, and msg=DRAGLATCH names every arm and teardown. tools/drag-analyze.py reads the
result. Nothing is emitted unless the value is set.
What was actually tested — and what was not
Measured, on the artifact's own code path:
- The defect above, on a hand drag, with the failing side recorded in full (the table). The build
that produced it is the defect build, on the same guest, window and tuning as the fix, so the
check has been seen to fail. - The instrument was calibrated against injected ground truth before being trusted: a synthetic
drag with a chosen dom0 apply lag of 17 / 80 / 200 ms was fitted back at 10 / 90 / 190 ms, and
the wobble metric moved 5 % → 41 % with it. - The drag tuning is unchanged from the 2026-08-16 user-approved baseline and is compiled in, not
configured:git diff 168a869..HEAD -- gui-agent/perf.cchanges no drag default, and the guest's
registry carries noInputDrag*override. - App-menu entries: emitted, launched, and returning on a live guest.
Confirmed by hand after this release was cut (owner, 2026-09-01: "it drags fine now"), on
agent C3D2A0193F9D493A, which differs from the released 45e788d by the version file alone. The
trace from those drags, same guest, same window, same tuning as the defect table above:
| defect build | fixed build | |
|---|---|---|
NotifyNormal crossings during the drag |
1 | 21 |
| what each did to the latch | destroyed it | kept it, every one |
| motion events on the fixed translation law | 50/540 = 9 % | 587/587 = 100 % |
| window-path reversals, per drag | 20 % after the crossing | 11 % / 0 % / 8 % |
Note the crossing rate: 14 arrived inside the first 3.5 s drag alone. On the defect build the
first one lands within about half a second of any drag, which is why this was not intermittent.
NOT verified, stated plainly:
- Clicking the two app-menu entries in dom0 after
qvm-sync-appmenusis unverified; only the
guest half was exercised. - No acceptance campaign was run against this artifact. It is a targeted bugfix on top of the
4.3.16 campaign (20260830-062519, 36 checks, 0 failures), not a re-qualified release.
Known-unchanged residual
The drag law assumes dom0 applies an announce within about 25 ms. Measured on the rig it does
(median 0, p75 17), but the tail reaches 82 and 398 ms, and the synthetic calibration quantifies
what happens past the assumption: 22 % reversals at 80 ms, 35 % at 200 ms, against 1 % at 17 ms. So
a dom0 that answers slowly will still wobble, with nothing regressed in the guest. That is a design
residual of InputDragAdoptMs / InputDragLagMs, unchanged since 2026-08-16, and it now has
numbers attached for the first time.
QWT-NG 4.3.16 (agent 409439d)
QWT-NG 4.3.16
Built from 6022427bd0ab (agent 4f1c1865351e). MSI ProductVersion 4.3.16 — a real bump over
4.3.14, so an in-place MajorUpgrade lands on an existing guest without an uninstall.
What changed since 4.3.14
System dialogs no longer arrive without a dom0 border. IsPopup() treated any caption-less
window as an override-redirect popup unless it carried WS_SYSMENU and WS_EX_APPWINDOW. Windows
Update's dialog (MusNotificationUx, class Shell_SystemDialogProxy) sets WS_EX_APPWINDOW but not
WS_SYSMENU, so a full system dialog reached the display undecorated — no trust border. A
taskbar-declaring, activatable, non-toolwindow top-level window is now treated as a real window.
Inline app popups are untouched: dropdowns, context menus and tooltips never request a taskbar
button, verified against every window on a live guest (Net UI Tool Window 0x00000088,
SCENIC_DROPSHADOW_WINDOW_CLASS 0x08180028, and others — none carries WS_EX_APPWINDOW).
dom0 focus is honoured under foreground lock. HandleFocus called SetForegroundWindow and
ignored its result. That call silently fails when another process owns the foreground, so dom0 could
ask for focus and the guest would quietly refuse — which looked like a modal dialog blocking the
desktop, though nothing was WS_DISABLED. It now attaches to the foreground thread's input queue,
retries, always detaches, and logs when focus still cannot be taken.
MSG_CROSSING (127) is handled. Every pointer enter/leave previously hit the dispatcher's
default branch and wrote a warning — file I/O on the input path at roughly ten lines per second. It
also releases a drag latch that could otherwise stick if a button release was lost.
The PV console (xencons) ships and binds. XENBUS\VEN_XP0001&DEV_CONS sat at CM code 28 on every
guest because QWT vendored no xencons at all. It is now built from a pinned xenbits commit,
test-signed with the rest of the package, and installed — giving dom0 an out-of-band channel into a
guest whose qrexec, window capture and event log have all stopped.
xenvif is pinned. The build cloned xenbits master, so the kernel-mode PV NIC driver in a
release was chosen by the wall clock and two builds of "the same" version could carry different
driver code. Pinned to 0c61248, with the rev-5 assertion still guarding a future bump.
Installer and packaging: all MSI driver catalogs are signed and verified at build time (four of
five were shipping unsigned, which is what turned an install into a 27.9-minute hang); the PV NIC
priming latch is seeded unconditionally, StandaloneVMs included; the network "discoverable setup"
prompt is suppressed; a same-version reinstall that leaves no gui-agent is recovered with an
ADDLOCAL-only retry; and the watchdog reports SERVICE_STOPPED on PRESHUTDOWN instead of stalling
every clean shutdown for about three minutes.
What was actually tested — and what was not
Campaign 20260830-062519, 36 checks, 0 failures, on this exact artifact, from sealed goldens
verified intact before and after, with one Windows guest running at a time:
| Path | Result |
|---|---|
| Same-version reinstall, Win10 + Win11 | 7/7 each — agent hash == release, no reboot dialog (watcher proved it sampled), PV console bound, autologon armed, xenbus_monitor disabled and not running |
| Seeded pending-reboot condition, Win10 + Win11 | 8/8 each — monitor armed to auto-start and a reboot Request written mid-MSI; both guests returned, suppressor held |
| AppVM cold boot, Win10 + Win11 | 3/3 boots each, windows mapped, none fullscreen-sized |
| Clean install (two-stage), Win10 | graded 9/0 separately on pristine media — both stages, two run ids, testsigning_active:false precondition, CONS/IFACE/VBD bound |
| Network | 25 MB transferred on both AppVMs, each adapter's own RX counter accounting for it, PV NIC the only adapter present |
NOT tested, stated plainly:
- Upgrade over a previous QWT-NG release. The goldens were built from this same release, so those
cells took the same-version-reinstall branch rather than a major upgrade. The in-place upgrade path
is unverified for this build. - Upgrade over stock QWT 4.2.2. Not demonstrated.
- Clean install on Windows 11. Only Win10 was graded on pristine media.
Ship it on that basis: it is better than 4.3.14 on every path that was measured, and two upgrade
paths are unmeasured.
QWT-NG 4.3.14 (agent 5634f90)
QWT-NG 4.3.14 — stability
This release is about a qube coming back by itself, and about the guest never taking over your
screen. Nothing here changes the display pipeline or performance.
The qube can log itself back in — the installer arms autologon
A Windows guest that stops at the sign-in screen is not merely inconvenient, it is gone: with
no interactive session, qrexec service calls have nobody to run as, so dom0 cannot run apps in it,
update it, or read it. And in seamless mode the sign-in screen is not displayed at all, so the
qube's window simply stays empty — measured: autologon off means zero windows mapped, while
the qube is running and answering. Two forum reports were exactly this.
The installer now arms autologon:
- it validates the credentials with
LogonUserbefore writing anything — arming with a wrong
password causes the very lockout this prevents; - it stores the password as the LSA secret
DefaultPassword, not as the world-readable
plaintext registry value, and removes a plaintext value if one is there. An LSA secret is also
not consumed byAutoLogonCount, which is what silently disarmed autologon before; - a boot-time SYSTEM task re-asserts the settings, because Windows updates rewrite them;
install.cmd /autologon:PASSWORDfor an unattended install,/noautologonto skip; run
interactively it asks, with a two-minute deadline so a minimised install can never hang on it.
Verified with a control that fires: with the secret correct a user session forms with no plaintext
password anywhere; with the secret deliberately wrong, no session forms — so Winlogon is
demonstrably using the value we write. Re-arm at any time with set-autologon.ps1, kept in the
guest under Qubes Tools\vmupdate-shim\.
Guests joined to a domain, or using a Microsoft account with Windows Hello, cannot be armed this
way. That is known and not yet handled.
A new qube has something in its application menu
A freshly installed Windows guest has almost nothing in its Start Menu that the shortcut sweep
finds, so dom0's application list came up nearly empty — and in seamless mode there is no taskbar
and no desktop either, so that list is the only way in. The guest now also reports Notepad,
Microsoft Edge (when present), File Explorer, Settings, Command Prompt, Windows PowerShell,
and elevated Command Prompt (Administrator) and Windows PowerShell (Administrator).
Entries a real Start Menu shortcut already provides are not duplicated.
The two elevated entries go through the ordinary Windows elevation prompt. They deliberately do
not use any silent-elevation trick — a menu entry that quietly handed out admin would be a hole —
and since 4.3.11 that prompt is an ordinary window in dom0, so it can actually be answered.
Only eight of them are enabled by default. A Windows guest reports every Start Menu shortcut
it can find — 45 on a stock Windows 11, mostly uninstallers, help files and vendor links — and
dom0 shows all of them when a qube has no menu selection yet. qwt-ng-prepare-qube now sets one:
Notepad, File Explorer, Settings, Command Prompt, PowerShell, the two elevated variants and Edge.
Everything else stays available and can be ticked in Settings → Applications, and an existing
selection is never touched.
qvm-sync-appmenus no longer fails the whole sync
Reported from the field as "Refresh applications … returned non-zero exit status 1", with no
application list at all. dom0 raises on any non-zero exit from the guest's appmenus service and
throws away everything it received, and the guest script had several ways to throw before emitting
a single line — most simply, it loaded its logging helper through %QUBES_TOOLS% and died outright
if that variable was not in the service's environment. An absent Start Menu folder, an unreadable
shortcut or a denied registry write did the same.
Every stage is now guarded, one bad shortcut is skipped instead of fatal, and the service always
exits 0 — reporting fewer apps is a bad day, reporting none is a broken qube. Two silent
constraints of dom0's parser are also enforced now: it discards any line that is not ASCII (so a
shortcut with a curly apostrophe used to vanish) and reads at most 1024 bytes per line. The service
is additionally installed under the exact name dom0 asks for, qubes.GetAppmenus.
Run qvm-sync-appmenus <vm> in dom0 after upgrading to pick all this up.
Nothing fullscreen-sized during boot or shutdown, ever
"The boot/shutdown screen is never allowed, feature or not" was enforced by matching the window
class (LogonUI, override-redirect). That leaked: with service.gui-fullscreen enabled, a
fullscreen-sized boot-phase surface that is neither of those was mapped and covered the whole
display. Startup and shutdown are a phase, so they are now tested as one — a fullscreen-sized
window is denied while there is no shell window (boot, logon, and shutdown once explorer has
gone), while the input desktop is secure, and briefly after it stops being. In those phases
nothing fullscreen-sized is an app anyone asked for, whatever its class, and the feature does not
apply to it.
The secure desktop, by mode
In seamless mode the Windows secure desktop is never shown — each surface would become its own
standalone dom0 window, and that full-screen dimming backdrop was the "unclosable black window"
field reports. In the windowed desktop (non-seamless, service.gui-fullscreen) the whole guest
desktop is shown in one bounded window, sign-in screen included, because there it is ordinary
content in a window you can move and close. That is the way into a guest that cannot log itself
in. UAC prompts are unaffected: they are ordinary windows in both modes.
Diagnostics that say what is wrong
QGADESKSTUCK— the agent now says when it has been frozen on the secure desktop for more
than 30 seconds, naming the desktop, the elapsed time, and the two ways out. A guest waiting at
a sign-in screen used to produce a completely silent log.- Reboot-cause audit — on an AppVM the System log lives on the volatile C: and is destroyed by
the restart it describes, so an unattended reboot could not be told apart from one dom0 asked
for. Event-triggered tasks now copy Event 1074 (who asked), 6008 and 41 onto the private volume
as they are written. - The watchdog stops respawning the agent into a shutting-down machine, and records which
signals it consulted every time the agent dies. One respawn can still happen at the very start
of a shutdown, before the service control manager reports it — visible in the log, harmless.
Upgrading
An in-place upgrade: run the installer, it detects the older version and lets the MSI replace it
in one transaction. Validated end to end on clean Windows 10 22H2 and Windows 11 25H2 installs,
template plus an AppVM through three cold boots each.
QWT-NG 4.3.13 (agent d3273de)
Two fixes that were never actually shipped
An audit of the whole project log — prompted by noticing that resizing a qube window still
played the Windows device-connect chime — found two changes that had been recorded as done but
only ever applied by hand to a test machine. Neither ever reached a release. Both ship now.
- Resizing no longer beeps. Every resolution change makes the display driver reload its
mode list, which Windows treats as a monitor being plugged in, so it plays the device
connect/disconnect sound. Those three sound events are now silenced for the live user, every
profile on the machine, and the profile new users are created from. - The guest no longer locks its own session. A Windows lock screen is drawn on the secure
desktop, which this agent deliberately refuses to show to dom0 — so an idle lock left the
qube looking frozen with no way back in. dom0's screen lock is the one that protects your
machine, and a guest lock only invites typing a password into an untrusted VM. Prevention
existed in the tree for weeks but was never included in the installer; it is now, and the
install log reports what it changed.
The audit checked ~111 other "we changed X" claims across 18,400 lines of log; every one of
them had a real implementation. The rule going in: a change applied by hand to a test machine
is not a fix.
Every dom0 setting is now documented in the README
service.enableWinKey, service.gui-fullscreen, service.hideGuestTitleBar,
service.gui-agent-debug and service.uac-disable are listed with what each does and when to
use it — including that service.uac-disable is the Windows equivalent of passwordless sudo
and must be set on the template, since an AppVM's system drive is restored from its
template at every boot.
Also documented: what is deliberately not configurable. A UAC prompt is always drawn on the
normal desktop, which makes it an ordinary window — standalone in seamless mode, inside the
desktop window otherwise. Windows' secure desktop is never shown to dom0 in any mode; that was
the "unclosable black window" fixed in 4.3.11.
Still open
Resizing is still slower than it should be, for the same reason it used to beep: the driver
reloads its mode list for every new size. The sound is gone; the pause is being worked on.
Install
RPM in dom0, or the ISO/tarball inside the template; let the template shut down at the end.
In-place upgrade from any 4.3.x. Hand-created qube? qvm-features <qube> vmexec 1 and
qvm-prefs <qube> qrexec_timeout 1800 in dom0 (on the template; AppVMs inherit).
QWT-NG 4.3.12 (agent 2adbd57)
The black window is fixed — it was Windows' UAC prompt
The recurring "unclosable black window" (forum 42717 — thank you GWeck for the logs) is
Windows' UAC secure desktop being mapped into dom0. When something asks to elevate,
Windows switches the guest to a separate desktop and draws the consent dialog over a
full-screen dimming backdrop; the agent followed that switch and mapped those surfaces. You
got a large dark window that could not be closed and ignored input — because the guest's
input was parked on a desktop dom0 never sees. If the agent died during the switch (a bug it
also hits), you got the backdrop without the dialog: a black rectangle and an invisible
prompt waiting for an answer.
It never reproduced in testing because every test machine ran with UAC off. One registry flip
reproduced the entire report on the first try.
Fixed in two layers:
- Elevation prompts are ordinary windows now. Consent is drawn on the normal desktop, so
it behaves like any other guest window: a standalone window in dom0 in seamless mode, or a
window inside the desktop window in non-seamless mode. You can read it and click it. - The secure desktop is never mapped. If Windows switches desktops anyway — lock screen,
Ctrl+Alt+Del, a site policy — the agent suppresses output for the duration instead of
mapping those surfaces, and resumes cleanly. Your open windows are left as they were.
A guest can no longer put a screen-sized window on your display
Non-seamless mode (the whole guest desktop in one window) used to inherit the guest's
host-sized desktop, so switching to it produced a window covering the entire display. Now the
agent shrinks the desktop first (1280×800 by default, remembered separately from the seamless
size) and completes the switch only once the smaller mode is live — and it refuses outright
any guest-initiated switch that would still cover the screen. Sizing driven by dom0 — you
resizing or maximizing the qube's window — is untouched: that is the one legitimate way for a
qube to fill the screen.
UAC can be turned off per qube, with one caveat
qvm-features <qube> service.uac-disable 1 turns UAC off (reboot required). Only an explicit
1 acts, and clearing the feature undoes it. It must be set on the TEMPLATE: an AppVM's
system drive is reset from its template at every boot, and Windows reads this setting at boot,
so a value written inside an AppVM can never take effect. The agent now warns when the feature
is set somewhere it cannot work. Turning UAC off is not recommended — without it any code in
the qube reaches admin/kernel.
Also
- Reinstalling an older release now really downgrades the display driver (Windows' driver
ranking only rebinds upward, which silently left a newer driver bound to an older agent). - Capture-engine telemetry (
WCBLACK/WCDEAD) has a fault-injection test behind it, so those
warnings are proven to fire rather than assumed to.
Known, not fixed here
Resizing the qube's window is slow and plays the Windows device-connect sound: each new size
makes the display driver reload its mode list, which Windows treats as a monitor change. Being
worked on.
Install
RPM in dom0, or the ISO/tarball inside the template; let the template shut down at the
end. In-place upgrade from any 4.3.x. Hand-created qube? qvm-features <qube> vmexec 1 and
qvm-prefs <qube> qrexec_timeout 1800 in dom0 (on the template; AppVMs inherit).
QWT-NG 4.3.11 (agent 71fa0a4)
The black window is fixed — it was the UAC prompt all along
The recurring "unclosable black window" (forum 42717, thank you GWeck for the logs) is
Windows' UAC secure desktop, mapped into dom0. When an app asks to elevate, Windows
switches the guest to a separate desktop and draws the consent dialog over a full-screen
dimming backdrop. The agent followed that switch and mapped those surfaces — so dom0 got a
big dark window that could not be closed and did not respond to anything, because the
guest's input was parked on a desktop dom0 never sees. If the agent happened to die during
the switch (a known bug it also hits), you got the backdrop without the dialog: a black
rectangle and an invisible prompt waiting for an answer nobody could give.
It never reproduced here because every test machine ran with UAC turned off. One registry
flip on a test VM reproduced the whole report on the first try.
4.3.11 fixes it in two layers:
- UAC prompts are now just windows. The guest is configured to draw consent on the
ordinary desktop, so an elevation prompt behaves like any other guest window: a
standalone window in dom0 in seamless mode, or a window inside the desktop window in
non-seamless mode. You can read it and click it. This is unconditional — there is no
mode or setting in which Windows' secure desktop gets shown. - The secure desktop is never mapped. If Windows switches desktops anyway — a lock
screen, Ctrl+Alt+Del, a site policy — the agent now freezes its output entirely for the
duration instead of mapping those surfaces, and resumes cleanly afterwards. No black
window, and your open windows stay as they were.
Also new: qvm-features <qube> service.uac-disable 1 turns UAC off in a guest if you want
that (reboot required). Only an explicit 1 acts, and clearing the feature undoes it —
disabling UAC is not recommended, since without it any code in the qube reaches
admin/kernel, which is the surface facing the hypervisor.
Other fixes
- Reinstalling an older release now actually downgrades the display driver. Windows'
driver ranking only ever rebinds upward, so reinstalling an older QWT-NG silently left
the newer driver bound against the older agent. Both install paths now verify the bound
driver version against the package and force the rebind when it differs. /iddonlywarns when it is run from a stale copy of the installer tree.- Capture-engine telemetry from 4.3.10 (
WCBLACK/WCDEAD) now has a fault-injection test
behind it, so those warnings are proven to fire rather than assumed to.
Install
RPM in dom0, or the ISO/tarball inside the template; let the template shut down at the
end. In-place upgrade from any 4.3.x. Hand-created qube? qvm-features <qube> vmexec 1 and
qvm-prefs <qube> qrexec_timeout 1800 in dom0 (on the template; AppVMs inherit) — see the
README.
QWT-NG 4.3.10 (agent ab36aef)
The black-window release: the log now diagnoses it, and normal logs shrink ~40×
The persistent black-window report (forum 42717, posts 79/96 — thank you GWeck for the log)
turned out not to be diagnosable from any log the agent could produce: the per-window capture
engine (PrintWindow-based) logged nothing. A capture that fails five times and dies, and a
capture that "succeeds" but renders black (DirectComposition content the API cannot reach), both
looked exactly like healthy operation while dom0 showed a black window. We could not reproduce
the black window on 25H2 — plain, with OpenShell, or as an AppVM; Explorer renders correctly in
all three here — so this release makes the field log answer the question:
WCBLACK— warns when captures succeed but return ≥99 % near-black content, three in a
row (validated live against a deliberately all-black window; no false positives on normal
windows, real windows measure ≤4 % near-black).WCDEAD— warns when a capture channel dies (5 consecutive failures, with the last error
code). Previously this latch was silent forever.- A failed initial capture ("window starts black until the first successful capture") is now a
visible warning instead of a debug line.
If you see the black window: upgrade to 4.3.10, reproduce, and post the gui-agent log — it
will now say explicitly which of the two failure modes your machine hits (or neither).
Logs are normal-user-sized now
A 15-minute log used to weigh 2.3 MB — 91 % of it per-frame performance telemetry, 7 % per-paint
tooltip traces. Both are now off by default and the log carries events, not the firehose
(same session ≈ 50 KB). Full diagnostics are one dom0 command away:
qvm-features <qube> service.gui-agent-debug 1
That single switch (read at agent start; unset it to go quiet again) enables everything a
diagnosis needs: per-frame QGAPERF telemetry, protocol/paint traces, and Debug-level logging.
Guest-local registry switches (PerfLog/PerfEveryN/ProtoTrace under the gui-agent key)
still exist for fine-grained control; the dom0 feature wins over them. The log itself tells
you this: it prints one QGAPERF off (enable ALL diagnostics from dom0: …) line.
Installer: template priming can no longer be skipped by a bad read
The netvm-free PV NIC priming gate read the qube's class from qubesdb once; an intermittent
misread made the installer skip priming on a real template — which is exactly the AppVM
restart-loop the priming exists to prevent. The read is now retried, and an unreadable class
primes anyway (fail closed — priming is transactional and harmless on non-templates).
Also new
guest/pwdiag.ps1— a one-command in-guest diagnostic: lists every visible window and
test-captures each the way the agent does (identity + black-ratio + PNG evidence).
Install
RPM in dom0, or the ISO/tarball inside the template; let the template shut down at the end.
In-place upgrade from any 4.3.x. Hand-created qube? qvm-features <qube> vmexec 1 and
qvm-prefs <qube> qrexec_timeout 1800 in dom0 (on the template; AppVMs inherit) — see the
README.
QWT-NG 4.3.9 (agent 33f3109)
Fixes the withdrawn 4.3.8's regression — root cause confirmed to the mechanism
4.3.8 was withdrawn before anyone downloaded it: upgrading to it silently killed arbitrary
resolutions. Root cause, confirmed by controlled experiment: the driver build left the INF's
DriverVer version to stampinf's default — the build time (HH.MM.SS.ms) — because the
vendored Microsoft sample ships DriverVer= empty with an unfinished TODO. Per Microsoft's own
Driver Versioning spec,
a display driver version's first field declares its WDDM model (21 = WDDM 2.1, 20 = 2.0, …),
and enforcement of the field begins at WDDM 2.1. A 14-package ladder over identical driver bytes
shows a perfect step exactly there: versions 4–20 work, 21–99 break (modes never surfaced,
control ioctl rejected) — so builds made at 21:00 or later declared themselves WDDM-2.1+
drivers and were punished for over-claiming, while earlier-in-the-day builds under-claimed
harmlessly. 4.3.7 was built at 15:51; 4.3.8 at 21:22. Verified further by an alternating
same-version repeat control (works/breaks tracks the declared version perfectly).
DriverVer is now pinned to a sane 4.3.x.y scheme in every build, and the driver DLL carries
a per-build FILEVERSION (it previously had no version resource at all). The installer's IDD
activation also gained an identical-bytes guard and a self-heal for guests that took 4.3.8 —
belt and suspenders; the version pin is the fix.
Carried over from the withdrawn 4.3.8
- Black fullscreen window in AppVMs — should be fixed, field confirmation wanted (forum
42717 post 85): the desktop window itself could be mapped as an unclosable black fullscreen
window when two window filters failed together. A new attribute-based reject catches it
regardless of which filter leg fails; anything it rejects could only ever have rendered as a
black rectangle. Toasts are structurally unmatchable and were verified live. If you saw the
black window: please upgrade and report back. DiagWindowFilterOff(registry DWORD, default 0, diagnostic only): disable individual window
filters to isolate a filter bug with one registry write.- Updater relay deploys reliably on upgrade installs and can never lose the working relay to
a failed compile.
Install
RPM in dom0, or the ISO/tarball inside the template; let the template shut down at the end.
In-place upgrade from any 4.3.x. Hand-created qube? qvm-features <qube> vmexec 1 and
qvm-prefs <qube> qrexec_timeout 1800 in dom0 (on the template; AppVMs inherit) — see the
README.
QWT-NG 4.3.7 (agent 24cf973)
Finishes the AppVM shutdown-loop fix — and removes what 4.3.6 left behind
4.3.6 stopped AppVMs from reboot-looping, and its release notes claimed "zero reboot requests
logged". That claim was wrong, and this release fixes what it hid.
What actually happens. The PV disk driver (xenvbd) files a "restart to complete installation"
request on every AppVM boot — an AppVM's system disk is volatile, so the device configuration
that would satisfy the request is forgotten at every shutdown. With AutoReboot=1 (4.3.4 and
earlier) that request became the silent reboot loop. With AutoReboot=0 (4.3.6) it became a
modal prompt — "Xen PV Storage Host Adapter needs to restart the system to complete
installation" — shown on the dom0 desktop on every AppVM start, with the xenbus_monitor
service wedged in STOP_PENDING behind it. Clicking Yes reboots the qube, and the demand returns
on the next boot.
The fix. xenbus_monitor now ships disabled, on every qube class. Nothing in the
unattended QWT flow needs it: the installer performs its own reboots at the right moments. The
per-boot payload re-asserts the disable in case a PV driver package upgrade re-registers the
service. No prompt, no wedged service, no reboot loop.
Tested: the defect was first reproduced on an unfixed 4.3.6 template (fresh AppVM: dialog
present, monitor wedged — so every acceptance check has been seen to fail), then 4.3.7 was
verified end to end on two paths: an in-place upgrade of a live 4.3.6 template (2 AppVM cold
boots), and a fresh template image carrying QWT-NG 4.3.2 upgraded to 4.3.7 (3 AppVM cold
boots). All five boots: no dialog, monitor Disabled/Stopped, zero reboot requests executed,
network up. The PV drivers still file their per-boot request on an AppVM — it is now inert.
A direct stock-4.2.2 → 4.3.7 run was not part of this pass; that upgrade machinery is
unchanged since it was validated for earlier 4.3.x releases.
Also fixed (found by review of 4.3.6)
- qubesdb connection leak in the network service.
QwtngNetSetupopened a new qubesdb
connection every 2 seconds and never closed any (~43k/day); qubesdb would eventually stop
answering for every consumer in the guest. Now one connection for the service's life, every
read buffer freed, dead connections self-heal. Measured flat handles and stable memory. - Fail-closed service install. 4.3.6 deleted stock
network-setup.exebefore confirming its
replacement service registered; a failed registration shipped a template with no network
applier at all while reporting success. Now the stock binary is only removed after the
replacement demonstrably exists, and registration is retried through Windows'
marked-for-delete window. - StandaloneVMs no longer keep silent-AutoReboot armed forever (4.3.6 reset it only on
templates). - Log rotation now bounds the real log directory (
Q:\Qubes Logson the private volume —
4.3.6 pruned a directory QWT no longer writes to, while the real one grew without bound). - A failed compile of the network service can no longer silently ship the previous build's
binary.
Known issue (upgrade installs only, cosmetic)
When upgrading a template that already runs QWT-NG, the install result may show
updater_agent: "error: relay compile failed (csc rc=1)" — the old updates-relay executable can
be locked by a running updater task at that moment. The relay is unchanged since 4.3.6, so the
already-installed copy keeps working; nothing is lost. Will be made retry-clean in the next
release.
Install
RPM in dom0, or the ISO/tarball inside the template. Install into the template, not an AppVM,
and let the template shut down at the end. Upgrading from 4.3.4/4.3.6 is an in-place upgrade — no
uninstall needed.
Created your Windows qube by hand (no qvm-create-windows-qube, no dom0 RPM)? Two dom0-side
settings are required or the Qubes Update tool fails against the qube with errors like
mkdir -p /run/qubes-update/& exit:
qvm-features <qube> vmexec 1
qvm-prefs <qube> qrexec_timeout 1800
Set them on the template/standalone; AppVMs inherit both. The README now documents this
prominently (new section near the top).
Do not run the Qubes updater inside a Windows AppVM: finish updates in the template and let it
reboot itself, or the AppVM will retry forever against its volatile root.
QWT-NG 4.3.6 (agent 048d2ad)
Superseded by 4.3.7 — install that instead. 4.3.6 stops the shutdown loop but leaves the
PV drivers' per-boot reboot request answered by a modal "Xen PV Storage Host Adapter needs to
restart" dialog on every AppVM start, with thexenbus_monitorservice wedged behind it. Do NOT
click Yes (it reboots the qube; the demand returns next boot). 4.3.7 removes the dialog entirely.
Fixes the AppVM that shuts down a few seconds after starting
If an AppVM created from your Windows template dies shortly after start — on Windows 10 or 11 — this
release fixes it. Install into the template, let the template shut down, and AppVMs on it work.
No rebuild needed.
What was wrong. The installer sets xenbus_monitor\Parameters\AutoReboot=1 so that the install
itself can reboot silently rather than hanging on the PV drivers' modal "needs to restart to complete
installation" prompt. That value was then left at 1 in the finished template.
An AppVM's system disk is volatile — discarded at every shutdown. So the PV driver install
re-runs on every boot, asks for a reboot on every boot, and with AutoReboot=1 it silently gets one.
The guest reboots in a loop until Qubes gives up and halts the qube.
The installer now resets AutoReboot to 0 as its final act and reads the value back, so the template
ships with it off. Silent reboots still happen during the install, which is the only place they
were ever wanted.
Verified on an AppVM built from a template upgraded in place to this package: stays up (t+360 s
and beyond) with zero reboots initiated, where the same test on 4.3.4 died at t+90 s.
(Correction 2026-08-25: this originally said "zero reboot requests logged" and "a pristine
Windows image" — both wrong. The request is re-filed on every AppVM boot and surfaces as the
dialog described above, and the verification template carried an earlier QWT-NG build. See the
4.3.7 notes for the full mechanism.)
Also in this release
- Logs survive an AppVM reboot.
LogDirmoved to the qube's private volume. An AppVM'sC:is
volatile, so until now the log covering a boot-time failure was gone before anyone could read it. - Stock
network-setup.exereplaced by a native service reading qubesdb directly. First working
traffic at ~13–17 s from qube start, and the mid-boot outage is gone — the old tool ran twice per
boot and its second run deleted an address it had already configured correctly. - PV NIC unplug latch armed before the template ships, so an AppVM completes its NIC install in
one boot (this was 4.3.4's fix; it is real, it was simply not the cause of the shutdown loop). - No stale network identity in the image — no DHCP lease, NetworkList profile or DHCPv6 DUID left
over from installation, and DHCP off on the PV NIC. Qubes configures guests statically from
qubesdb, exactly as for Linux qubes.
Install
RPM in dom0, or the ISO/tarball inside the template. Install into the template, not an AppVM, and
let the template shut down at the end.
Do not run the Qubes updater inside a Windows AppVM: finish updates in the template and let it reboot
itself, or the AppVM will retry forever against its volatile root.