No description
  • Rust 93.5%
  • Nix 6.5%
Find a file
/u/efi bdebee5940
fix: login flow
Signed-off-by: /u/efi <carlo@swiss.dev>
2026-11-01 21:11:36 +00:00
.cargo fix: support windows authentication 2026-11-01 20:31:29 +00:00
docs feat(connect): native AVD session via IronRDP engine + winit window 2026-10-30 09:11:59 +00:00
src fix: login flow 2026-11-01 21:11:36 +00:00
.gitignore feat(connect): native AVD session via IronRDP engine + winit window 2026-10-30 09:11:59 +00:00
Cargo.lock fix: support windows authentication 2026-11-01 20:31:29 +00:00
Cargo.toml fix: support windows authentication 2026-11-01 20:31:29 +00:00
flake.lock fix: login flow 2026-11-01 21:11:36 +00:00
flake.nix fix: login flow 2026-11-01 21:11:36 +00:00
README.md fix(identity): default DRS enrollment to registered 2026-11-01 20:45:24 +00:00
rust-toolchain.toml feat(connect): native AVD session via IronRDP engine + winit window 2026-10-30 09:11:59 +00:00

winapp — Windows App for Linux

Connect to your Windows 365 / Azure Virtual Desktop / Dev Box VDIs from Linux with a native session window. Protocol-compatible with the Microsoft Store "Windows App" (MicrosoftCorporationII.Windows365 2.0.1485.0), which was reverse-engineered to derive the service contract (see docs/reversing-notes.md).

What it does

Windows App winapp
Entra sign-in (WAM/device) winapp register (once) + winapp login
Workspace discovery ARM feed discovery (msrdc UA) → MS-TSWP feed
List desktops/apps winapp workspaces / winapp list
Launch a session winapp connect <alias> — full native RDP session

Conditional Access (error 53003)

If your tenant requires registered/compliant devices, plain device-code sign-in fails with 53003 (Device state: Unregistered). On Windows the Store app passes because its tokens are brokered by WAM with the device's Entra registration + PRT. winapp reproduces that on Linux via libhimmelblau (software keys, no TPM needed):

winapp register contoso.com     # one-time: Entra device enrollment (DRS)
winapp login                    # user PRT bound to the enrolled device
winapp list                     # feed tokens via PRT exchange

Registration does not require your org to "support Himmelblau": it speaks the same MS-DRS protocol Windows uses when you add a work account on a personal PC, and "users may register devices" defaults to All in Entra. The default join type is "Azure AD registered" (BYOD parity). If sign-in logs show your policy filters on joined (not merely registered) devices, retry winapp register --joined.

If your tenant additionally requires Intune compliance (not just registration), run the full himmelblau daemon (distro package / NixOS module) so the device reports compliant; winapp uses the same identity primitives.

winapp connect performs the same flow the Store app does: ARM connection orchestration → reverse-connect WebSocket to the AVD gateway → TLS → RDP (CredSSP/RDSTLS/AAD) → live framebuffer in a native window (winit + softbuffer; Wayland/X11). The RDP engine is IronRDP (MIT/Apache-2.0), pinned to a specific revision; Entra token handling (including host proof-of-possession) is delegated to its OAuthAvdTokenProvider with device-code login.

Usage

nix run .#default -- login            # device-code: code + URL, sign in once
nix run .#default -- list             # your desktops / RemoteApps
nix run .#default -- connect SessionDesktop   # native session window
nix run .#default -- connect <alias> --headless   # CI/smoke: frames → exit
nix run .#default -- connect <alias> --out x.rdp  # also write .rdp handoff

Token cache: $XDG_STATE_HOME/windows-app/token-cache.json (0600).

Build

nix build          # static-ish closure, rustls only, no OpenSSL
nix develop        # fenix stable toolchain + rust-analyzer

Layout

src/cli.rs     clap surface (register|login|workspaces|list|connect)
src/identity.rs Entra device enrollment + user PRT (libhimmelblau)
src/token.rs   token cache + refresh
src/avd.rs     feed discovery → workspaces → resources (MS-TSWP)
src/engine.rs  IronRDP session: orchestration → WS gateway → RDP (own thread)
src/gui.rs     native window (winit + softbuffer), frame blit, Poll loop
src/rdpfile.rs .rdp generation for external engines (FreeRDP etc.)
docs/          reversing notes (evidence-cited)
packages/      downloaded Store MSIX + deps (gitignored, not redistributed)

Status / limits (v2)

  • Sign-in, feed, orchestration, native window: implemented.
  • Input (keyboard/mouse/resize) events are wired in the engine API but not yet forwarded from the window — the next milestone (RdpInputEvent::FastPath).
  • Clipboard, drive/audio redirection: engine supports them; wiring is future scope. UDP shortpath falls back to WebSocket automatically.
  • Verified against the protocol contract from the binary + IronRDP's own AVD integration tests; live-tenant end-to-end has not been run (no test account was provided). First connect against a real workspace is the remaining unknown.

Interoperates with Microsoft's public AVD endpoints for accounts you control. packages/ contains Microsoft's app binaries fetched from Microsoft's CDN for reverse-engineering; they are gitignored and not redistributed.