Hadal turns your Android phone into a remote for your Windows PC. Check on it, control media and sound, use the phone as a touchpad, move files, or stream the whole screen with sound at up to 120 fps. Power actions work even before anyone logs in.
It only talks over your own Tailscale network. The PC never listens on the internet, only answers your paired phone, and only does things from a fixed list. No accounts, no cloud, no relay servers of ours.
|
🖥️ Status |
📈 Performance |
⚡ Power |
|
🎬 Stream |
🖱️ Touchpad and keyboard |
🎵 Media and sound |
|
📁 Files |
📋 Clipboard and links |
🚀 Apps |
|
🖼️ Displays |
📱 Phone extras |
🧭 Several PCs |
Note
You need Tailscale on both the PC and the phone, logged into the same account.
1. Tailscale on the PC. Turn on Run unattended (tray → Preferences) so it connects before you log in. In the admin console, open your PC → Disable key expiry.
2. Install on the PC. Download Hadal-PC.zip from Releases, extract it, and double-click Install.cmd. Accept the UAC prompt. The helmet appears in the tray.
3. Install on the phone. Download Hadal.apk from the same release and open it on the phone.
4. Pair. Right-click the tray helmet → Show pairing QR, then tap Scan pairing QR in the app. Close the QR window afterwards, it contains the key.
That's it. Optional extras:
- Quick toggles. Swipe down twice from the top of the phone to open the toggle panel (Wi-Fi, Bluetooth...), tap Edit, and drag in Lock PC, PC play/pause or PC monitors off.
- Widget. Long-press your home screen → Widgets → Hadal.
- Send links and files. In any app, tap Share → Send to PC. Links open in the PC's browser, files land in
Downloads\Hadal.
flowchart LR
phone["📱 Hadal app<br/><sub>Android</sub>"]
subgraph pc["Windows PC"]
svc["hadal-svc<br/><sub>service, LocalService</sub>"]
tray["hadal-tray<br/><sub>your session</sub>"]
end
phone -- "HTTP inside WireGuard<br/>100.x.y.z:47810" --> svc
svc -- "named pipe<br/>SYSTEM + you only" --> tray
tray -. "screen + sound" .-> svc
hadal-svcis a small Windows service. It listens on the PC's Tailscale address only, checks who's asking, does the power actions itself and samples performance. It runs as LocalService with every privilege removed except shutdown and the basic one every process needs to open folders.hadal-trayruns in your session. It does everything that needs your desktop: media, sound, input, clipboard, files, screenshots and the encoder for streaming. Every action from the phone shows up as a notification.- The app is plain Kotlin and Jetpack Compose. Streams are decoded in hardware with MediaCodec.
No third-party libraries on the PC: just Win32, DXGI, Media Foundation and Core Audio.
Hadal is built on the idea that a remote control for your PC should be boring to attack.
| Layer | What it does |
|---|---|
| 🌐 Network | Listens only on the Tailscale IP. The firewall rule only allows 100.64.0.0/10, and the code checks again. |
| 📌 Phone pinning | The first phone to log in after pairing is the only address the PC answers. Everyone else is refused before the key is even checked. |
| 🔑 Key | 256-bit random token, compared in constant time, stored on the phone encrypted with the Android Keystore. 10 wrong tries lock that device out for 5 minutes. |
| 📜 Fixed actions | The network protocol only accepts a fixed list of actions. No endpoint takes a shell command, and files only move through two fixed folders. |
| 🔒 Least privilege | The service keeps only the shutdown privilege, plus the basic one every process needs to open folders. Desktop work runs as you, behind a named pipe only SYSTEM, the service and you can open. |
| 🔔 Visible | Every action is logged and shows a notification on the PC. You can mute routine ones, but security alerts, streams and touchpad sessions always notify. |
| 👆 App lock | Optional fingerprint prompt every time the app opens. |
Important
A paired phone has the same power as you sitting at the PC. The touchpad, keyboard and screen stream reach everything your Windows session can: any program, any command you could type, anything on screen. The fixed action list limits what the network will accept, not what someone holding the phone can do with it. While Windows is locked, input, screenshots and streaming are refused.
Treat the phone and its token like an unlocked PC: keep the app lock on, and regenerate the token if the phone is lost.
Lock your tailnet down to just the PC and the phone
Tailscale lets every device talk to every other one by default. In the access controls, replace the grants block (or acls on older tailnets) with this, using the IPs from tailscale status:
Rules are allow-lists, so any device not named here can't reach either machine.
Lost your phone?
- Right-click the tray helmet → Regenerate token. The old key and the phone pin are gone instantly, and any live stream or touchpad session is cut off.
- In the Tailscale admin console, remove the lost phone from your tailnet. That's what really locks it out: it can no longer reach the PC at all.
- Pair the new phone with the fresh QR.
Everything a network peer can reach is fuzzed with libFuzzer and AddressSanitizer. The harness sends bursts of raw connections through the real request handler with a fake tray behind it. It fails not just on crashes but also when:
- a request makes the PC do something outside the fixed action list
- anything gets through without the key
- a device other than the paired phone gets past the pin
| Remote desktop apps | Phone remote apps | Hadal | |
|---|---|---|---|
| Live screen with sound | ✅ | ❌ | ✅ |
| Touchpad, media, power buttons | ➖ | ✅ | ✅ |
| Works before login | ➖ | ❌ | ✅ |
| Never listens on the internet | ➖ | ➖ | ✅ |
| Pinned to one phone | ➖ | ❌ | ✅ |
| No vendor account or cloud | ➖ | ➖ | ✅ |
| Games at Moonlight-level latency | ➖ | ❌ | ❌ |
For serious game streaming use Moonlight with Sunshine. Hadal is the remote for everything else.
Releases are built by GitHub Actions straight from the tagged source, never on someone's PC. The release workflow compiles the PC side with Visual Studio on a fresh Windows machine, runs the selftest, and builds the APK on a fresh Linux machine, signed with the project key. Every release links to the run that made it, so you can check exactly what went in.
Each release also lists the SHA-256 of every file and carries a signed build attestation, so you can prove a download came from that workflow:
gh attestation verify Hadal.apk --repo BaxterWolf/HadalTo build it yourself:
PC (Visual Studio 2022 with C++, x64)
cmake -S pc -B pc/build -A x64cmake --build pc/build --config Releasepc\build\Release\hadal-svc.exe --selftestThen double-click pc\Install.cmd. It picks up the exes from pc\build\Release. Running it again updates in place and keeps your pairing.
Android (Android Studio's JDK, nothing else)
cd android && gradlew.bat assembleReleaseRelease builds are signed with a key kept outside the repo: HADAL_KEYSTORE and HADAL_KEY_PASSWORD in ~/.gradle/gradle.properties. Back the key up. Without it, updates mean uninstalling and pairing again.
Fuzzer (MSVC's libFuzzer + ASan, run from a Developer prompt)
cmake -S pc -B pc/build-fuzz -DHADAL_FUZZ=ONcmake --build pc/build-fuzz --config Release --target hadal-fuzzmkdir pc\fuzz-corpus & pc\build-fuzz\Release\hadal-fuzz.exe pc\fuzz-corpus pc\fuzz-seeds -dict=pc\fuzz.dict -max_len=65536 -jobs=16 -workers=16Repo layout
| Path | What |
|---|---|
pc/svc.cpp |
Service: listener, auth, routes, power, performance, pipe server |
pc/tray.cpp |
Tray app: menu, pairing QR, notifications, all desktop actions |
pc/stream.cpp |
Screen capture, hardware H.264, sound |
pc/fuzz.cpp |
Fuzz harness |
pc/Install.cmd, pc/Uninstall.cmd |
Double-click install and uninstall |
pc/install.ps1, pc/uninstall.ps1 |
Everything that touches the system, in one place each |
android/.../Main.kt |
The app: Control, Performance, Touchpad, Settings, tiles, widget |
android/.../Stream.kt |
The stream screen |
android/.../Theme.kt |
Colours and type |
iPhone? Mac? Linux?
Not yet. Hadal is Windows on the PC side and Android on the phone side.
Why Tailscale?
It gives your phone and PC a private, encrypted network that works from anywhere, without opening ports or trusting a relay. Hadal leans on it so it never has to face the internet.
Does it work away from home?
Yes, anywhere both devices have internet. On slow connections set the stream bitrate to 3-6 Mbit/s in Settings.
The touchpad does nothing in some games
Some anti-cheat systems block remote input while their game runs. Windows also won't let it reach programs running as administrator, such as Task Manager or installers. You'll get a notification on the PC the first time it's blocked.
Why isn't the PC reachable when it's asleep?
A sleeping PC isn't on the network. Wake-on-LAN needs an always-on device on the same LAN, which is on the roadmap.
Does it work for other Windows accounts on the PC?
Partly. Hadal is set up for the account that installed it. Sleep, restart, shut down and status work whoever is logged in, but everything that needs the desktop (lock, touchpad, streaming, media, files, clipboard) only works while that account is signed in. To move Hadal to another account, uninstall it, then install it again from that account.
How do I uninstall it?
Double-click Uninstall.cmd in C:\Program Files\Hadal and accept the UAC prompt. It removes the service, the firewall rule, the startup entry, the program files and the settings. Files you transferred stay in Downloads\Hadal. On the phone, uninstall the app like any other.
GPL-3.0. Use it, change it, share it. If you distribute a modified version, its source has to stay open under the same license. Found a security issue? See SECURITY.md.



