nvdr lets you control a remote NVDA screen reader from a machine that isn't
running NVDA — a Linux box over SSH, a Mac, or an iPhone. It speaks the
NVDA Remote relay protocol as the master (controller): it hears what the
remote NVDA says and forwards your keystrokes back to it.
Disclaimer. This is a hobby project. I built it for personal use to see if the idea would work — nothing more. I'm aware of NVDA's native remote access and of the NVDA Remote controller app currently in TestFlight; this is not a replacement for either. What
nvdradds is (a) an SSH-bridged transport and (b) what I believe is better keyboard handling than that TestFlight app in some situations. Contributions are welcome — especially anything around security, but really anything at all.
This is a monorepo. Several independently-built components, different
languages, all speaking the same wire protocol (client_spec.md is the shared
contract):
| Path | What it is |
|---|---|
src/ + Cargo.toml |
The Rust terminal client. Also exposes nvdr --ipc, a line-oriented IPC bridge used by the apps below. |
addon/ |
An NVDA add-on (nvdrBridge) — lets a Windows NVDA use the SSH bridge. |
mac/ |
A native macOS SwiftUI app. |
ios/ |
An iOS SwiftUI app. |
The terminal client connects directly to the relay over TLS. The Mac app, iOS app, and NVDA add-on all go through the SSH bridge instead (see below).
The Mac app, the iOS app, and the NVDA add-on do not open a TLS connection
to the relay themselves. They launch ssh and run nvdr --ipc on a remote
bridge box, and that machine dials the NVDA Remote relay:
your device (Mac / iOS / Windows+NVDA)
│ SSH
▼
bridge box ── runs `nvdr --ipc` ── TLS ──▶ relay ──▶ remote NVDA
So to use the Mac app, iOS app, or add-on you need a reachable host (Linux,
macOS, or Windows) with the nvdr binary built and on its PATH, plus
key-based SSH auth set up (no password prompts — the apps can't answer one).
This bridge is useful when you want to encrypt the whole remote session over SSH, or when you simply can't reach port 6837 (firewalled networks, captive Wi-Fi, etc.) — your only outbound connection is SSH.
- The Mac and iOS apps use the SSH bridge unconditionally — there is no way to turn it off. They will not connect to a relay directly. If you want a direct connection, use the Rust terminal client. A contribution that lets the Mac and iOS apps dial the relay directly (no SSH bridge) would be very welcome — and I'll likely get around to it myself before long if there's demand for it.
- The bridge only works on the controller (master) side, not the host (slave) side. There is no way to make the machine being controlled run through this bridge — that machine still needs ordinary NVDA Remote.
- The bridge forwards keystrokes and speech only. Other NVDA audio — beeps, the forms-mode tone, progress-bar ticks, wave/tone effects — is not carried across, for now. You get speech text and that's it.
- Connecting NVDA's native remote access through this bridge is not implemented. If you'd like to add it, contributions are super appreciated.
A terminal client for the NVDA Remote relay. Designed to run on a VPS over SSH; works from any terminal on Linux, macOS, Windows, or iOS. This is the only component that talks to the relay directly (no bridge).
Requires Rust 1.75+.
cargo build --release # binary at target/release/nvdrDrop target/release/nvdr somewhere on your $PATH.
nvdr # localhost:6837, prompts for channel
nvdr --host relay.example.com --port 6837 --channel 123456789
nvdr --show-keys # dump the key reference and exitOn first connect nvdr pins the relay's TLS fingerprint (Trust On First Use)
into ~/.config/nvdr/known_hosts; later connects verify against it. Relay
certs are usually self-signed — that's expected. A cert change drops you into
an interactive re-pin prompt.
Type, and printable characters, arrows, F-keys, Tab/Enter/Esc/Backspace, and
any Ctrl+letter are forwarded to the remote NVDA — even Ctrl+C. Speech
prints as plain text lines (no ANSI color, screen-reader friendly).
A terminal can't send Insert or the Windows key, so there's a leader key
(default Ctrl+G, set with --leader):
| Shortcut | Sends | Meaning |
|---|---|---|
<leader> c |
Alt+F4 | close window |
<leader> w |
NVDA+T | read window title |
<leader> d |
Win+D | show desktop |
<leader> t |
Alt+Tab | switch window |
<leader> <leader> toggles a sticky NVDA modifier; <leader> : opens a
command prompt (:k win+r, :caps, :say <text>, :help, :quit, …).
There's also a one-shot scripting mode (-k / -s) — see
scripting.md.
CapsLock as a key, the Windows key alone, left/right modifier distinction, a
modifier pressed alone, most numpad keys. Use :k win+... for the Windows key.
nvdrBridge lets a Windows machine running NVDA reach a remote NVDA
through the SSH bridge instead of NVDA Remote's normal direct connection —
again, for SSH encryption or when port 6837 is blocked.
- Install: zip the contents of
addon/(somanifest.iniis at the archive root), rename tonvdrBridge.nvda-addon, install via NVDA's Add-on Store ("Install from external source"), restart NVDA. - Configure under NVDA → Preferences → Settings → nvdr Bridge (SSH host / user / key, relay host, channel key).
NVDA+F11toggles key forwarding between local and remote. While forwarding is on, every keystroke (exceptNVDA+F11itself) goes to the remote slave and the remote NVDA's speech is spoken on your local synth.
This is the controller side only — see the bridge caveats above. Full setup
and troubleshooting: addon/README.md.
A native SwiftUI app that bridges your Mac to a remote NVDA through the SSH
bridge. It installs a system-wide low-level keyboard hook
(CGEventTap + IOHIDManager).
NVDA+F11(e.g.CapsLock+F11) toggles forwarding on and off.- While forwarding is on, the app eats all keystrokes — including
Cmd+Q,Cmd+Tab, and other system shortcuts. They are sent to the remote machine, not handled by macOS. Turn forwarding off (NVDA+F11) to get your keyboard back. - The app requires Accessibility and Input Monitoring permissions to run the keyboard hook. It detects when they're missing and shows a banner with buttons that jump straight to the relevant System Settings pane.
- It is a Developer ID / non-sandboxed app — the keyboard hook can't work in the App Store sandbox.
A SwiftUI app that bridges an iPhone/iPad to a remote NVDA through the SSH bridge.
NVDA+F11toggles forwarding on and off, same as the Mac app.- While forwarding is on, the app captures all hardware-keyboard input — system combos included — and sends it to the remote machine.
Both Swift apps use xcodegen-generated Xcode projects:
cd mac # or: cd ios
xcodegen generate
xcodebuildThese apps will not be published on the App Store (the Mac one can't be sandboxed; the iOS one isn't headed there either). Build them yourself from source.
This started as a personal experiment, so there's plenty of room to improve it. Some things to look at— security review and hardening especially, but also better protocol coverage (carrying NVDA's non-speech audio, wiring NVDA's native remote access through the bridge), more keyboard handling, docs, anything. Open an issue or a PR.
client_spec.md— the wire protocol this project implements.scripting.md— the-k/-sone-shot scripting grammar.~/.config/nvdr/known_hosts— TLS fingerprint cache for the terminal client.
Same as the NVDA Remote project.