f8ead215d8
build-windows / build-hello-agent-x64 (push) Successful in 5m41s
A single-binary, Flutter-free remote-support agent that speaks the stock
RustDesk wire protocol. Designed for one-line MDM deployment against a
self-hosted rustdesk-server: a supporter using the unmodified rustdesk.exe
client connects, the controlled-side user gets a native Win32 approval
prompt, click Yes / No.
CLI surface
hello-agent.exe --install # register + start service
hello-agent.exe --uninstall # stop, delete, clean up
hello-agent.exe --config <BLOB> # admin-UI deploy string
hello-agent.exe --install --config <BLOB> # MDM one-liner
--config accepts both forms emitted by the rustdesk-server admin UI: the
reversed-base64 deploy string and the host=,key=,api=,relay= filename
form. Decoded via the upstream custom_server module, persisted via
hbb_common::config::Config::set_option.
Architecture
--service runs as a Session 0 LocalSystem service. It polls
WTSGetActiveConsoleSessionId and (re)spawns hello-agent.exe --server
into the active console session via librustdesk::platform::run_as_user,
handling the Session 0 → user-session token impersonation.
--server is the worker. It boots three concurrent components:
1. cm_popup: an IPC listener on the rustdesk `_cm` named pipe
2. librustdesk::start_server(true, false): the upstream protocol
stack — rendezvous mediator, NAT punch, IPC server, screen
capture, login validation, hbbs_http heartbeat / sysinfo sync
3. (implicit) ApproveMode::Click is pinned in config, so every
incoming connection routes through cm_popup
The popup mechanism reuses an existing upstream contract without any
patches to the protocol code: when a peer connects with no password,
Connection::start in the upstream code calls try_start_cm_ipc, which
ipc::connect-s the `_cm` pipe before falling back to spawning a Flutter
CM child. Since cm_popup is up first, step 1 succeeds; we read the
Data::Login{authorized:false} frame, show MessageBoxTimeoutW (Yes/No,
60s, top-most, system-modal), and reply Data::Authorize or Data::Close.
Source tree
src/main.rs CLI dispatcher + run_server() composition
src/cli.rs hand-rolled argv parser + unit tests
src/service.rs windows-service install/uninstall/dispatcher
src/config_import.rs --config blob decoding + persistence
src/cm_popup.rs _cm IPC listener + Win32 approval dialog
Vendoring
The upstream RustDesk crate is vendored under vendor/rustdesk/ — full
workspace including libs/{hbb_common, scrap, enigo, clipboard,
virtual_display, remote_printer}. This makes the build self-contained
(no submodules, no sibling-repo checkout in CI) and gives us freedom to
fork in a different direction later. Excluded from the vendor: .git,
target/, flutter/, appimage/, flatpak/, fastlane/, docs/, examples/,
ci/, build.py, Dockerfile, upstream README/CLAUDE/AGENTS/GEMINI.
One local divergence vs. upstream: vendor/rustdesk/src/lib.rs flips
`mod custom_server` → `pub mod custom_server` so config_import.rs can
call get_custom_server_from_string without going through the
ui_interface shim. Documented in README.md → "Re-syncing the vendored
copy".
CI
.gitea/workflows/build-windows.yml builds on a self-hosted Windows
runner with Rust 1.75, LLVM 15.0.6 (libclang for bindgen via libvpx-sys),
and a vcpkg cache. The vendored vcpkg.json drives x64-windows-static
deps. The workflow stages the resulting hello-agent.exe into
SignOutput\, reports authenticode signing status (warns on unsigned),
and uploads as artifact. ~15 min full build, faster on incremental.
Out of scope for this commit: Linux/macOS builds, code signing, MSI
packaging, coexistence with stock rustdesk on the same box (currently
shares the RustDesk APP_NAME and config dir).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
84 lines
6.3 KiB
XML
84 lines
6.3 KiB
XML
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
|
<Fragment>
|
|
|
|
<?include ..\Includes.wxi?>
|
|
|
|
<!--
|
|
Properties and related actions for specifying whether to install start menu/desktop shortcuts.
|
|
-->
|
|
|
|
<!-- These are the actual properties that get used in conditions to determine whether to
|
|
install start menu shortcuts, they are initialized with a default value to install shortcuts.
|
|
They should not be set directly from the command line or registry, instead the CREATE* properties
|
|
below should be set, then they will update these properties with their values only if set. -->
|
|
<Property Id="STARTMENUSHORTCUTS" Value="1" Secure="yes"></Property>
|
|
<Property Id="DESKTOPSHORTCUTS" Value="1" Secure="yes"></Property>
|
|
<Property Id="STARTUPSHORTCUTS" Value="1" Secure="yes"></Property>
|
|
<Property Id="PRINTER" Value="1" Secure="yes"></Property>
|
|
|
|
<!-- These properties get set from either the command line, bundle or registry value,
|
|
if set they update the properties above with their value. -->
|
|
<Property Id="CREATESTARTMENUSHORTCUTS" Secure="yes">
|
|
<RegistrySearch Id="CreateStartMenuShortcutsSearch" Root="HKCR" Key="$(var.RegKeyRoot)" Name="STARTMENUSHORTCUTS" Type="raw" />
|
|
</Property>
|
|
<Property Id="CREATEDESKTOPSHORTCUTS" Secure="yes">
|
|
<RegistrySearch Id="CreateDesktopShortcutsSearch" Root="HKCR" Key="$(var.RegKeyRoot)" Name="DESKTOPSHORTCUTS" Type="raw" />
|
|
</Property>
|
|
<Property Id="INSTALLPRINTER" Secure="yes">
|
|
<RegistrySearch Id="InstallPrinterSearch" Root="HKCR" Key="$(var.RegKeyRoot)" Name="PRINTER" Type="raw" />
|
|
</Property>
|
|
|
|
<!-- Component that persists the property values to the registry so they are available during an upgrade/modify -->
|
|
<DirectoryRef Id="INSTALLFOLDER_INNER">
|
|
<Component Id="Product.Registry.PersistedStartMenuShortcutProperties1" Guid="62F79BCF-3367-4ACF-950F-F8BCABACDDC0" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = "Y" OR STARTMENUSHORTCUTS = "y"">
|
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
|
<RegistryValue Type="string" Name="STARTMENUSHORTCUTS" Value="1" KeyPath="yes" />
|
|
</RegistryKey>
|
|
</Component>
|
|
<Component Id="Product.Registry.PersistedStartMenuShortcutProperties0" Guid="8EA2D5A8-6E5D-4BDD-9019-2099297FF519" Condition="NOT (STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = "Y" OR STARTMENUSHORTCUTS = "y")">
|
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
|
<RegistryValue Type="string" Name="STARTMENUSHORTCUTS" Value="0" KeyPath="yes" />
|
|
</RegistryKey>
|
|
</Component>
|
|
<Component Id="Product.Registry.PersistedDesktopShortcutProperties1" Guid="1BBAD054-6EC2-4362-BF1B-E8BDE988B597" Condition="DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = "Y" OR DESKTOPSHORTCUTS = "y"">
|
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
|
<RegistryValue Type="string" Name="DESKTOPSHORTCUTS" Value="1" />
|
|
</RegistryKey>
|
|
</Component>
|
|
<Component Id="Product.Registry.PersistedDesktopShortcutProperties0" Guid="FA992614-D2E1-4795-9696-D45A5EF1B9C8" Condition="NOT (DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = "Y" OR DESKTOPSHORTCUTS = "y")">
|
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
|
<RegistryValue Type="string" Name="DESKTOPSHORTCUTS" Value="0" />
|
|
</RegistryKey>
|
|
</Component>
|
|
<Component Id="Product.Registry.PersistedPrinterProperties1" Guid="AF617116-2502-EB3D-5B52-B47AA89EB4B0" Condition="PRINTER = 1 OR PRINTER = "Y" OR PRINTER = "y"">
|
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
|
<RegistryValue Type="string" Name="PRINTER" Value="1" />
|
|
</RegistryKey>
|
|
</Component>
|
|
<Component Id="Product.Registry.PersistedPrinterProperties0" Guid="51F944D3-AAEB-F167-03A1-081A38E9468A" Condition="NOT (PRINTER = 1 OR PRINTER = "Y" OR PRINTER = "y")">
|
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
|
<RegistryValue Type="string" Name="PRINTER" Value="0" />
|
|
</RegistryKey>
|
|
</Component>
|
|
</DirectoryRef>
|
|
|
|
<!-- If a property value has been passed via the command line (which includes when set from the bundle), the registry search will
|
|
overwrite the command line value, these actions temporarily store the command line value before the registry search
|
|
is performed so they can be restored after the registry search is complete -->
|
|
<SetProperty Id="SavedStartMenuShortcutsCmdLineValue" Value="[CREATESTARTMENUSHORTCUTS]" Before="AppSearch" Sequence="first" Condition="CREATESTARTMENUSHORTCUTS" />
|
|
<SetProperty Id="SavedDesktopShortcutsCmdLineValue" Value="[CREATEDESKTOPSHORTCUTS]" Before="AppSearch" Sequence="first" Condition="CREATEDESKTOPSHORTCUTS" />
|
|
<SetProperty Id="SavedPrinterCmdLineValue" Value="[INSTALLPRINTER]" Before="AppSearch" Sequence="first" Condition="INSTALLPRINTER" />
|
|
|
|
<!-- If a command line value was stored, restore it after the registry search has been performed -->
|
|
<SetProperty Action="RestoreSavedStartMenuShortcutsValue" Id="CREATESTARTMENUSHORTCUTS" Value="[SavedStartMenuShortcutsCmdLineValue]" After="AppSearch" Sequence="first" Condition="SavedStartMenuShortcutsCmdLineValue" />
|
|
<SetProperty Action="RestoreSavedDesktopShortcutsValue" Id="CREATEDESKTOPSHORTCUTS" Value="[SavedDesktopShortcutsCmdLineValue]" After="AppSearch" Sequence="first" Condition="SavedDesktopShortcutsCmdLineValue" />
|
|
<SetProperty Action="RestoreSavedPrinterValue" Id="INSTALLPRINTER" Value="[SavedPrinterCmdLineValue]" After="AppSearch" Sequence="first" Condition="SavedPrinterCmdLineValue" />
|
|
|
|
<!-- If a command line value or registry value was set, update the main properties with the value -->
|
|
<SetProperty Id="STARTMENUSHORTCUTS" Value="" After="RestoreSavedStartMenuShortcutsValue" Sequence="first" Condition="CREATESTARTMENUSHORTCUTS AND NOT (CREATESTARTMENUSHORTCUTS = 1 OR CREATESTARTMENUSHORTCUTS = "Y" OR CREATESTARTMENUSHORTCUTS = "y")" />
|
|
<SetProperty Id="DESKTOPSHORTCUTS" Value="" After="RestoreSavedDesktopShortcutsValue" Sequence="first" Condition="CREATEDESKTOPSHORTCUTS AND NOT (CREATEDESKTOPSHORTCUTS = 1 OR CREATEDESKTOPSHORTCUTS = "Y" OR CREATEDESKTOPSHORTCUTS = "y")" />
|
|
<SetProperty Id="PRINTER" Value="" After="RestoreSavedPrinterValue" Sequence="first" Condition="INSTALLPRINTER AND NOT (INSTALLPRINTER = 1 OR INSTALLPRINTER = "Y" OR INSTALLPRINTER = "y")" />
|
|
|
|
</Fragment>
|
|
</Wix>
|