Repository navigation
Releases
egpu-switch 0.3.4: final release (security fixes, discontinued)
This is the last release of egpu-switch. The project continues as
Yby eGPU, which provides GPU switching
and safe eject without depending on all-ways-egpu, plus a desktop app and
additional controls. Its installer removes egpu-switch from Decky. Update
to 0.3.4 if you still use egpu-switch, then migrate to Yby eGPU.
Security fixes
- High: a user-writable file was executed as root. The plugin runs as root
and executedall-ways-egpufrom~/bin(and from any user's
/home/*/bin) every few seconds. Any program running as a user could
replace that file and gain root without a password. The plugin, and the
shutdown hook inextras/, now only run all-ways-egpu when the file and
every folder above it belong to root and nobody else can write to them.
If yours is in~/bin, the panel shows a command to install it
for the whole system. - Medium: root followed a link when saving settings.
settings.json
lives in a folder the user owns; replacing it with a link made root
overwrite arbitrary system files, and the settings screen could read any
JSON file. Settings are now read and written without following links,
through a temporary file, and only the two known options are kept. - Low: fixed file names in
/tmp. The shutdown hook wrote its errors to
predictable files in/tmpas root. It no longer uses/tmp. - Low: Deep rescan could pick a bridge in another PCI domain. On
machines with more than one PCI domain it could reset the wrong bridge. It
now matches the domain too.
These issues were identified during the review for the Yby eGPU release.
Português (Brasil)
Esta é a última versão do egpu-switch. O projeto continua como
Yby eGPU, que mantém a troca de GPU e
a ejeção segura, acrescenta um app do desktop e outros controles, e não
depende do all-ways-egpu. O instalador remove o egpu-switch do Decky durante
a migração. Se você ainda usa o plugin antigo, atualize para a 0.3.4 e
depois migre para o Yby eGPU.
- Alta: o root executava um arquivo que o usuário podia alterar. O
plugin roda como root e executava oall-ways-egpude~/bin(e do
/home/*/binde qualquer usuário) a cada poucos segundos. Qualquer
programa do usuário podia substituir esse arquivo e obter privilégios de root sem senha.
Agora o plugin e o gancho de desligamento emextras/só executam o
all-ways-egpu quando o arquivo e todas as pastas acima dele pertencem ao root e
não permitem escrita por outros usuários. Se o seu estiver em~/bin, o painel mostra
o comando para instalar em um diretório do sistema. - Média: o root seguia um link ao salvar as configurações. O
settings.jsonfica numa pasta do usuário; substituí-lo por um link simbólico fazia o
root sobrescrever qualquer arquivo do sistema, e a tela de configurações
podia ler qualquer arquivo JSON. Agora as configurações são lidas e
gravadas sem seguir links simbólicos. A gravação usa um arquivo
temporário, e apenas as duas opções reconhecidas são mantidas. - Baixa: nomes fixos em
/tmp. O gancho de desligamento gravava erros
em arquivos previsíveis no/tmpcomo root. Não usa mais o/tmp. - Baixa: a Busca profunda podia escolher uma ponte de outro domínio PCI.
A busca agora também confere o domínio PCI.
Español
Esta es la última versión de egpu-switch. El proyecto continúa como
Yby eGPU, que mantiene el cambio de GPU y
la expulsión segura, añade una app de escritorio y otros controles, y no
depende de all-ways-egpu. Su instalador retira egpu-switch de Decky. Si aún
usas el plugin antiguo, actualiza a la 0.3.4 y después migra a Yby eGPU.
- Alta: root ejecutaba un archivo que el usuario podía cambiar. El
plugin se ejecuta como root y ejecutabaall-ways-egpudesde~/bin(y
desde el/home/*/binde cualquier usuario) cada pocos segundos.
Cualquier programa del usuario podía sustituir ese archivo y obtener root
sin contraseña. Ahora el plugin y el gancho de apagado deextras/solo
ejecutan all-ways-egpu cuando el archivo y todas las carpetas por encima
pertenecen a root y nadie más puede modificarlos. Si el tuyo está en
~/bin, el panel muestra el comando para instalarlo en un directorio del
sistema. - Media: root seguía un enlace al guardar los ajustes.
settings.json
está en una carpeta del usuario; sustituirlo por un enlace hacía que root
sobrescribiera cualquier archivo del sistema, y la pantalla de ajustes
podía leer cualquier archivo JSON. Ahora los ajustes se leen y escriben sin
seguir enlaces, mediante un archivo temporal, y solo se conservan las dos
opciones conocidas. - Baja: nombres fijos en
/tmp. El gancho de apagado escribía errores
en archivos predecibles de/tmpcomo root. Ya no usa/tmp. - Baja: la búsqueda profunda podía elegir un puente de otro dominio PCI.
Ahora comprueba también el dominio.
简体中文
这是 egpu-switch 的最后一个版本。 项目以
Yby eGPU 的形式继续,它保留了
egpu-switch 的 GPU 切换和安全弹出功能,增加了桌面应用等功能,并且不再依赖
all-ways-egpu。安装程序会把
egpu-switch 移出 Decky。正在使用 egpu-switch 的用户请立即更新到 0.3.4,并迁移
到 Yby eGPU。
- 高危:root 会执行用户可修改的文件。 插件以 root 身份运行,每隔几秒就会从
~/bin(以及任意用户的/home/*/bin)执行all-ways-egpu。任何以用户身份
运行的程序都能替换该文件并无需密码获得 root 权限。现在插件以及extras/中的
关机钩子,只有在该文件及其所有上级目录都属于 root 且他人无法写入时才会执行
all-ways-egpu。如果你的文件在~/bin,面板会显示将其安装到系统范围的命令。 - 中危:root 保存设置时会跟随链接。
settings.json位于用户拥有的目录中;
将其替换为链接会让 root 覆盖系统中的任意文件,设置界面也能读取任意 JSON
文件。现在读写设置时不跟随链接,通过临时文件写入,并且只保留两个已知选项。 - 低危:
/tmp中的固定文件名。 关机钩子以 root 身份把错误写入/tmp中
可预测的文件。现在不再使用/tmp。 - 低危:深度重新扫描可能选中其他 PCI 域的桥。 现在也会核对域。
Français
Ceci est la dernière version d'egpu-switch. Le projet continue sous le
nom Yby eGPU, qui conserve le basculement
de GPU et l'éjection sûre, ajoute une application de bureau et d'autres
commandes, sans dépendre d'all-ways-egpu. Son installateur retire
egpu-switch de Decky. Si vous utilisez egpu-switch, mettez à jour vers la
0.3.4 maintenant, puis passez à Yby eGPU.
- Élevée : root exécutait un fichier modifiable par l'utilisateur. Le
plugin tourne en root et exécutaitall-ways-egpudepuis~/bin(et
depuis le/home/*/binde n'importe quel utilisateur) toutes les quelques
secondes. N'importe quel programme de l'utilisateur pouvait remplacer ce
fichier et obtenir root sans mot de passe. Le plugin et le hook
d'extinction dansextras/n'exécutent désormais all-ways-egpu que si le
fichier et tous les dossiers au-dessus appartiennent à root et que
personne d'autre ne peut les modifier. Si le vôtre est dans~/bin, le
panneau affiche la commande pour l'installer dans un répertoire système. - Moyenne : root suivait un lien en enregistrant les paramètres.
settings.jsonse trouve dans un dossier de l'utilisateur ; le remplacer
par un lien faisait écraser par root n'importe quel fichier du système, et
l'écran des paramètres pouvait lire n'importe quel fichier JSON. Les
paramètres sont désormais lus et écrits sans suivre de lien, via un
fichier temporaire, et seules les deux options connues sont conservées. - Faible : noms de fichiers fixes dans
/tmp. Le hook d'extinction
écrivait ses erreurs dans des fichiers prévisibles de/tmpen root. Il
n'utilise plus/tmp. - Faible : la recherche approfondie pouvait choisir un pont d'un autre
domaine PCI. Elle vérifie désormais aussi le domaine.
Download: egpu-switch.zip below (Decky: Settings, Developer, Install Plugin from ZIP File).
eGPU Switch v0.3.3 (stable)
First stable release since v0.1.0, with everything learned from real hardware testing on an AyaNeo (AMD Ryzen 7840U) and an ROG Ally X folded in.
Highlights since v0.1.0
- Safe eject flow: unloads NVIDIA modules and removes the eGPU from the PCI bus before you disconnect the Thunderbolt cable, instead of only switching boot VGA.
- Deep Rescan option for cases where a plain PCI rescan does not bring the eGPU back after reconnecting.
- Automatic HDMI audio rebind after a rescan re-adds the eGPU, gated on actual ELD health instead of a fixed sleep.
- Connection diagnostics panel: connection type (Thunderbolt or PCIe), link speed and width, Thunderbolt controller and peripheral names, and boot/sleep freeze fix status.
- Friendly GPU and Thunderbolt controller names parsed from machine-readable lspci and boltctl output, picking the authorized peripheral instead of just the first one listed.
- Settings persistence for the auto-eject and deep rescan toggles.
- Documented fix for AMD platforms that freeze on boot or sleep/wake with the eGPU connected (thunderbolt.host_reset=0 kernel argument).
- Standalone shutdown-eject hook, distributed separately from the plugin, for systems that freeze on shutdown/reboot with the eGPU still attached.
- Dependency-free self-check test suite covering the pure parsing and decision functions.
Installation
Download egpu-switch.zip below and install via Decky Loader: Settings -> Developer -> Install Plugin from ZIP File. Requires all-ways-egpu to be installed and configured first (see README).
v0.3.3 (dev, unstable)
Unstable dev-branch build, not the recommended release for most users (use v0.1.0 for that). Published specifically so testers on different hardware can try recent fixes and new options.
Changed since v0.3.2
- Fixed a real audio bug: eject the eGPU, then click Switch to eGPU directly (without a manual Rescan first) could bring video back on the TV with no audio, and restarting the display manager didn't help. The plugin used to decide whether to repair audio based on whether a rescan had been "needed" for the video side, which is the wrong signal: automatic Thunderbolt hotplug can re-add the eGPU (video and audio both) before you press anything, so the video side already looked fine while the audio codec was still broken from that same re-enumeration. It now checks the ALSA card's own ELD state directly and repairs audio whenever it's actually broken, regardless of what triggered the reconnect. Confirmed fixed on hardware, and it's cheap: when audio is already fine, the check is a couple of instant file reads with no added delay.
- New doc: if the whole system hangs on boot with the eGPU already connected, or on wake from sleep with it active, see the README section on
thunderbolt.host_reset=0. Confirmed on hardware (AMD Ryzen 7840U) to fix both outright, not just work around them. Includes therpm-ostree kargscommands for Bazzite/SteamOS-style atomic systems.
Install
Same as the stable release: Desktop Mode → download the zip below → Game Mode → Decky Developer Mode → Install Plugin from ZIP File.
v0.3.2 (dev, unstable)
Unstable dev-branch build, not the recommended release for most users (use v0.1.0 for that). Published specifically so testers on different hardware can try recent fixes and new options.
Changed since v0.3.1
- Data loss guard: nothing previously checked for mounted storage before detaching PCI devices. Some NVIDIA boards expose their own USB-C controller as a sibling function, and Deep rescan removes an entire parent bridge, so an enclosure carrying an NVMe or a USB storage controller next to the GPU was one eject away from losing it mid-write. Eject and Deep rescan now refuse upfront (before the display manager is even touched) when mounted storage sits behind the target.
- Concurrency fix:
rescan_pcinow takes the same operation lock as every other action. It had grown from a single, safe sysfs write into an operation that can remove a PCI bridge, reauthorize a Thunderbolt tunnel and rebind a driver, while still running unserialized, so closing and reopening the QAM mid-operation could let two mutating calls run at once. Confirmed on hardware after the fix: a real concurrent request was caught and rejected, and a retry right after ran cleanly. - Lock rejections are now logged, naming which action was rejected, instead of being silently swallowed.
- Frontend polling now pauses during an operation, instead of hammering
all-ways-egpu statusas root every 5 seconds while a multi-minute operation is racing that same state. _unloadnow restarts the display manager if a Decky-initiated reload lands mid-operation, instead of leaving the session stopped with no code left running to bring it back.- Thunderbolt tunnel reauthorization fix: the experimental logic added to work around a reported reconnect issue had an anchored regex that could never match
boltctl list's actual output (a silent no-op), and it authorized every unauthorized device it found rather than only ones the user had previously approved. Both fixed. - Audio rebind now waits for a real display connection (a DRM connector reporting "connected") instead of a blind timed sleep, so it can only ever land later than the timing already validated on hardware, never earlier.
- Added
tests/selfcheck.py, a dependency-free self-check for the three pieces of logic where being wrong is expensive: the storage guard, the Thunderbolt authorization parser, and theall-ways-egpu statusparsing everything else reads from. - Packaging: committed the
pnpm-lock.yamlthe plugin store's build requires, and added thepublishmetadata block toplugin.json.
Install
Same as the stable release: Desktop Mode → download the zip below → Game Mode → Decky Developer Mode → Install Plugin from ZIP File.
v0.3.1 (dev, unstable)
Unstable dev-branch build, not the recommended release for most users (use v0.1.0 for that). Published specifically so testers on different hardware can try recent fixes and new options.
Changed since v0.3.0
- Fixed the Deep rescan safety check. Tester logs (Strix Halo / CachyOS Handheld) showed that after an eject, the system can automatically re-add the eGPU as a broken, half-initialized device: it exists on the PCI bus but is not actually usable (no working
boot_vga, the samebridge window ... can't assign; no spaceallocation issue). The previous check saw "device exists" and skipped the deep path exactly when it was needed, which also made Switch to eGPU give up without doing anything. Deep rescan now triggers wheneverall-ways-egpudoes not report the eGPU as connected and no driver is bound to it, covering both "missing from the bus" and "re-added broken". - With that fix, the expected recovery flow after a disconnect (or after waking from sleep with the eGPU previously ejected) is: Rescan for eGPU, then Switch to eGPU, all without leaving Game Mode and without rebooting. Log lines to look for in
journalctl -u plugin_loader | grep -i "deep rescan":remove bridge ... ok=True(deep path ran),eGPU connected and functional, skipping bridge removal(nothing to fix), orfunctions still driver-bound (...), skipping bridge removal(safety stop).
Install
Same as the stable release: Desktop Mode → download the zip below → Game Mode → Decky Developer Mode → Install Plugin from ZIP File.
v0.3.0 (dev, unstable)
Unstable dev-branch build, not the recommended release for most users (use v0.1.0 for that). Published specifically so testers on different hardware can try recent fixes and new options.
Changed since v0.2.0
- New "Deep rescan (experimental)" toggle under Advanced, off by default. On some platforms the eGPU's PCI bridge (the Thunderbolt/USB4 tunnel's downstream port) gets sized too small at boot for the eGPU to fit once hot-added, so a plain rescan can never bring it back after an eject or disconnect:
dmesgshowsbridge window ...: can't assign; no space(reported on a ROG Xbox Ally X + Strix Halo running CachyOS Handheld). When enabled, both Rescan for eGPU and the automatic pre-rescan in Switch to eGPU first remove and re-add that parent bridge (discovered dynamically from the configured bus ID, nothing hardcoded) so the kernel recomputes the window. The bridge removal only runs when the eGPU is actually absent from the PCI bus; with the card present it falls back to a plain rescan, since removing the bridge under a live, driver-bound GPU was confirmed on hardware to fail (NVRM: ... non-zero usage count) and wedge the operation. - The session now always comes back after a failed eject. If a module unload or PCI remove stalls inside the kernel (seen on hardware when the nvidia driver was already in a bad state from an earlier failure), the operation now times out after 90s, restarts the display manager regardless, and reports a clear "reboot required" error. Previously this could leave the screen black with no recovery.
- The UI can no longer get permanently stuck on a wedged operation: every action now has a 150s timeout that re-enables the buttons and shows a recovery hint, instead of leaving them disabled forever.
- Documented a known limitation: after eject + reconnect, the eGPU's HDMI audio can re-probe as non-operational (
snd_hda_intel ... please add a quirk to driver_denylist) until a reboot. This is a kernel limitation with re-hotplugged GPU audio; video is unaffected.
Install
Same as the stable release: Desktop Mode → download the zip below → Game Mode → Decky Developer Mode → Install Plugin from ZIP File.
If you're the tester with the "no space" bridge issue: enable Advanced → Deep rescan (experimental) and try the flow that previously failed (eject → disconnect → reconnect → Rescan/Switch to eGPU). journalctl -u plugin_loader will show either Deep rescan: remove bridge ... or ... skipping bridge removal depending on the state, and that log output is exactly what we'd like to see either way.
v0.2.0 (dev, unstable)
Unstable dev-branch build, not the recommended release for most users (use v0.1.0 for that). Published specifically so testers on different hardware can try recent fixes and new options.
Changed since v0.1.0
- Clearer safety indicator: the "Thunderbolt cable" badge now distinguishes "Not safe (in use)" (eGPU actively driving the display) from "Not safe (not ejected)" (eGPU connected but idle, never activated or already switched away from), each with a short explanation. Previously both cases just showed a bare "Not safe" with no context, which was confusing if you'd never activated the eGPU yet, it's still correct that it's not safe (gamescope keeps GPU device nodes open regardless of which one is active), but the reason wasn't visible before.
- New collapsed "Advanced" section: an Automatic eject (experimental) toggle, off by default. When enabled, "Switch to iGPU" also ejects the eGPU automatically in the same operation. Off by default because that longer operation can occasionally collide with Decky Loader's own plugin-reload grace period; see the README for details.
- New collapsed "Connection" section: shows negotiated PCIe link speed/width (works for OCuLink too, not Thunderbolt-specific) plus Thunderbolt generation/tunnel speed/controller name when available via
boltctl. - Fixed a bug where
all-ways-egpu's own internal retry messages could be misread as a failure, silently skipping the display manager restart. - Retries
modprobe -ron a transient "module in use" failure seen right after the display manager stops. - "Switch to eGPU" now always triggers a PCI rescan first, so it can find an eGPU that was just ejected without a separate manual rescan step.
Install
Same as the stable release: Desktop Mode → download the zip below → Game Mode → Decky Developer Mode → Install Plugin from ZIP File.
v0.1.0
Early, experimental release of eGPU Switch, a Decky Loader plugin that automates all-ways-egpu from Deck Mode.
Marked as a prerelease (0.x per SemVer): validated on one specific hardware/OS combination so far, behavior may still change. See the README's Compatibility section for what is tested vs. expected-but-unconfirmed.
Install
The handheld itself is all you need, no second computer or SSH required:
- Switch to Desktop Mode and download
egpu-switch.zipbelow with a browser. - Switch back to Game Mode.
- In Decky, enable Developer Mode (Settings, General) if not already on.
- Settings, Developer, Install Plugin from ZIP File, Browse, pick the ZIP from Downloads.
See the README for the SSH-based deploy alternative (useful for development) and full usage/safety notes.
Features
- Switch to eGPU / Switch to iGPU: toggles boot VGA and restarts the display manager. Switching to iGPU also safely ejects the eGPU (unloads NVIDIA kernel modules, detaches it from the PCI bus) in the same operation.
- Eject eGPU: standalone safe-eject action for the edge case where iGPU is already active but the eGPU was never ejected.
- Restart Display Manager and Rescan for eGPU: recovery actions.
- Always-visible Safe/Not safe indicator showing whether it is currently safe to disconnect the Thunderbolt cable.
Why the eject step exists
The proprietary NVIDIA driver does not support surprise PCIe hot-unplug. Disconnecting the Thunderbolt cable while the kernel modules are still loaded can crash the graphical session with a kernel BUG(), confirmed via dmesg during development. Ejecting first (unload modules, detach PCI functions) avoids this.