Build your own branded RustDesk client on your computer β server, key, password, and permissions baked in. No GitHub. No cloud CI. No pip installs.
Open a local page in the browser Β βΒ pick a target Β βΒ hit build.
The finished installer lands in workspace/output/.
DVForge is a tiny, zero-dependency (Python standard-library only) local web app that compiles a customized RustDesk remote-desktop client on your own machine. You point it at your self-hosted RustDesk server, set an app name, icon, baked-in password and permission set, pick a target platform, and it produces a ready-to-distribute installer β a Windows .exe/.msi, a macOS .dmg, Linux .deb/.rpm/.AppImage, or an Android .apk. It performs the exact same source customizations a GitHub Actions pipeline would, but everything runs offline on 127.0.0.1, and the heavy build toolchains (Flutter, Rust, NDK, JDK, β¦) install into a private, project-local .toolchains/ folder β nothing touches your system.
New here? Jump to β Quick Start Β· How it works Β· FAQ Β· Troubleshooting
AI agent / LLM reading this repo? Jump to β π€ For AI agents & automated tools for a machine-oriented map of the whole codebase.
Click to expand / collapse
- Screenshots
- Why DVForge?
- Feature highlights
- Quick start (90 seconds)
- How it works
- Detailed installation
- Running the app
- Using the GUI
- Who can build what
- Android β which APK do I pick?
- Configuration reference
- Toolchains reference
- Code signing
- Output
- Build times (real-world)
- Multi-machine build farm
- Printer adapter (remote printing)
- Patches & tweaks reference
- Project layout
- API reference
- Environment variables & flags
- Troubleshooting
- Uninstall & clean
- FAQ
- Design principles
- For AI agents & automated tools
- Contributing
- Credits & license
- Community
Placeholders below β replace the files in
docs/images/with real captures. Seedocs/images/README.mdfor the exact shot list.
| Capability board | Baked-in config |
|---|---|
| The board lights only what this machine can build. | Everything that gets baked into the client. |
| Toolchain panel | Live build console |
|---|---|
| Detect, one-click install, on-disk sizes, remove. | Every command streamed live over SSE. |
RustDesk is fantastic, but distributing a client that already knows your server β with your branding, your baked-in password, and your permission policy β normally means running a CI pipeline on GitHub Actions. That's slow, public-ish, requires secrets in the cloud, and is a pain to iterate on.
DVForge removes GitHub from the loop entirely:
| The old way (cloud CI) | The DVForge way (local) |
|---|---|
| Push to GitHub, wait for Actions | Click Build, watch the log stream live |
| Secrets live in the cloud | Everything stays on 127.0.0.1 |
| One workflow file per platform | One folder, one UI, every platform |
| System-wide SDK installs | Portable .toolchains/ β nothing system-wide |
pip install a wall of deps |
Zero pip dependencies (stdlib only) |
| Opaque runner logs | Full command visibility + dry-run preview |
It's built for people who self-host RustDesk and want a client that already knows their server β MSPs, IT departments, homelab users, and anyone shipping branded remote-support tools.
- π₯οΈ Browser GUI, zero install β a Python stdlib HTTP server serves a local page at
http://127.0.0.1:8765. No Electron, no Node, no framework. - π§ Capability-aware board β auto-detects your OS/CPU and lights only the targets this machine can actually produce. Wrong-OS targets are visibly disabled, not silently failing.
- π¦ Portable toolchains β Flutter, Rust, LLVM, NDK, JDK, vcpkg, and more download into a project-local
.toolchains/folder. One click each, or "install missing." Delete the folder to reset. - π¨ Full branding β app name, company name, icon, logo, accent colors, light/dark theme, slogan, download/URL links β all baked in.
- π Baked-in security policy β server address, public key, API server, a permanent password, approve mode, and a per-feature permission matrix (keyboard, clipboard, file transfer, audio, recording, terminal, printer, camera, β¦).
- π Connection direction lock β ship an incoming-only host, an outgoing-only controller, or a full
bothclient. - π§© Feature patches β hide the connection manager, remove the update nag, strip the "set up your server" tip, hide offline peers, add a privacy screen, and more (toggle in the GUI).
- π± Android from Linux/macOS β cross-compile every ABI (
arm64-v8a,armeabi-v7a,x86_64, or a universal APK). - βοΈ Code signing β Windows Authenticode (PFX + timestamp), Android keystore, macOS Developer ID / notarization / self-signed
.p12. Or generate a self-signed cert from the UI for local tests. - π΅οΈ Dry-run / Preview plan β print every command that would run, without compiling. See the whole plan first.
- π‘ Live streaming logs β build output streams to the browser over Server-Sent Events; refresh-safe (logs replay).
- π Optional build farm β offload each OS's build to a machine that can actually do it (a Mac builds DMGs, a Windows box builds EXEs), over a shared folder or a small HTTP queue.
- π¨οΈ Open printer adapter β an included Rust crate that restores remote printing in custom builds (RustDesk's stock printer DLL refuses to run for non-RustDesk-signed executables).
- π§Ή Clean uninstall β dedicated uninstall/clean scripts per OS; your config and branding are preserved by default.
git clone https://github.com/VenimK/DVForge.git
cd DVForge| You are on | Run this |
|---|---|
π macOS (DMG; add Android with --with-android) |
bash Setup-DVForge-macOS.sh |
πͺ Windows (.exe / .msi) |
powershell -NoProfile -ExecutionPolicy Bypass -File .\Setup-DVForge-Windows.ps1 |
| π§ Windows + WSL2 (Linux packages + Android APKs) | powershell -NoProfile -ExecutionPolicy Bypass -File .\Setup-DVForge-WSL2.ps1 |
Already inside the cloned folder and don't want it copied elsewhere? macOS:
bash Setup-DVForge-macOS.sh --in-placeΒ· Windows: add-InPlace.You can run both the Windows and WSL2 installers on one PC β Windows for
.exe/.msi, WSL for Android + Linux packages.
# Linux / macOS
./run.sh
# Windows
run.batβ¦or just python3 app.py. The UI opens automatically at http://127.0.0.1:8765.
- Targets tab β click a lit target (e.g. Windows x86_64 (exe)).
- Config tab β set your server, key, app name, password, permissions.
- Build tab β hit Preview plan once to see the commands, then Build. Watch it stream.
Your installer appears in workspace/output/v1.4.9/. Done. π
DVForge is three cooperating layers:
web/β a hand-written browser GUI (HTML + CSS + vanilla JS, no framework) with a hardware-capability aesthetic. Three tabs: Targets, Config, Build, plus a left rail for toolchains and updates.app.pyβ aThreadingHTTPServerbuilt entirely on the Python standard library. It serves the static GUI, exposes a small JSON API, and fans out live build/install logs over Server-Sent Events (SSE). No Flask, no FastAPI, no pip.builder/β the engine. Detection, toolchain management, config generation, source customization, and build orchestration (see Project layout for the module map).
βββββββββββββββββ ββββββββββββββββ ββββββββββββββββββββββ
β 1. DETECT β βββΆ β 2. CONFIG β βββΆ β 3. BUILD β
β hardware & β β edit server β β checkout source, β
β OS β which β β key, brand, β β patch it, compile β
β targets are β β password, β β per-OS, collect β
β buildable β β permissions β β artifacts, stream β
βββββββββββββββββ ββββββββββββββββ ββββββββββββββββββββββ
detect.py config_gen.py orchestrator.py
prereqs.py customize.py
When you hit Build, orchestrator.py runs (roughly) this sequence:
1. Clean checkout β git clone RustDesk @ tag v1.4.9 into workspace/rustdesk-src
(any previous tree is removed first β customizations mutate it)
2. Apply patches β allowCustom (strip signature check), hidecm, xoffline,
removeNewVersionNotif, removeSetupServerTip, privacyScreen, β¦
3. Bake config β sed/patch server, key, API, app/company name, URLs, flags
into the RustDesk *source* (compiled-in customizations)
4. Emit custom_.txt β base64-encoded password + permissions (runtime config)
(Android: also embedded into MainService.kt + native_model.dart)
5. Bridge codegen β flutter_rust_bridge_codegen (local, 1.80.1)
6. Compile β cargo + flutter build (per target: exe/msi/dmg/deb/rpm/apk)
7. Collect β copy finished installers to workspace/output/v<version>/
Dry-run / Preview plan prints every command in this sequence without executing the compile β run it once to understand exactly what will happen on your machine.
Customizations land two different ways, and mixing them up is the classic footgun:
| Kind | What | When it's read | How |
|---|---|---|---|
| Compiled-in | Server IP, public key, API server, app/company name, URLs, feature flags | At build time | sed-patched into the RustDesk source before compiling |
| Runtime | Password, permissions, approve mode | At client startup | Read from a base64 custom_.txt file next to the binary |
β οΈ custom_.txtMUST be base64, not raw JSON. RustDesk'sread_custom_client()begins withdecode64().config_gen.pyemits the correct base64 payload (verified byte-for-byte against the originalload-config.py).π± Android never file-reads
custom_.txt. On Android the base64 config is embedded directly into native code (MainService.kt,native_model.dart) and bundled as a Flutter asset.customize.pyhandles this automatically.π The signature check.
allowCustom.pystrips a 9-line signature-verification block fromsrc/common.rsand renamescustom.txtβcustom_.txt, so your unsigned/custom build will actually load its baked-in config.
All setup scripts are idempotent (safe to re-run) and write a portable .toolchains/env.json so installed tools are picked up automatically on the next launch. Python 3.8+ is the only prerequisite you must have beforehand.
bash Setup-DVForge-macOS.sh # DMG toolchain
bash Setup-DVForge-macOS.sh --with-android # + JDK 17, NDK r28c, Android SDK (API 34) for APKs
bash Setup-DVForge-macOS.sh --in-place # use this folder, don't copy to ~/DVForge
bash Setup-DVForge-macOS.sh --skip-optional # skip sccache / ImageMagick / potraceInstalls / verifies: Xcode Command Line Tools (git, clang, iconutil), Homebrew, Python 3.8+, cmake, ninja, nasm, pkg-config, create-dmg, cocoapods, then via builder/toolchains.py: Rust 1.81 (macOS pin), Flutter 3.24.5, LLVM/libclang 15.0.6, vcpkg (pinned), sccache 0.11.0, ImageMagick, potrace. It pins rustup to 1.81-<host> and adds both Darwin targets so universal DMGs can lipo. Writes .toolchains/env.json + a DVForge block in ~/.zprofile.
# Default install β C:\DVForge (short path = reliable deep-path builds)
powershell -NoProfile -ExecutionPolicy Bypass -File .\Setup-DVForge-Windows.ps1
# Use the folder you already have open
powershell -NoProfile -ExecutionPolicy Bypass -File .\Setup-DVForge-Windows.ps1 -InPlace
# Skip the large (~4β6 GB) Visual Studio Build Tools install
powershell -NoProfile -ExecutionPolicy Bypass -File .\Setup-DVForge-Windows.ps1 -SkipVsBuildToolsInstalls / verifies: Git, Python 3 (via winget if missing), LongPathsEnabled (so Flutter/MSBuild deep paths don't break), then: Rust 1.75 (MSVC), Flutter 3.24.5, LLVM/libclang 15, vcpkg (pinned), VS Build Tools 2022 (the C++ workload β link.exe + MSBuild), .NET 8 SDK, NuGet, sccache 0.11.0, ImageMagick (official installer β .toolchains\imagemagick), and optionally JDK 17. Pins rustup to 1.75-x86_64-pc-windows-msvc.
π§± The MSVC linker is mandatory on Windows β for every target, including Android. Rust's Windows host toolchain is MSVC, and any
cargocompile needslink.exe. Until VS Build Tools (C++) are installed, board cells read "install first: msbuild." This one ~4β6 GB, admin-requiring piece is the only unavoidably heavy install; it's excluded from bulk "install missing" and requires an explicit click.
powershell -NoProfile -ExecutionPolicy Bypass -File .\Setup-DVForge-WSL2.ps1Sets up WSL + Debian, then installs Flutter / Android SDK+NDK / vcpkg / Rust / JDK / LLVM inside the Linux distro (~/DVForge), producing Android APKs + .deb/.rpm/.AppImage. Run this alongside the Windows script on the same PC to cover every target.
Clone the repo, ensure Python 3.8+, then ./run.sh and use the toolchain panel's install missing to fetch Flutter, Rust, LLVM, NDK, JDK, and vcpkg into .toolchains/. Produces .deb, .rpm, .AppImage, and Android APKs.
./run.sh # Linux / macOS
run.bat # Windows
python3 app.py # any OS, directFlags & environment:
| Option | Effect |
|---|---|
--no-browser |
Don't auto-open the browser (useful for headless / farm workers) |
RDLB_PORT=9000 |
Change the listen port (default 8765) |
The server binds to 127.0.0.1 only β it is not exposed to your network by default. (See Build farm for the safe way to accept remote jobs.)
Left rail β This machine + Toolchains. Shows your detected CPU/OS spec, plus every toolchain with its state, download/on-disk size, version, a per-tool remove (β), and a total local footprint. Install missing grabs everything installable in one go (except VS Build Tools, which needs an explicit click). Check for updates pulls a newer DVForge commit when you want it.
Targets tab β "What can this machine build?" A board of every RustDesk target. Cell states:
| State | Meaning |
|---|---|
| π Amber / ready | This machine can build it right now β click to select |
| β¬ Outline | Needs a tool β the cell says install first: <tool> |
| β¨ Hatched / disabled | Wrong OS (e.g. DMG on Windows) β can't build here |
Config tab β "Baked-in config." Server / key / API, branding (name, icon, logo, accent, theme, slogan), password & approve mode, the full permission matrix, connection direction, and all the feature tweaks. A live preview shows the exact baked-in payload as you type.
Build tab β "Build." Select target(s), optionally Preview plan (dry-run), then Build. The console streams every command; Cancel stops a run. On success, artifacts are listed with an open folder button.
Desktop clients are host-locked β each is built on its own OS (this mirrors the original CI workflows and is not a limitation DVForge can lift). Android is cross-platform and builds on any Linux or macOS host.
| You want | Build it on | You get | Target ID |
|---|---|---|---|
| πͺ Windows app | Windows | portable .exe, optional .msi |
windows-x86_64-exe, windows-x86_64-msi |
| π macOS Apple Silicon | any Mac | *-aarch64.dmg |
macos-arm64-dmg |
| π macOS Intel | any Mac | *-x86_64.dmg (cross-compiled from Apple Silicon) |
macos-x86_64-dmg |
| π macOS universal | any Mac | *-universal.dmg (both CPUs, slower) |
macos-universal-dmg |
| π§ Linux (x86_64) | Linux / WSL | .deb / .rpm / .AppImage |
linux-x86_64-deb, linux-x86_64-rpm, linux-x86_64-appimage |
| π§ Linux (ARM64) | Linux / WSL | .deb |
linux-aarch64-deb |
| π± Android phone / tablet | Linux, WSL, or macOS | .apk |
android-arm64, android-armv7, android-x86_64, android-universal |
π« Windows PCs cannot build APKs (the Android
.shscripts need a Unix host; on Windows use WSL2). The board hides impossible combinations for you.
Pick the cell that matches the device CPU, not the computer you built on.
| Device | Board cell | File name |
|---|---|---|
| Almost every phone (Galaxy A54 5G, Pixel, most 2019+) | Android arm64-v8a | YourApp-arm64-v8a-release.apk |
| Very old 32-bit phones | Android armeabi-v7a | β¦-armeabi-v7a-release.apk |
| Android x86 emulators / some tablets | Android x86_64 | β¦-x86_64-release.apk |
| One APK for every ABI (largest) | Android universal | β¦-release.apk |
- Only the target you selected is collected into
output/. - π‘οΈ Sideloaded remote-desktop APKs always trip Play Protect ("install anyway"). That's expected β it is not a broken build.
- βοΈ APKs use debug signing unless you add your own keystore (see Code signing).
Get the Android toolchain (Linux or Mac): click install on the board, or on macOS run bash Setup-DVForge-macOS.sh --with-android. That installs JDK 17, NDK r28c (16 KB page size β required on Android 15+), and the Android SDK (API 34) into .toolchains/, reusing the same Flutter you already have for DMGs.
Everything lives in configs/RustDesk.json β edit it in the GUI (recommended) or by hand. Below is the full field reference.
Core / server
| Field | Example | Meaning |
|---|---|---|
version |
1.4.9 |
RustDesk source tag to build |
platform |
windows |
Target platform hint |
serverIP |
your.server.com |
Rendezvous/relay server β compiled in |
key |
YourApiKeyHere |
Server public key β compiled in |
apiServer |
https://api.you.com/ |
API server URL β compiled in |
appname / exename / compname |
loadworker |
App name, executable name, company name |
androidappid |
com.e4bdb2cd.client |
Android application ID |
urlLink / downloadLink |
https://you.com |
Branding links baked into the client |
slogan |
loadworker |
Client slogan |
direction |
both |
both Β· incoming (host-only) Β· outgoing (controller-only) |
Branding & theme
| Field | Example | Meaning |
|---|---|---|
iconFile / logoFile |
workspace/branding/icon.png |
Paths to icon/logo used at build |
iconbase64 / logobase64 |
<base64> |
Embedded icon/logo bytes (so farm workers can recreate them) |
theme |
light |
Default theme (light / dark) |
themeDorO |
override |
Theme default-or-override |
themeColor |
#19E646 |
Primary accent |
themeSurfaceLight / themeSurfaceDark |
#19DBE6 / #18191E |
Surface colors |
themeMeColor |
#21790B |
"Me" accent |
Password & permissions
| Field | Example | Meaning |
|---|---|---|
passApproveMode |
password |
Approve mode |
permanentPassword |
CHANGE_ME |
Baked-in permanent password (runtime, via base64 custom_.txt) |
permissionsDorO |
default |
Permissions default-or-override |
permissionsType |
custom |
Permission preset type |
enableKeyboard, enableClipboard, enableFileTransfer, enableAudio, enableTCP, enableRemoteRestart, enableRecording, enableBlockingInput, enableRemoteModi, enablePrinter, enableCamera, enableTerminal |
on / off |
Per-feature permission toggles |
overrideManual |
hide-tray=Y\n⦠|
Extra RustDesk config lines appended verbatim |
defaultManual |
`` | Default config lines |
Tweaks (feature patches)
| Field | Meaning |
|---|---|
hidecm |
Hide the connection manager window |
removeNewVersionNotif |
Remove the "new version available" nag |
removeWallpaper |
Remove wallpaper during sessions |
denyLan |
Deny LAN discovery |
enableDirectIP |
Allow direct IP access |
autoClose |
Auto-close behavior |
cycleMonitor |
Cycle monitors |
xOffline |
Show/hide offline peers styling |
statussort |
Sort peers by status |
delayFix |
Input-delay fix |
Signing (see the dedicated section)
| Field | Meaning |
|---|---|
signWinPfx / signWinPassword / signWinTimestamp |
Windows Authenticode PFX, its password, RFC-3161 timestamp URL |
signAndroidKeystore / signAndroidAlias / signAndroidStorePassword / signAndroidKeyPassword |
Android keystore + credentials |
signMacIdentity |
macOS Developer ID / Keychain identity |
signMacP12 / signMacP12Password |
macOS signing .p12 + password |
signMacNotaryKey / signMacNotaryKeyId / signMacNotaryIssuer |
Apple notarization API key details |
The left rail detects what you have and can download a portable copy of most tools into .toolchains/ (no admin). Click install next to a gap, or install missing.
| Tool | Version (pin) | Needed for |
|---|---|---|
| Python | 3.8+ | the app itself (must pre-exist) |
| Git | any | source checkout |
| Rust | 1.75 Β· (1.81 on macOS) | every native build |
| Flutter | 3.24.5 | every Flutter UI |
| flutter_rust_bridge_codegen | 1.80.1 | DartβRust bridge codegen |
| LLVM / libclang | 15.0.6 | bindgen / ffigen |
| vcpkg | pinned 120deac3β¦ba10b |
FFmpeg / hwcodec |
| JDK | 17 | Android |
| Android NDK | r28c | Android native lib (16 KB pages, Android 15+) |
| Android SDK | API 34 | flutter build apk |
| VS Build Tools (C++) | 2022 | Windows linker (link.exe) + MSBuild β required for all Windows-host builds |
| .NET 8 SDK / NuGet | 8 | Windows .msi packaging |
| sccache | 0.11.0 | compile caching (optional) |
| ImageMagick / potrace | β | icon/logo processing (optional) |
| Xcode + create-dmg | β | macOS .dmg |
Xcode and Visual Studio cannot be silently sideloaded β the UI gives the exact install hint for those. Everything else installs one-click.
Reset: delete
.toolchains/to start tool downloads clean, then use the uninstall scripts.
DVForge can produce signed installers, or generate self-signed material from the UI for local tests.
| Platform | Options | UI action |
|---|---|---|
| πͺ Windows | Authenticode PFX + RFC-3161 timestamp | POST /api/signing/self-signed generates a self-signed code-signing PFX (and trusts it locally) for testing |
| π€ Android | Your keystore + alias + passwords | POST /api/signing/android-keystore generates a debug/keystore for testing; otherwise APKs use debug signing |
| π macOS | Developer ID / Keychain identity, or .p12, plus Apple notarization (API key) |
POST /api/signing/macos-self-signed generates a self-signed identity for local runs |
Signing files (
signWinPfx,signMacP12, keystores) are embedded as base64 in the saved config so a farm worker can recreateworkspace/signing/remotely. Apple Developer ID / Keychain identities are not files β they must already exist on the Mac worker.
Finished installers are collected here, versioned by the RustDesk tag:
workspace/output/v1.4.9/
βββ YourApp-1.4.9-aarch64.dmg
βββ YourApp-1.4.9.exe
βββ YourApp-1.4.9.msi
βββ YourApp-1.4.9.deb
βββ YourApp-1.4.9.rpm
βββ YourApp-arm64-v8a-release.apk
βββ β¦
Measured on real hardware (WSL2 Debian) β your mileage varies with CPU, disk, and cache state. First runs are slowest (cold toolchain + full source compile); subsequent runs benefit from sccache.
| Target | Time | Notes |
|---|---|---|
Linux .rpm |
~8m 29s | |
Linux .deb |
~8m 13s | |
Linux .deb (ARM) |
~7m 52s | |
| Android arm64-v8a | ~14m 56s | |
| Android x86_64 | ~14m 43s | |
| Android armeabi-v7a | ~18m 36s | |
| Android universal (all ABIs) | ~64 min | first run; normal for all-ABI |
Optional. Lets you offload each OS's build to a machine that can actually do it β a Mac builds DMGs, a Windows box builds EXEs, a Linux box builds APKs + packages. DVForge itself still stays on
127.0.0.1on every machine; only a small job queue is shared.
Model: you drop a job β a worker that matches the job's OS claims it β the finished file lands in an outbox.
You submit a job β farm/inbox/*.json (or the HTTP queue)
Mac worker β claims macos-* β builds .dmg
Windows worker β claims windows-* β builds .exe / .msi
Linux worker β claims linux-* + android-* β builds packages / APKs
Finished files β farm/outbox/<job-id>/ (+ status.json)
Failures β farm/failed/
Two transports are supported:
- Shared folder (NAS/SMB/NFS) β every worker watches the same
farm/directory. Startpython3 farm/worker.pyon each machine; submit withpython3 farm/submit.py --targets macos-arm64-dmg. - HTTP queue β run
farm/queue.pybehind nginx (HTTPS + bearer token) and let workers claim over HTTP withpython worker.py --queue "https://api.example" --token "β¦". There's a public submit UI (farm/public/) and live stats at/status,/health,/stats.
Worker rating: new machines start at 50%. Higher-rated idle workers of the same OS get jobs first. A worker with zero successes after 2+ jobs, or 5 failures in a row, is skipped until reset. You can pin a specific machine for a job.
Offline alerts: pass --notification-webhook <url> (or DVFORGE_WORKER_WEBHOOK) to a worker and the queue will POST to that URL when the worker drops offline or recovers β useful for a Discord/Slack channel. See farm/README.md Β§ 3d.
Stopping :8765 / :8766: --with-app / --with-queue intentionally leave those running after Ctrl+C so other machines keep claiming. Run ./farm/stop-farm.sh (farm\stop-farm.bat on Windows) to stop both and clean up stale worker locks.
π Security: never expose port 8766 or DVForge's
:8765to the public internet without nginx HTTPS + a token. Prefer blocking/api/build/on any public vhost and letting the queue be the only public entry. One job = one OS.
Full walk-through (both PCs, NAS mounts, nginx, curl recipes, worker reset/pin): farm/README.md.
RustDesk's stock printer_driver_adapter.dll verifies the calling executable's Authenticode signature inside init() and refuses to run for anything not signed by RustDesk β so remote printing silently fails in custom builds:
ERROR [src\server.rs:160] printer service init failed: Failed to init printer driver
Signing with your own cert doesn't help β the check is an allow-list of specific RustDesk identities, not "is it signed." Everything else in RustDesk's printing chain is open source.
printer-adapter/ is a drop-in Rust replacement that reimplements the exact same four-function C ABI (init / uninit / get_prn_data / free_prn_data) with no signature check. It captures print jobs via a virtual printer whose Local Port name is a file path, polls that spool directory, and returns the bytes β no custom print driver, therefore no WHQL signing required.
Full explanation, ABI, and capture mechanics: printer-adapter/README.md.
DVForge applies patches to the RustDesk (and sometimes Flutter) source at build time. The essential ones run always; the rest are GUI toggles.
| Patch / file | Effect |
|---|---|
patches/allowCustom.py Β· allowCustom.diff |
Required. Strips the 9-line signature-check block from src/common.rs; renames custom.txt β custom_.txt so baked-in config loads |
patches/hidecm.diff |
Hide the connection manager (CM) window |
patches/removeNewVersionNotif.diff |
Remove the "new version available" notification |
patches/removeSetupServerTip.diff |
Remove the "set up your server" tip on the connection page |
patches/xoffline.diff |
Change offline-peer display in the peer card |
patches/privacyScreen.py |
Add privacy-screen support (PNGβC++ image embed) |
patches/flutter_3.24.4_dropdown_menu_enableFilter.diff |
Enable filtering in Flutter's dropdown menu |
patches/load-config-original.py |
Reference: the original CI load-config.py DVForge stays byte-compatible with |
DVForge/
βββ app.py # Local HTTP UI + JSON API + SSE log stream (stdlib only)
βββ run.sh / run.bat # Launchers (Linux/macOS Β· Windows)
βββ precheck.sh / precheck.bat # Pre-flight environment check
β
βββ Setup-DVForge-macOS.sh # First-time toolchain setup, per OS
βββ Setup-DVForge-Windows.ps1
βββ Setup-DVForge-WSL2.ps1
βββ Uninstall-DVForge-*.{sh,ps1} # Matching uninstallers
βββ clean.sh / clean.bat # Clean-slate reset (keeps config + branding)
β
βββ builder/ # ββ the engine ββ
β βββ detect.py # hardware/OS detection + capability matrix
β βββ prereqs.py # toolchain detection + per-OS install hints
β βββ toolchains.py # portable toolchain download/install + env.json
β βββ config_gen.py # RustDesk.json β CUSTOM_* + base64 custom_.txt
β βββ customize.py # all sed/patch steps, Android embed, signature strip
β βββ orchestrator.py # build orchestration (checkoutβpatchβcompileβcollect)
β βββ signing.py # Windows/macOS/Android self-signed material
β βββ precheck.py # environment pre-flight
β
βββ web/ # ββ the browser GUI ββ
β βββ index.html # spec readout, capability board, config form, console
β βββ app.js # all client logic (vanilla JS)
β βββ advanced-keys.js # advanced config keys
β βββ style.css # hardware-capability aesthetic
β
βββ configs/
β βββ RustDesk.json # Your baked-in config (edit in the GUI)
β
βββ patches/ # allowCustom + feature diffs (see reference above)
β
βββ farm/ # ββ optional multi-machine build farm ββ
β βββ worker.py # claims + builds jobs matching its OS
β βββ queue.py # HTTP job queue (run behind nginx + token)
β βββ submit.py # submit a job
β βββ stop-farm.sh / .bat # stop :8765 / :8766 + clean worker locks
β βββ inbox/ outbox/ failed/ β¦ # job flow directories
β βββ public/ # public submit UI (index/join/stats)
β βββ README.md # full farm guide
β
βββ printer-adapter/ # ββ open printer_driver_adapter.dll replacement ββ
β βββ src/lib.rs # the four-function C ABI, no signature check
β βββ Cargo.toml
β βββ README.md
β
βββ workspace/ # (created at runtime)
β βββ rustdesk-src/ # cloned + customized RustDesk source
β βββ branding/ # your icons/logos
β βββ signing/ # generated signing material
β βββ output/v<version>/ # β
finished installers
β
βββ .toolchains/ # (created on install) portable SDKs + env.json
β
βββ HANDOFF.md # Deep engineering context / test history
βββ LICENSE # GPL-3.0
βββ README.md # You are here
app.py exposes a small JSON + SSE API on http://127.0.0.1:8765. Useful for scripting or for understanding the GUI's behavior.
GET endpoints
| Endpoint | Returns |
|---|---|
GET /api/host |
Detected hardware + OS spec |
GET /api/prereqs |
Toolchain detection results |
GET /api/matrix |
Capability matrix (which targets are buildable) |
GET /api/config |
Current RustDesk.json |
GET /api/config/status |
Config validity / status |
GET /api/build/stream |
SSE β live build log |
GET /api/build/status |
Current build state / result |
GET /api/toolchains |
Toolchain list with sizes/versions |
GET /api/toolchains/stream |
SSE β live install log |
GET /api/toolchains/status |
Install state |
GET /api/update/status |
Update availability |
GET /api/update/stream |
SSE β live update log |
GET /api/branding/<file> |
Serve a branding asset (icon/logo) |
POST endpoints
| Endpoint | Does |
|---|---|
POST /api/config |
Save config (unpacks embedded icon/logo/signing blobs) |
POST /api/preview |
Render the baked-in custom_.txt preview |
POST /api/build/preflight |
Validate before building |
POST /api/build/start |
Start a build (dry_run supported) |
POST /api/build/cancel |
Cancel the running build |
POST /api/toolchains/install |
Install a toolchain / "install missing" |
POST /api/toolchains/cancel |
Cancel a running install |
POST /api/toolchains/remove |
Remove a locally-installed toolchain |
POST /api/signing/self-signed |
Generate a Windows self-signed PFX |
POST /api/signing/android-keystore |
Generate an Android keystore |
POST /api/signing/macos-self-signed |
Generate a macOS self-signed identity |
POST /api/upload |
Upload a branding/signing file |
POST /api/open-folder |
Open the output folder in the OS file manager |
POST /api/update/start |
Pull a newer DVForge commit |
| Variable / flag | Where | Effect |
|---|---|---|
RDLB_PORT |
app.py |
Listen port (default 8765) |
--no-browser |
app.py / launchers |
Don't auto-open the browser |
DVFORGE_URL |
farm worker | DVForge instance the worker drives (default http://127.0.0.1:8765) |
DVFORGE_FARM |
farm worker | Path to the shared farm/ directory |
DVFORGE_FARM_TOKEN |
farm/queue.py |
Bearer token for the HTTP queue |
Hard-won fixes from real Windows/macOS/Linux test rounds (see HANDOFF.md for the full history):
| Symptom | Cause & fix |
|---|---|
linker link.exe not found (Windows) |
Rust's Windows host is MSVC. Install VS Build Tools (C++) β required for all Windows-host builds, including Android. Board cells will say "install first: msbuild" until it's present. |
msbuild still shows missing after install |
Detection uses vswhere; a fresh terminal (or re-scan) flips it green once link.exe is available. |
WinError 740 on LLVM install |
The official LLVM installer requires admin. DVForge runs it elevated via one UAC prompt β approve it. LLVM is only needed for Windows desktop builds. |
WinError 2 on a tool (e.g. flutter_rust_bridge_codegen) |
A tool isn't on PATH. run() resolves executables via shutil.which and adds ~/.cargo/bin; if a tool is genuinely missing you'll get a clear message β install it from the toolchain panel. |
'charmap' codec can't decode 0x90 |
Old Unicode crash on non-cp1252 build output β fixed (all subprocess output is decoded utf-8 / errors="replace"). Update to latest. |
rustdesk-src already exists |
Checkout is now always clean β any previous source tree is removed first (customizations mutate it, so reuse would corrupt the build). |
Android .sh scripts fail on Windows |
They need bash; DVForge auto-finds Git Bash (<Git>\bin\bash.exe). If missing, install Git for Windows. Or build Android via WSL2. |
ConnectionAbortedError / WinError 10053 spam |
Normal SSE disconnect when you close the browser tab β harmless, now swallowed. |
| A download URL 404s | URLs are official but can move. The console prints the exact URL β it's a one-line fix in builder/toolchains.py. |
| Play Protect warns on the APK | Expected for all sideloaded remote-desktop apps. Tap "install anyway." Add your own keystore for a cleaner install. |
| Remote printing fails in a custom build | RustDesk's printer DLL is signature-gated. Use the included printer adapter. |
Clean build artifacts (keeps your config + branding + toolchains):
bash clean.sh # Linux / macOS (clean.bat on Windows)Removes workspace/rustdesk-src, workspace/output, caches, and stray logs. Add --all for a deeper clean; your configs/ and workspace/branding/ are preserved.
Uninstall toolchains (per OS):
bash Uninstall-DVForge-macOS.sh
# Windows (toolchains only, keeps project + VS):
# powershell -File .\Uninstall-DVForge-Windows.ps1
# Windows (also delete C:\DVForge):
# powershell -File .\Uninstall-DVForge-Windows.ps1 -InstallRoot C:\DVForge -RemoveProject -Force
# WSL2:
# powershell -File .\Uninstall-DVForge-WSL2.ps1
# WSL2 (full wipe incl. Debian distro):
# powershell -File .\Uninstall-DVForge-WSL2.ps1 -RemoveDebian -RemoveWslConfig -ForceOr simply delete .toolchains/ to reset all portable tool downloads.
Do I need GitHub or any cloud account?
No. DVForge builds entirely locally. The only network access is downloading toolchains and cloning the RustDesk source once.
Do I need to pip install anything?
No. app.py and the whole server use only the Python standard library. Python 3.8+ is the single prerequisite.
Why can't my Windows PC build a Mac DMG (or vice-versa)?
Desktop builds are host-locked β this mirrors RustDesk's own CI and can't be lifted. Use the build farm to route each OS's build to a machine that can do it. Android is the exception β it builds on any Linux/macOS host.
Is my baked-in password secure?
It's stored base64-encoded in custom_.txt next to the binary (this is how RustDesk reads runtime config). Base64 is encoding, not encryption β treat any client that ships a permanent password as you would any credential.
My APK triggers Play Protect. Is the build broken?
No β every sideloaded remote-desktop APK does. Tap "install anyway," and add your own keystore for a smoother install.
Does DVForge publish or host the installers?
No. DVForge only builds. It never publishes releases or hosts downloads β the files stay in workspace/output/.
How do I update DVForge itself?
Check for updates in the left rail pulls a newer DVForge commit when you want it (POST /api/update/start).
Remote printing doesn't work in my branded build.
RustDesk's printer DLL only runs for RustDesk-signed executables. Use the included open printer adapter.
- Zero pip dependencies β the server is pure Python stdlib. Nothing to install, nothing to break, trivial to audit.
- Nothing system-wide β every heavy SDK lives in a project-local
.toolchains/. Delete the folder to reset. Your OS stays clean. - Local-only by default β the server binds
127.0.0.1. The farm is opt-in and documented with a security checklist. - Honest capability board β the UI never offers a build this machine can't do; it tells you exactly which tool is missing.
- Full transparency β dry-run prints every command; the console streams the real thing live.
- Byte-compatible with the original CI β
config_gen.pyis verified byte-for-byte against the upstreamload-config.py, so builds match the proven GitHub Actions output.
A machine-oriented map so an LLM or agent can understand and operate this repo without spelunking.
What this project is: a local, offline builder that compiles a customized RustDesk remote-desktop client. No cloud CI. Pure-stdlib Python server + vanilla-JS browser GUI + a builder/ engine.
Entry point: app.py β ThreadingHTTPServer, serves web/, exposes the JSON+SSE API in API reference. Runs toolchains.apply_persisted_env(ROOT) at startup to load .toolchains/env.json before detection. Default port 8765 (RDLB_PORT), --no-browser to suppress auto-open. Bind is 127.0.0.1.
Run it: python3 app.py (or ./run.sh / run.bat). First-time toolchains via Setup-DVForge-{macOS.sh,Windows.ps1,WSL2.ps1}.
Engine modules (builder/):
detect.pyβ OS/CPU β capability matrix. Target IDs:windows-x86_64-{exe,msi},macos-{arm64,x86_64,universal}-dmg,linux-{x86_64-deb,x86_64-rpm,x86_64-appimage,aarch64-deb},android-{arm64,armv7,x86_64,universal}.prereqs.pyβ toolchain presence + install hints.toolchains.pyβ portable download/install registry; writes/loads.toolchains/env.json.config_gen.pyβconfigs/RustDesk.jsonβ compiled-inCUSTOM_*vars and base64custom_.txt(runtime). Byte-identical to upstreamload-config.py.customize.pyβ applies all source patches, the Android native-embed, and the signature-check strip.orchestrator.pyβ full pipeline: clean checkout β patch β bake β bridge codegen β compile β collect toworkspace/output/v<version>/. Supports dry-run + cancel + live-log callback.signing.pyβ self-signed Windows/macOS/Android material.
Key invariants (do not violate):
custom_.txtis base64, never raw JSON (RustDesk'sread_custom_client()callsdecode64()first).- Android does not file-read
custom_.txtβ the config is embedded intoMainService.kt+native_model.dartand bundled as a Flutter asset. - Desktop builds are host-locked; Android is cross-platform.
- Windows host needs the MSVC linker (
link.exe, via VS Build Tools) for every target including Android. - Pinned versions: RustDesk
v1.4.9Β· Rust1.75(macOS1.81) Β· Flutter3.24.5Β· LLVM15.0.6Β· NDKr28cΒ· JDK17Β· flutter_rust_bridge_codegen1.80.1Β· vcpkg commit120deac3062162151622ca4860575a33844ba10b.
Config file: configs/RustDesk.json β see Configuration reference for every field. Compiled-in vs runtime split documented there.
Subsystems: farm/ (multi-machine build queue, shared-folder or HTTP), printer-adapter/ (open replacement for the signature-gated printer DLL). Each has its own README.
Deeper context: HANDOFF.md documents architecture, test history, and remaining work.
Contributions are welcome! To get started:
- Fork the repo and create a feature branch.
- Read
HANDOFF.mdfor architecture and current state. - Keep the zero-pip-dependency rule for
app.pyandbuilder/(standard library only). - Test with Preview plan (dry-run) before real builds β it prints the full command sequence.
- If you touch config generation, keep it byte-compatible with
patches/load-config-original.py. - Open a PR with a clear description; note which OS/target you tested on.
Found a bug or a 404'd download URL? Open an issue β the console prints the exact failing URL, which usually makes it a one-line fix.
DVForge is licensed under the GNU GPL v3.0.
It incorporates work from rdgen by Bryan Gerlach (GPL-3.0) β the custom-client config shape, patches, and GitHub Actions generator this local builder is based on.
Clients you produce are based on RustDesk (AGPL-3.0).
Contributors: VenimK Β· deadboy18 Β· Tiddley123 Β· bryangerlach
Built for people who self-host RustDesk and want a client that already knows their server.
Discord: discord.gg/de2srV6sx Β Β·Β Donate: paypal.me/VenimK
β If DVForge saved you a CI pipeline, consider starring the repo.