Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,37 @@ This script automatically downloads and installs everything:
| .NET 9 SDK | Required only to **build from source**. Not needed with `-FromZip`, which installs a self-contained release asset. |
| Node.js LTS | Required only to **build the dashboard from source**. Not needed with `-FromZip`, which ships it prebuilt. |

#### Optional: USB forwarding driver (opt-in, off by default)

ApolloVibe can forward a USB device from a Moonlight client to the host. The host then needs the
[usbip-win2](https://github.com/vadimgrn/usbip-win2) USB/IP kernel driver. It is **never** installed by a
default or unattended run. You ask for it:

```powershell
.\prerequisites\install-prerequisites.ps1 -WithUsbForwarding
.\prerequisites\install-usbip-driver.ps1 -WhatIf # print the steps, change nothing
```

- **Pinned and checked.** One release (`v.0.9.8.1`), downloaded over HTTPS, verified against a fixed SHA-256 and
its Authenticode signature before it runs, then installed silently with `/NORESTART`.
- **Microsoft-signed drivers only.** After the install the script checks every installed `usbip2` kernel driver
file. If any is not signed by "Microsoft Windows Hardware Compatibility Publisher", it stops, enables nothing
and exits with code 2. Each file must be signed by "Microsoft Windows Hardware Compatibility Publisher", chain to
Microsoft Root Certificate Authority 2010 and carry a hardware-driver usage. **Exit code 2 means the driver package
is already on the machine and will load at the next boot: do not reboot until you have removed it.** The script
prints the removal steps but never runs them, and no reboot prompt is shown. It refuses to run while Windows
test-signing mode or disabled integrity checks are on, or if that state cannot be read, and it never turns them on.
- **Private staging.** The installer is downloaded into a new folder under `%ProgramData%` that only SYSTEM and
Administrators can open, and the verified file stays locked while the installer runs.
- **A reboot is needed** before the driver works. The script never reboots the machine; that is your decision.
- **Seats keep USB forwarding off.** MultiSeat writes `usb_forwarding_enabled = disabled` into every seat's
Apollo config, because a forwarded device is machine-wide, not per session. Only the console ApolloVibe can
use it, and only when you enable it there.
- **Port reservation.** ApolloVibe's USB tunnel listens on its main port + 7 (TCP). That offset is reserved in
every seat's port block and checked against all other ports, but MultiSeat does not open it in the firewall.
That check assumes the default console Apollo port (47989) and `PortBase` (48100). If you override either in
config, re-check that the console Apollo's ports (its main port -5 to +26) stay outside the seat blocks.

It also enables Remote Desktop and opens the required firewall ports automatically.

> **Reboot** when prompted — HidHide and RDPWrap require it before the service will work.
Expand Down
5 changes: 5 additions & 0 deletions prerequisites/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ Notes:

To use offline/local files instead of downloading, place the installer files in
this folder before running the script — it will use them as-is.

Optional, off by default:
install-prerequisites.ps1 -WithUsbForwarding - installs the pinned usbip-win2 USB/IP driver (host side of
USB forwarding). Only if its kernel drivers are Microsoft-signed; otherwise it stops. Never reboots for you.
Preview with: install-usbip-driver.ps1 -WhatIf
47 changes: 46 additions & 1 deletion prerequisites/install-prerequisites.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@
Suppress the reboot prompt at the end.
.PARAMETER SkipDownload
Skip downloading missing files; only install what is already present.
.PARAMETER WithUsbForwarding
OPT-IN, off by default, never part of an unattended run. Also installs the pinned usbip-win2
USB/IP kernel driver that the HOST needs for USB forwarding (see install-usbip-driver.ps1).
Used only while the driver is Microsoft-signed; otherwise it stops and exits non-zero. The driver
needs a reboot, which this script never performs for it - that is your decision.
.PARAMETER UsbForwardingDryRun
With -WithUsbForwarding: print the USB driver steps and do none of them. To dry-run ONLY that
step, run install-usbip-driver.ps1 -WhatIf directly.
.PARAMETER Seats
Number of seats to provision audio cables for. Defaults to 4.
Each seat needs one VB-CABLE device. Only meaningful under SharedHost audio.
Expand All @@ -30,6 +38,8 @@
param(
[switch]$SkipReboot,
[switch]$SkipDownload,
[switch]$WithUsbForwarding,
[switch]$UsbForwardingDryRun,
[int]$Seats = 4,
[ValidateSet('PerSession', 'SharedHost')]
[string]$AudioMode
Expand Down Expand Up @@ -60,6 +70,8 @@ if (-not [Environment]::Is64BitProcess -and [Environment]::Is64BitOperatingSyste
$Fwd = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $PSCommandPath)
if ($SkipReboot) { $Fwd += '-SkipReboot' }
if ($SkipDownload) { $Fwd += '-SkipDownload' }
if ($WithUsbForwarding) { $Fwd += '-WithUsbForwarding' }
if ($UsbForwardingDryRun) { $Fwd += '-UsbForwardingDryRun' }
if ($AudioMode) { $Fwd += @('-AudioMode', $AudioMode) }
$Fwd += @('-Seats', $Seats)
& $NativeHost @Fwd
Expand Down Expand Up @@ -1473,6 +1485,29 @@ if (-not (Get-NetFirewallRule -DisplayName "MultiSeat Streaming" -ErrorAction Si
Write-OK "Firewall: streaming ports already open"
}

# ----------------------------------------------------------------
# 10. USB forwarding driver (usbip-win2) -- OPT-IN ONLY
# ----------------------------------------------------------------
# Runs only with -WithUsbForwarding. Never part of a default or unattended run. It does not set
# $NeedsReboot and never reboots: the driver needs one, and that is the user's decision.
$UsbipExit = 0
if ($UsbForwardingDryRun -and -not $WithUsbForwarding) {
Write-Host "`n NOTE: -UsbForwardingDryRun does nothing without -WithUsbForwarding; the USB forwarding step was not run." -ForegroundColor Yellow
}
if ($WithUsbForwarding) {
Write-Step "USB forwarding driver (usbip-win2, opt-in)"
. (Join-Path $ScriptDir 'lib\usbip-driver-lib.ps1')
$usbip = Invoke-UsbipDriverInstall -DryRun:$UsbForwardingDryRun
$UsbipExit = $usbip.ExitCode
if ($UsbipExit -eq 0 -and $usbip.Status -eq 'Installed') {
$Installed += "usbip-win2 driver (REBOOT needed before it works; your decision)"
} elseif ($UsbipExit -eq 2) {
$Skipped += "usbip-win2 driver package is INSTALLED but NOT VERIFIED Microsoft-signed - do not reboot until it is removed (steps printed above)"
} elseif ($UsbipExit -ne 0) {
$Skipped += "usbip-win2 driver NOT installed or NOT trusted ($($usbip.Status)); see the messages above"
}
}

# ----------------------------------------------------------------
# Summary
# ----------------------------------------------------------------
Expand All @@ -1490,7 +1525,16 @@ if ($Skipped.Count -gt 0) {
$Skipped | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow }
}

if ($NeedsReboot -and -not $SkipReboot) {
if ($UsbipExit -eq 2) {
# The USB forwarding driver package is on this machine and is NOT verified Microsoft-signed.
# Never offer a reboot here, even when another step needs one: it would load that driver.
Write-Host "`n DO NOT REBOOT yet." -ForegroundColor Red
Write-Host " The USB forwarding driver installed above is not verified as Microsoft-signed and will load at the next boot." -ForegroundColor Red
Write-Host " Remove it first (the removal steps are printed above), then reboot. No reboot prompt is shown." -ForegroundColor Red
if ($NeedsReboot) {
Write-Host " (Another step also needs a reboot; that waits until the driver is dealt with.)" -ForegroundColor Magenta
}
} elseif ($NeedsReboot -and -not $SkipReboot) {
Write-Host "`n A REBOOT is required before continuing." -ForegroundColor Magenta
# Read-Host THROWS under -NonInteractive, which failed the whole script at the very last step
# even when every install before it succeeded — so a scripted or CI run reported failure for a
Expand All @@ -1512,3 +1556,4 @@ Write-Host " Log written to: $LogFile" -ForegroundColor DarkGray
Write-Host ""

Stop-Transcript | Out-Null
if ($UsbipExit -ne 0) { exit $UsbipExit }
28 changes: 28 additions & 0 deletions prerequisites/install-usbip-driver.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<#
.SYNOPSIS
OPT-IN install of the pinned usbip-win2 USB/IP kernel driver (needed on the HOST for USB forwarding).
.DESCRIPTION
Not part of any default or unattended run. install-prerequisites.ps1 calls this only when you pass
-WithUsbForwarding.

Pinned release: vadimgrn/usbip-win2 v.0.9.8.1, asset USBip-0.9.8.1-x64.exe, SHA-256 checked.

RULE: usbip-win2 is used ONLY while its kernel drivers are signed by Microsoft. The script checks the
installer's hash and signature before running it, then checks the installed drivers. If any driver is
not Microsoft-signed it stops, enables nothing and exits 2. It never turns on test-signing mode and
refuses to run while test-signing is on.

The script NEVER restarts the machine. The driver needs a reboot before it works; that is your call.
.PARAMETER WhatIf
Print every step and do nothing: no download, no install, no system query.
.NOTES
Exit codes: 0 ok (installed or already present), 1 refused or failed, 2 driver not Microsoft-signed.
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param()

$ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'lib\usbip-driver-lib.ps1')

$result = Invoke-UsbipDriverInstall -DryRun:([bool]$WhatIfPreference)
exit $result.ExitCode
Loading
Loading