# RDP FIDO — what's new

Client and gate are updated together; the client offers each new version on
start, the gate is updated from the client after a key-confirmed session
(`Automatically update the gate on this machine`). Downloads:
[RdpFido-Setup.exe](https://accounts.new-imobile.com/updates/RdpFido-Setup.exe)
(host or client), [RdpFidoClient-Setup.msi](https://accounts.new-imobile.com/updates/RdpFidoClient-Setup.msi),
[RdpFidoGate-Setup.msi](https://accounts.new-imobile.com/updates/RdpFidoGate-Setup.msi).
Русская версия: [CHANGELOG.ru.md](CHANGELOG.ru.md).

## 1.22.0 — 2026-09-28

- **RDP FIDO Cloud in the client: a gate behind NAT without port
  forwarding.** A gate linked to the cloud gets an address of the form
  `<id>.g.<domain>` (port 443); enter it as the server and gate address and
  everything else stays as it was: the same key, the same pinned
  certificate thumbprint, the same check. Through the cloud the gate issues
  a one-time tunnel ticket instead of a firewall rule, and the client
  carries RDP through the hub: a listener on `127.0.0.1`, `mstsc`
  (xfreerdp on Linux) connects to it, and the bytes travel to the hub in
  encrypted records (ECDH + AES-256-GCM on top of RDP's own TLS and NLA).
  The hub sees addresses and volumes only. The tunnel lives as long as the
  RDP window; its listener stays up for the ticket's lifetime and takes up
  to four connections through the same ticket, since mstsc drops the
  connection and dials again after its own prompts and on auto-reconnect
  (the hub and the gate count uses; the gate also insists on the address
  the ticket was issued to). The gate reports the certificate of its RDP
  listener in `auth/finish` (`rdpCertSha256`, `rdpCertSha1`) and the client
  pins it for `127.0.0.1` before launching mstsc (`HKCU\...\Terminal Server
  Client\Servers\127.0.0.1\CertHash`, what "don't ask me again" writes) -
  the identity prompt does not appear, `authentication level` stays 2; the
  Linux client hands xfreerdp `/cert:fingerprint:sha256:...`. The status
  line and the tile say "via cloud"; the session dialog explains the
  address form and the probe reports the gate's public name.
- The client names its version in every gate request (`clientVersion`); a
  gate reached through the cloud answers a client older than 1.22 with a
  readable message (`client_too_old`), and likewise when its link to the
  cloud is down right now (`cloud_unavailable`).
- Without the cloud (a gate that is not linked, a reply without `tunnel`)
  the client behaves exactly like 1.21.
- **The cloud as an option - gate side.** A gate can be linked to an RDP FIDO
  Cloud hub (your own `rdpfido-cloud` or the Imobile cloud):
  `RdpFidoGate.exe cloud link HUB CODE` (Linux: `rdpfido-gate cloud link`),
  `cloud unlink`, `cloud status`. Until linked the gate makes no outgoing
  connection at all and behaves exactly as before. A linked gate keeps one
  connection to the hub (reconnecting with a 1 -> 60 s back-off) and takes
  the clients' HTTPS through it into its own listener, substituting the
  client's real address (country filter, rate limits and the challenge's
  address binding work as usual). RDP through the cloud is opened **by a
  tunnel ticket instead of a firewall rule**, for the grant window (up to
  four connections, from the client's address only): the `auth/finish`
  reply carries `tunnel`, port 3389 stays closed from outside as it was. `/v1/hello` reports `cloud {linked, connected, publicHost,
  publicPort}`; the hub's relays are added to the gate's own in
  `stream/open`. Clients older than 1.22 get `client_too_old` through the
  cloud; with the hub link down the gate answers `cloud_unavailable` (503).
  State lives in `cloud-state.json`. `RdpFidoGate.exe cloud selftest` runs
  the protocol against a built-in fake hub (Windows and Linux).
- **The Imobile cloud is a private service.** In 1.22.0 the cloud mode works
  with your own `rdpfido-cloud` hub (self-hosted: the binary, the unit and
  `install.sh` ship as `rdpfido-cloud-1.22.0-linux-x86_64.tar.gz` next to the
  Linux packages, described in `README-cloud.ru.md`) or with the Imobile
  cloud. The Imobile cloud is not open to everyone: Imobile links a gate to
  it by arrangement, and demo access for connecting is available to get
  acquainted (the demo machines through the cloud). A gate and a client that
  are not linked to a hub work exactly as before.
- **Cloud:** the hub `rdpfido-cloud` gets a cabinet - organisations, members, roles, member-to-gate rights with conditions (days, hours, countries, expiry), signed rights manifests for the gates, a journal, a web UI on `adminPort` and a JSON API; sign in with an Imobile account or with the server's own logins (`rdpfido-cloud user`). See `docs/CLOUD-CABINET.md`.
- **Shared stream sessions - the viewer side.** Several participants join
  one FIDO Stream session; one of them is in control. The viewer shows who
  (a corner hint at every change) and, while it is not you, a bar "View
  only - Ctrl+Alt+R: request control"; it then sends the host nothing, and
  the mouse over the picture becomes a pointer everyone sees (a ring with
  your name, one colour per participant). The leader gets "<name> asks for
  control: Hand over (Enter) / Decline (Esc)", declined by itself after
  30 s. The session menu gains "Participants…", "Request control", "Hand
  over control ▸ <name>", "Give control back to the host" and "Invite…"
  (a registration code for a guest who may lead or only watch, with the
  gate address and expiry, and a "Copy" button). The overlay (Ctrl+Alt+O)
  shows `participants: N, control: <name>`. On Linux the same through
  keys: Ctrl+Alt+R, Ctrl+Alt+Shift+P/H/I, Enter/Esc; `rdpfido stream`
  prints participant events. With a 1.21 peer nothing changes.
- The client sends `share: true` and `role: lead` in `stream/open` and
  hands the viewer the reply's `participant` / `inviteToken`; a 1.21 gate
  sends none, and the viewer behaves as before.
- To look at it without a 1.22 host: `RdpFidoViewer.exe … --demo-share` /
  `rdpfido-viewer … --demo-share` (or `RDPFIDO_VIEWER_DEMO_SHARE=1`) play
  a scripted three-participant session.
- **Shared sessions in FIDO Stream.** Several participants (up to 8, 4 by
  default) join one screen and exactly one of them controls it: the first
  to connect with a "lead" key leads, the others watch, listen and point; a
  watcher asks for control and the leader hands it over or refuses (an
  unanswered request fades after 30 s); the person at the host takes
  control with Ctrl+Alt+Shift+H, and `RdpFidoGate.exe stream control
  host|<id>` sets the leader outright. Input, clipboard, files, the
  microphone and quality settings are taken from the leader only - on the
  host, not merely in the client window. The screen is captured and
  encoded once; every participant has its own keys, port and firewall
  rule. When the leader disconnects, control goes to the host or to the
  first in the queue (`"streamLeaderLeft"`); a returning leader gets it back.
- **Invitations.** The leader mints a 15-minute invitation code
  (`/v1/stream/invite`); the guest enrols a key with it and gets the role
  "watch" or "lead" and a lifetime (8 hours by default), after which the
  gate removes the key itself. Ordinary keys have a role too: `key-role
  <number|label> lead|watch`; `keys` shows it.
- Gate: `"streamShared"`, `"streamMaxParticipants"`, `"streamLeaderLeft"`,
  `"inviteTtlSeconds"` in `gate.json`; `stream participants`; the stream's
  UDP ports are base..base+7 (was +3). The `stream/open` reply carries
  `participant` and `inviteToken`. A 1.21 side works as before.

## 1.21.0 — shipped as part of 1.22.0 (never released on its own)

- **File transfer in FIDO Stream, both ways, across operating systems.**
  Drop files or folders on the stream window and they land on the host in
  `Downloads\RDP FIDO` (`~/Downloads/RDP FIDO` on Linux, per XDG). The same
  from the session menu: "Send files to the host…". The other way: anything
  put into `Downloads\RDP FIDO\Outbox` on the host arrives in your own
  "RDP FIDO" folder and moves to `Outbox\Sent` on the host. Windows↔Linux in
  any combination: UTF-8 names, either path separator, modification time
  kept, the execute bit on Linux.
- **The transfer does not get in the way.** Files ride a third reliable
  channel of the stream that is sent only when no video, audio or input is
  waiting, at its own rate: a controller raises it while the path's delay
  stays flat and halves it as soon as the delay grows - before the video
  controller would notice. Picture and responsiveness stay as they were; the
  file takes what is left, tens of megabytes per second on a LAN. Progress
  is in the window title and the menu (with "Cancel file transfers"); every
  finished file is announced by a hint.
- **Safety.** Files land only in the "RDP FIDO" folder: names are sanitised
  (`..`, drives, forbidden characters, device names), clashes get a "(2)"
  suffix. A host can refuse files altogether with `"streamFiles": false` in
  `gate.json` (the viewer has `--no-files`). Until a file is complete it
  sits next to its destination as `.part` and is removed on failure.
- With a 1.20 peer the stream works as before, only without files (the
  client says "the host does not accept files").

## 1.20.0 — 2026-09-26

- **RDP FIDO for Linux.** `rdpfido-gate` for Ubuntu, Astra Linux and RED OS
  keeps the xrdp port closed with its own nftables rules (iptables where
  nftables is missing), IPv4 and IPv6, and opens it only for the address that
  passed the FIDO key check - the same checks and the same protocol as the
  Windows gate. `sudo rdpfido-gate setup` does it all: certificate, service,
  xrdp starting only after the gate, ufw/firewalld ports, enrollment code.
- **A Linux client**: the `rdpfido-gui` window and the `rdpfido` command
  line. Any FIDO2 USB/NFC key (through libfido2), FreeRDP for the session,
  passwords in the desktop keyring. Works with Windows and Linux gates.
- **Windows to Linux.** The Windows client recognises xrdp on the other side
  and runs mstsc without NLA (xrdp has none; TLS stays), and never tries to
  push a Windows gate binary to a Linux host.
- **Fixed (security, the Windows gate too).** A key removed with `remove-key`
  kept opening the port until the service restarted, and the next use of any
  key wrote it back into the list. Removal now takes effect at once. Update
  the gate.
- **Fixed (the Windows gate too).** Two simultaneous authorisations from one
  address could leave a firewall rule that nothing tracked and that therefore
  stayed until the service restarted. No longer - checked under a load of up
  to 150 authorisations per second.
- The Linux gate closes the RDP port at boot, before the network and before
  xrdp; if it cannot, xrdp does not start. Port 3389 is opened in
  ufw/firewalld only once the gate guards it, and `uninstall` puts
  ufw/firewalld back the way it was.
- Enrolling a key from the Linux client can check the gate's thumbprint up
  front (`rdpfido enroll … --fingerprint`, a field in the window), so the
  enrollment code never reaches an impostor.
- Gate (Windows too): the service and the command line can no longer rewrite
  `gate.json` at the same time and lose each other's changes.
- **FIDO Stream on Linux, both ways.** A Linux machine can stream its
  desktop (the agent ships with the gate: X11 capture, H.264, sound, input,
  gamepads) and watch a stream from Windows or Linux (`rdpfido stream`, the
  "Stream" button, `rdpfido-viewer`). The Windows client streams to a Linux
  machine exactly as to a Windows one. The stream ports open only to the
  address that confirmed with the key. Details: [LINUX.md](LINUX.md).
- **One year without an update, at most.** Every version works for a year
  from its release date, then requires an update. A client stops connecting:
  the Windows client downloads and installs the new version itself, the Linux
  client names the package upgrade command. A gate still checks the key but
  opens no access until updated: the Windows client then pushes the new gate
  version right away and connects again; a Linux gate is upgraded through its
  package (the command is in the message and in `status`). Warnings start 30
  days before. Setting the clock back does not extend the year. Key enrollment
  and the emergency `unlock` keep working after it.
- The Windows client no longer calls a session list saved with a byte-order
  mark (PowerShell, older Notepad) corrupt.

## 1.19.3 — 2026-09-20

- **No more false "foreign access to RDP" behind a port forward.** Once a
  minute the client checks that the RDP port does not answer without a key.
  When the host is reached through a port forwarder (netsh portproxy, an SSH
  or userspace relay, some routers), the forwarder itself accepts every
  connection, so the check raised the red alarm although the RDP port behind
  it was closed. The client now requires a real RDP handshake reply before it
  calls the port open. A port that is genuinely open without a key is still
  reported. Only the client changes; the gate stays as it is.

## 1.19.2 — 2026-09-20

- **The session menu is back, and so is everything else 1.19 had dropped.**
  1.19.0 and 1.19.1 were built from a development line that had never received
  four earlier releases, so updating to 1.19 silently took away: the in-session
  menu (Ctrl+Alt+Shift+M) and its hotkeys, the bar at the top edge in full
  screen, Ctrl+Alt+Del to the host, the pointer-position fixes, "host
  resolution follows my screen", and the gate fix that keeps a stream
  connection from opening the RDP port. All of it is merged back, together
  with everything 1.19 added (exact refresh rates, the metrics overlay, AV1 /
  10-bit / HDR, Opus, relays, the gamepad).
- **Update the gate as well.** The two lines had used the same two internal
  message numbers for different things. With a 1.19.0-1.19.1 client and a
  1.18.1 gate, connecting pressed Ctrl+Alt+Del on the host and asked it to
  change display mode once a second. 1.19.2 retires both numbers; until both
  sides are 1.19.2 the stream runs with the 1.18 feature set (no AV1, 10-bit,
  HDR or exact refresh rate), and nothing worse.
- Releases are now checked by looking at the stream window itself - a test
  card must arrive upright and in colour, the menu must open - and the
  publishing script refuses to build from a tree that lacks anything released
  before.

## 1.19.1 — 2026-09-20

- **FIDO Stream showed a black window.** The session connected, the title bar
  and the overlay reported healthy frame rates, but the picture itself stayed
  black on every machine and with any codec or quality setting: the viewer
  discarded the video rectangle before drawing it. Fixed; the remote pointer,
  drawn the same way, is visible again too. Only the client changes - the gate
  needs no update for this.

## 1.19.0 — 2026-09-16

- **120 / 144 / 240 Hz.** The stream now runs at your monitor's exact refresh
  rate (143.856 Hz stays 143.856, not 144): the host captures and encodes in
  that rhythm with a sub-millisecond timer, and the viewer either shows each
  frame the moment it is decoded ("Immediate": lowest latency, tearing
  allowed) or on the next refresh ("Smooth": no torn or doubled frames).
  "Automatic" chooses Smooth when the stream runs at the display's rate. The
  mouse is forwarded event by event at its own polling rate (1000 Hz pads
  included); events that piled up while the window was busy are merged without
  adding delay. Software encoders are held to 60 fps.
- **Metrics overlay and trace.** Ctrl+Alt+O shows what every millisecond is
  spent on: capture, convert, encode, network and assembly, decode, wait for
  the refresh, render; bitrate, loss, FEC parity and repairs, NACKs, RTT,
  queue delay, datagram size, relay in use; present rate and jitter; and a
  click-to-photon estimate measured by tagging your clicks and key presses and
  finding the first frame the host captured after injecting them (the
  display's own response time is not included). Ctrl+Alt+T records a CSV
  trace (one row per frame with every timestamp, one per second with the link
  counters, one per event) under `%LOCALAPPDATA%\RdpFidoClient\traces`; the
  session can also start with the overlay on or a trace running.
- **AV1, 10-bit, 4:4:4 and HDR.** New session settings "Codec" (auto / H.264 /
  HEVC / AV1) and "Colour" (auto / 8-bit / 10-bit / 4:4:4 / HDR). AV1 encodes
  on NVENC (RTX 40) and on the AV1 encoders of Intel Arc and AMD RDNA3, and
  decodes on NVDEC or a hardware decoder. 10-bit HEVC/AV1 removes banding in
  gradients; 4:4:4 keeps thin text sharp. With HDR switched on in Windows on
  the host and an HDR display on the client, the desktop is duplicated in its
  FP16 form, converted on the GPU to 10-bit PQ BT.2020 and shown as HDR (or
  tone-mapped to SDR on an SDR display). Everything degrades automatically to
  what both ends support; "Automatic" picks AV1 or HEVC when the link is the
  bottleneck, H.264 when the computers are.
- **Opus audio.** Host sound travels as Opus at 128 kbit/s (restricted-low-
  delay mode, 10 ms frames) and the microphone at 48 kbit/s; a lost packet is
  patched from the next one's in-band FEC. libopus 1.5.2 (BSD-3) is built into
  the executables; older peers keep ADPCM.
- **Relays for symmetric NATs.** When neither side can be reached directly,
  the stream goes through a relay of your own: `rdpfido-relay` for a Linux VPS
  (`src/relay`, `make install`, systemd unit) or `RdpFidoGate.exe relay serve`
  on Windows. List relays in the gate's `gate.json` (`"streamRelays":
  [{"address": "relay.example.com:7443", "name": "eu-1"}]`); both sides probe
  them and use the nearest after giving the direct paths a 1.5 s head start.
  The relay forwards end-to-end encrypted datagrams only and holds no key.
- **WAN tuning.** Congestion control scales its delay thresholds with the
  path's base RTT (12 ms on a LAN, up to 50 ms across continents), the repair
  budget stretches with the RTT, FEC grows on long paths. MTU discovery raises
  the datagram from 1200 to up to 1472 bytes when the path takes it (fewer,
  larger shards), re-checked every 30 s and dropped back if the path changes.
- Wire compatible with 1.15–1.18: older peers ignore the new messages.

## 1.18.1 — 2026-09-13

- **A FIDO Stream connection no longer opens the RDP port.** Authorising a
  stream also opened the RDP grant, which a stream never uses; a lingering
  RDP connection could then keep it open and the client showed a false "RDP
  open to anyone" alarm on the tile.
- **Host resolution can follow your screen** (off by default; a checkbox in
  the session settings and a row in the session menu): the host switches to
  your display's mode, or the largest one of the same proportions, and
  switches back when the session ends.

## 1.18.0 — 2026-09-16

- **Gamepad in FIDO Stream.** Plug an XInput gamepad (Xbox One/Series,
  Xbox 360, most pads with an "X" switch) into the client: on the host it
  appears as an Xbox 360 controller, games notice nothing, and vibration comes
  back to your pad. Up to four pads per session. Like the keyboard, the pad
  drives the host only while the stream window is in front.
- **Based on ViGEmBus.** The virtual controller is the open-source ViGEmBus
  driver by Nefarius Software Solutions (BSD-3-Clause), the same one Sunshine,
  Apollo and DS4Windows use. The project was archived at the end of 2023; its
  last release (driver 1.21.442.0, signed by Microsoft/WHQL) installs on
  current Windows 10/11 x64 builds without test mode and with HVCI on. The
  package travels inside `RdpFidoGate.exe`: `setup` installs it, a gate
  updated in place installs it at the next service start, and it is removed
  by `uninstall` only when this gate installed it - a ViGEmBus that another
  program brought stays. `RdpFidoGate.exe gamepad status|install|remove`
  manages it; `RdpFidoGate.exe stream gamepad-test` checks the whole host
  path (plug in, XInput sees it, rumble comes back, unplug) without a viewer.
  If the driver cannot be installed the gate and the stream still work, only
  the pad does nothing on that host.
- Both installers ship `THIRD-PARTY-LICENSES.txt` (ViGEmBus, the ViGEmClient
  headers, the NVIDIA codec headers) next to the executable.
- A `gate.json` saved with a UTF-8 byte-order mark (Notepad, Windows
  PowerShell) no longer stops the service; the step numbers of `setup` now
  survive in the Windows Installer log.

## 1.17.2 — 2026-09-12

- **The pointer landed beside where you aimed.** The host agent was not
  DPI-aware, so with display scaling above 100% absolute mouse input was
  mapped to a smaller screen than the one captured; and with the mouse
  captured the viewer placed the host's pointer by the video size rather than
  the host's, so a scaled-down stream drew it short.

## 1.17.1 — 2026-09-12

- **Session menu and hotkeys.** Ctrl+Alt+Shift+M opens a menu in the stream
  window: mouse capture, full screen, quality balance, Ctrl+Alt+Del,
  disconnect. Hotkeys on three modifiers, which no game uses: +Z mouse, +X
  full screen, +D Ctrl+Alt+Del, +Q disconnect. Ctrl+Alt+End sends
  Ctrl+Alt+Del like mstsc. In full screen a bar slides out at the top edge.
- The stream window was black on machines without a GPU.

## 1.17.0 — 2026-09-11

- **One file per side.** The FIDO Stream host agent now lives inside
  `RdpFidoGate.exe` and the stream viewer inside `RdpFidoClient.exe`. Both
  installers and the auto-update ship exactly what a stream needs; nothing has
  to be copied by hand any more. The gate starts itself as `stream serve` in
  the user's session, the client starts itself as `viewer` in a child process,
  so a decoder or driver crash closes only the stream window.
- On an installed host, `RdpFidoGate.exe stream encoders`,
  `stream encoders --bench`, `stream selftest` and `stream audio-devices` are
  available for diagnostics.
- Self-update of a running stream: the old image is renamed aside before the
  new one is copied in, so an update no longer fails while a stream is open.
- **Gate recovers from a hung Windows Firewall service.** If a call into the
  firewall service never returns, a watchdog ends the gate after 90 s and the
  service manager restarts it, instead of the gate answering every request
  with "the firewall worker did not respond in time" until someone notices.
  Grants recorded from the firewall thread expire on schedule even when the
  caller stopped waiting; rule removals are never dropped.

## 1.16.1 — 2026-09-11

- HEVC is decoded on the GPU by a safe route: NVIDIA NVDEC on NVIDIA, a
  hardware DXVA decoder on Intel and AMD. Microsoft's software HEVC extension
  is never used (it crashes), so a viewer without a suitable GPU falls back to
  H.264 instead of crashing and writes the reason to `client.log`.
- The H.264 Media Foundation decoder now runs on the GPU (DXVA) as well.

## 1.16.0 — 2026-09-10

- **Encoder and decoder are selectable per session.** `Encoder (host)`:
  Automatic / NVIDIA NVENC (direct) / Hardware (any GPU) / Software (CPU);
  `Decoder`: Automatic / Hardware (GPU) / Software (CPU). NVENC talks to the
  driver directly, the hardware option is any vendor's Media Foundation
  encoder (Quick Sync, AMF), software is Microsoft's H.264.
- `encoders --bench` measures every backend on synthetic frames without a
  screen or a network.
- Two latency fixes: the desktop-duplication wait no longer stalls the
  encoder thread, and the NVIDIA encoder's low-latency settings are applied
  where it honours them. 2560×1600 encode went from 60 ms to 6.5 ms per frame;
  LAN end-to-end from 380 ms to 25–45 ms.

## 1.15.0 — 2026-09-10

- **FIDO Stream** — a second connection mode next to RDP for games and other
  work where "click-to-photon" latency matters most. Video, audio and input
  go over UDP: AES-256-GCM on keys derived from ECDH bound to a ticket the
  gate issues only after the FIDO2 key confirms; Reed–Solomon FEC instead of
  retransmissions; a reliable channel for input; congestion control.
- Host side: DXGI desktop duplication, hardware encode (NVENC / Quick Sync /
  AMF through Media Foundation) with a zero-copy D3D11 pixel path, WASAPI
  audio, scan-code input injection. Viewer side: hardware decode, D3D11 flip
  rendering without vsync, raw input, mouse capture (Ctrl+Alt+Home), full
  screen (Ctrl+Alt+Enter), disconnect (Ctrl+Alt+End).
- One **Balance** slider from "poor link" to "weak PCs" with an automatic
  mode; two-way audio including the microphone; the host can dial the client
  through most NATs using address candidates exchanged over the gate's HTTPS
  channel. UDP 7441 is opened only for the confirmed address, only for the
  session.

## 1.14.0 — 2026-09-03

- **One combined installer**, `RdpFido-Setup.exe`, asks a single question —
  is this computer the host (connected to) or the client (connected from) —
  and installs the right half. Unattended: `RdpFido-Setup.exe host|client`.
  The client stays a per-user package (no admin rights), the gate a
  per-machine one (service).

## 1.13.0 — 2026-09-01

- The gate ships as an MSI, `RdpFidoGate-Setup.msi`: it enables Remote Desktop
  with NLA, binds the certificate to port 7440, registers the service and
  shows the one-time key enrollment code on its last page, in the Windows
  display language. Silent: `msiexec /i RdpFidoGate-Setup.msi /qn`.

## 1.12.x — 2026-08-30

- Signed license and update manifests, image banners chosen by the server
  with offline rotation, RDP port control, failed gate calls logged in
  `client.log`.
