macOS menu bar app that forwards a physical keyboard and mouse from one Mac to another over the local network.
The Mac with the hardware attached captures keystrokes and pointer events, sends them to your other Mac over TCP/UDP, and replays them there. Toggle control with ⌘⇧K or the menu bar — the hotkey always works locally so you are never locked out.
It works across different Apple IDs (a work Mac + a personal Mac), doesn't depend on Handoff or iCloud, and gives you an explicit hotkey instead of edge-scroll — just a plain TCP/UDP connection over Bonjour with its own pairing token.
- macOS 13+
- Xcode Command Line Tools (
swift,xcodebuild) - Both Macs on the same local network (or reachable by IP)
- OpenKVM installed and running on both Macs
- Build or install OpenKVM on both Macs (see Install).
- On the Mac with the keyboard, open Settings and enable This Mac has the physical keyboard.
- On either Mac, open Settings → Other Mac and click Pair next to the discovered peer. Approve the dialog on the other Mac.
- Grant Accessibility, Input Monitoring, and Local Network when prompted.
- Press ⌘⇧K (or use the menu) to forward the keyboard to the other Mac. Press again to bring it back.
./build-app.shOutput: dist/OpenKVM.app (universal arm64 + x86_64)
For a stable code signature so Accessibility permission survives rebuilds:
./scripts/make-signing-cert.sh # once per Mac
./build-app.shchmod +x build-dmg.sh
./build-dmg.shOutput:
dist/OpenKVM.zip— preferred for your other Mac (avoids false "damaged" errors)dist/OpenKVM.dmg— drag-to-Applications installer
macOS shows that for unsigned apps — the file is not actually corrupted. Use the ZIP instead:
# On the other Mac, after copying OpenKVM.zip over:
unzip OpenKVM.zip
chmod +x install-on-mac.sh
./install-on-mac.shThen right-click OpenKVM → Open in Applications (first launch only).
If you still want the DMG:
xattr -cr ~/Downloads/OpenKVM.dmg
open ~/Downloads/OpenKVM.dmgOpen dist/OpenKVM.dmg, drag OpenKVM to Applications.
cp -R dist/OpenKVM.app /Applications/First launch: right-click → Open (unsigned build). Grant Accessibility, Input Monitoring, Post Event (on the receiver), and Local Network permissions.
| Mac | Settings |
|---|---|
| Owner (has keyboard) | Enable "This Mac has the physical keyboard" |
| Receiver (other Mac) | Leave that toggle off |
Pairing (automatic):
- Open Settings on either Mac.
- Wait for the other Mac to appear under Other Mac.
- Click Pair, then click Approve on the other Mac.
Manual (if Bonjour discovery fails): open Advanced, set the other Mac's IP and ensure both Macs share the same pairing token.
Use Test connection (Advanced) to verify reachability.
| Action | How |
|---|---|
| Forward keyboard & mouse to other Mac | Menu bar → "Switch keyboard to other Mac", or ⌘⇧K |
| Bring keyboard back to this Mac | Menu bar → "Switch keyboard back to this Mac", or ⌘⇧K |
| Open settings | Menu bar → Settings…, or ⌘, |
| Recover stale permissions / network | Menu bar → Refresh, or ⌘R |
| Component | Role |
|---|---|
| HIDInputCapture | IOKit HID Manager capture from one specific external keyboard + mouse (never the built-in trackpad/keyboard) |
| InputBridge | Orchestrates capture/injection, hotkey, media keys, CGEvent replay on the receiver |
| PeerNetwork | TCP (keys/buttons) + UDP (mouse move/scroll) listener, Bonjour discovery (_openkvm._tcp), wire protocol |
| ConfigStore | Persists pairing token, peer, owner flag in UserDefaults |
| SettingsView | SwiftUI settings: pairing, permissions, diagnostics |
Default TCP port: 9847. Messages are length-prefixed JSON.
For the full technical reference — data flows, wire protocol, TCC permissions, build pipeline, and debugging — see ARCHITECTURE.md.
# Run from source (debug)
swift run
# Test peer connectivity (app must be running)
python3 scripts/test-peer-ping.py 127.0.0.1 9847 <your-token>
python3 scripts/test-peer-setup.py 127.0.0.1 9847 <your-token>Sources/OpenKVM/ Application source (7 Swift files)
Resources/ Info.plist, app icon
scripts/ Installer, signing cert, test clients
build-app.sh Universal .app packaging
build-dmg.sh DMG + ZIP distribution
New here? These are scoped, self-contained, and a good place to start — the wire protocol is small and documented in ARCHITECTURE.md.
- Windows ↔ Mac support — a Windows client that speaks the existing protocol and injects events via
SendInput. - Linux ↔ Mac support — a Linux client injecting events via
uinput/evdev. - Mouse jitter during fast movement — sequence UDP mouse packets and coalesce deltas on the receiver.
See all good first issues.
Contributions are welcome — bug fixes, new platform clients, and docs.
- Fork and clone the repo.
- Build and run from source:
swift run(see Development). - Create a branch:
git checkout -b my-change. - Make your change. Keep the diff focused; match the surrounding style.
- Test against a real second machine, or with
scripts/test-peer-ping.py/scripts/test-peer-setup.py. - Commit, push, and open a pull request describing what changed and how you verified it.
For anything non-trivial, open an issue first so we can agree on the approach. The architecture reference explains the data flows, wire protocol, and permission model.
