From e03c978d85fe98818aaea63101f86dd56f5cafab Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Thu, 7 May 2026 00:21:50 -0400 Subject: [PATCH] README: update roadmap, document Linux+Windows builds, reflect first-packet tunnelUp signal --- README.md | 197 +++++++++++++++++++++++++++++------------------------- 1 file changed, 106 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 38a8f16..db3df64 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 🐉 DragonMoonlight +# DragonMoonlight A fork of [moonlight-qt](https://github.com/moonlight-stream/moonlight-qt) with native [DragonRelay](https://forge.wilddragon.net/zgaetano/dragonrelay) integration and an embedded WireGuard client. @@ -15,8 +15,10 @@ Instead of managing a separate VPN app, DragonMoonlight authenticates against yo │ ▼ │ │ │ │ TunnelManager │ │ wg0 interface │ │ ├─ boringtun (WireGuard/Rust) │◄═════►│ 10.99.0.1 │ -│ └─ utun (macOS, no root*) │ WG │ │ -│ │ │ mDNS → Apollo/Artemis hosts │ +│ ├─ utun (macOS, no root*) │ WG │ │ +│ ├─ Wintun (Windows, Admin) │ │ Hosts registered by Artemis │ +│ └─ /dev/net/tun (Linux, CAP) │ │ via /api/host/register │ +│ │ │ │ │ DragonRelayView.qml │ └──────────────────────────────┘ │ (host list, stream button) │ └─────────────────────────────────┘ @@ -28,92 +30,97 @@ Instead of managing a separate VPN app, DragonMoonlight authenticates against yo ``` app/vpn/ -├── boringtun_ffi.h C ABI for the boringtun Rust library -├── wireguardconfig.h/.cpp WireGuard .conf parser -├── tunnelmanager.h Cross-platform tunnel interface -├── tunnelmanager_mac.mm macOS implementation (utun + boringtun) -├── relayclient.h/.cpp HTTP client for the DragonRelay API -└── CMakeLists_vpn.cmake Build system additions +├── boringtun_ffi.h C ABI for the boringtun Rust library +├── wireguardconfig.h/.cpp WireGuard .conf parser +├── tunnelmanager.h Cross-platform tunnel interface +├── tunnelmanager_mac.mm macOS implementation (utun + boringtun) +├── tunnelmanager_win.cpp Windows implementation (Wintun + boringtun) +├── tunnelmanager_linux.cpp Linux implementation (/dev/net/tun + boringtun) +├── wintun.h Wintun dynamic loader (Windows) +├── relayclient.h/.cpp HTTP client for the DragonRelay API +├── dragonrelaybackend.h/.cpp QObject bridge: RelayClient + TunnelManager → QML +└── CMakeLists_vpn.cmake Build system additions app/gui/ -└── DragonRelayView.qml Connect / host-list UI page +├── DragonRelayView.qml Connect / host-list UI page +└── DragonDisplayPicker.qml Multi-display selection modal + +app/ +├── DragonMoonlight.manifest Windows UAC manifest (requireAdministrator) +├── main.cpp Entry point with QML engine + dragonRelay backend +└── assets/ WildDragon logo + qrc scripts/ -└── build-boringtun.sh Compiles boringtun as a static library +├── build-boringtun.sh Compiles boringtun for Linux/macOS +├── build-boringtun-win.ps1 Compiles boringtun for Windows +├── build-installer-mac.sh macOS .dmg + .pkg installer +├── build-installer-win.ps1 Windows .exe installer (Inno Setup) +└── DragonMoonlight.iss Inno Setup script -deps/boringtun/ Created by build-boringtun.sh (git-ignored) +package/ +├── mac/ macOS code-signing entitlements + DMG art +└── win/ Windows pre-install elevation/version checks + +deps/boringtun/ Created by build scripts (git-ignored) ``` -## Building (macOS) +## Building -### 1. Prerequisites +### macOS ```bash -# Xcode Command Line Tools -xcode-select --install - -# Homebrew brew install qt@6 cmake rustup +rustup-init && rustup target add aarch64-apple-darwin x86_64-apple-darwin -# Rust toolchain -rustup-init -rustup target add aarch64-apple-darwin x86_64-apple-darwin +git clone https://forge.wilddragon.net/zgaetano/dragonmoonlight && cd dragonmoonlight +git remote add upstream https://github.com/moonlight-stream/moonlight-qt.git +git fetch upstream && git merge upstream/master --allow-unrelated-histories +git submodule update --init --recursive + +bash scripts/build-boringtun.sh --universal + +mkdir build && cd build +cmake .. -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6)" +make -j$(sysctl -n hw.logicalcpu) ``` -### 2. Clone upstream moonlight-qt into this repo +### Windows -This repo holds only the DragonMoonlight additions. You need to merge with upstream: - -```bash +```powershell +# Prerequisites: Visual Studio Build Tools 2019+, Rust, CMake, Qt 6, Inno Setup git clone https://forge.wilddragon.net/zgaetano/dragonmoonlight cd dragonmoonlight - -# Add upstream as a remote and merge git remote add upstream https://github.com/moonlight-stream/moonlight-qt.git git fetch upstream git merge upstream/master --allow-unrelated-histories git submodule update --init --recursive + +pwsh scripts/build-boringtun-win.ps1 + +cmake -B build -A x64 -DCMAKE_BUILD_TYPE=Release +cmake --build build --config Release + +# Optional: build the .exe installer +pwsh scripts/build-installer-win.ps1 ``` -### 3. Build boringtun +`wintun.dll` (x64) must sit next to `DragonMoonlight.exe` at run time. The +installer script downloads it automatically; for ad-hoc builds, fetch it from + and copy it to the build output directory. + +### Linux ```bash -bash scripts/build-boringtun.sh --universal -# Produces: deps/boringtun/libboringtun.a (universal arm64+x86_64) -``` +sudo apt install qt6-base-dev qt6-declarative-dev qml6-module-qtquick \ + cmake build-essential libcurl4-openssl-dev rustup -### 4. Integrate the VPN CMake snippet +bash scripts/build-boringtun.sh -In moonlight-qt's root `CMakeLists.txt`, add **after** the main target is defined: +cmake -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build -j -```cmake -include(app/vpn/CMakeLists_vpn.cmake) -``` - -### 5. Register DragonRelayBackend with QML - -In `app/main.cpp` (or wherever the QML engine is set up), add: - -```cpp -#include "dragonrelaybackend.h" // thin QObject wrapping RelayClient + TunnelManager - -// Before engine.load(): -DragonRelayBackend relayBackend; -engine.rootContext()->setContextProperty("dragonRelay", &relayBackend); -``` - -`DragonRelayBackend` is the next class to write — it wires `RelayClient` ↔ `TunnelManager` ↔ QML. - -### 6. Add the view to the navigation stack - -In `app/gui/main.qml` or the computer browser stack, push `DragonRelayView` alongside the normal `PcView`. - -### 7. Build - -```bash -mkdir build && cd build -cmake .. -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6)" -make -j$(sysctl -n hw.logicalcpu) +# Grant net-admin so the binary can open /dev/net/tun without being root: +sudo setcap cap_net_admin+ep ./build/dragonmoonlight ``` ## How it works (connection flow) @@ -128,21 +135,23 @@ RelayClient.login(url, user, pass) RelayClient.provisionVPN(deviceName) │ POST /api/vpn/peer → {id, conf} ▼ -WireGuardConfig.fromConf(conf) +WireGuardConfig::fromConf(conf) │ ▼ -TunnelManager.start(cfg) +TunnelManager.start(cfg) (mac/win/linux platform impl) ├─ new_tunnel() via boringtun FFI - ├─ openUtun() → utunN fd (no root) - ├─ UDP socket → server endpoint - ├─ configureInterface() → osascript admin dialog (once per session) - │ ifconfig + route add for 10.99.0.0/24 + ├─ Open utun/Wintun/tun device + ├─ UDP socket → server endpoint + ├─ configureInterface() → routes └─ I/O threads (tunToUdp, udpToTun, ticker) │ ▼ WireGuard handshake completes │ ▼ + udpToTunThread emits tunnelUp() on first decrypted packet + │ + ▼ RelayClient.fetchHosts() → GET /api/hosts │ ▼ @@ -152,38 +161,44 @@ TunnelManager.start(cfg) │ ▼ dragonRelay.streamHost(ip, "Desktop") - (launches Moonlight streaming session) + (launches Moonlight streaming session via QProcess) ``` ## Privilege model -| Operation | Root / Admin? | -|---|---| -| Open utun device | No | -| `ifconfig utunN up` | Yes — one-time macOS auth dialog | -| `route add -net 10.99.0.0/24 -interface utunN` | Yes — same dialog, batched | -| Streaming (RTSP/UDP to Apollo host) | No | +| Operation | macOS | Windows | Linux | +|-----------|-------|---------|-------| +| Open TUN device | none | Admin (Wintun driver) | `CAP_NET_ADMIN` | +| Configure address + routes | one osascript dialog | none (IP Helper API) | `CAP_NET_ADMIN` | +| Streaming (RTSP/UDP) | none | none | none | -The admin dialog appears once when the VPN first connects. Subsequent reconnects in the same session reuse the saved original-gateway and only need the dialog again if the interface was torn down. +The macOS dialog appears once when the VPN first connects; subsequent reconnects +in the same session reuse the saved original-gateway and only need the dialog +again if the interface was torn down. -**Production path:** Replace the `osascript` approach with a signed, sandboxed SMJobBless privileged helper or a NetworkExtension PacketTunnelProvider entitlement (requires Apple developer approval). - -## DragonRelayBackend (next step) - -The QML view is wired to a `dragonRelay` context property. The next file to write is `app/vpn/dragonrelaybackend.h/.cpp` — a `QObject` that: - -- Owns `RelayClient` and `TunnelManager` -- Exposes `Q_PROPERTY` values (`status`, `statusText`, `hosts`) to QML -- Has `Q_INVOKABLE` methods: `connectRelay()`, `disconnectRelay()`, `streamHost()` -- Translates `RelayClient` and `TunnelManager` signals into QML-visible state changes +**Production path (macOS):** Replace the `osascript` approach with a signed, +sandboxed SMJobBless privileged helper or a NetworkExtension PacketTunnelProvider +entitlement (requires Apple developer approval). ## Roadmap -- [ ] `DragonRelayBackend` C++ ↔ QML bridge -- [ ] Integrate `DragonRelayView` into upstream `PcView.qml` navigation -- [ ] Windows support (`tunnelmanager_win.cpp` using Wintun) -- [ ] Linux support (`tunnelmanager_linux.cpp` using kernel WireGuard) +- [x] `DragonRelayBackend` C++ ↔ QML bridge +- [x] `DragonRelayView` is the navigation entry point exposed to QML +- [x] Windows support (`tunnelmanager_win.cpp` using Wintun) +- [x] Linux support (`tunnelmanager_linux.cpp` using boringtun userspace + `/dev/net/tun`) +- [x] `tunnelUp()` signal from first received packet (with 1500 ms fallback) +- [x] Multi-monitor / display selection (Sunshine API integration via DragonDisplayPicker) - [ ] Replace `osascript` with SMJobBless privileged helper on macOS - [ ] Replace plain-text password storage with system keychain (QKeychain) -- [ ] `connected()` signal from first received packet rather than a fixed timer -- [ ] Multi-monitor / display selection (Sunshine API integration) +- [ ] Auto-reconnect on network change + +## Notes for upstream maintainers + +- `app/main.cpp` is the entry point; it instantiates a `DragonRelayBackend` and + registers it as the `dragonRelay` context property before loading `main.qml`. +- All Dragon-specific files live under `app/vpn/`, `app/gui/Dragon*`, and + `scripts/`. Upstream `app/main.qml` is unmodified — DragonRelayView is loaded + on demand by user action in the existing PcView navigation stack. +- The boringtun FFI header is identical to the one used by Artemis; both + projects link the same boringtun build artifact and the FFI surface must + stay in lock-step.