macOS hotfix for Razer Naga gaming mice - Fixes modifier keys not working with Naga's 12-button grid.
Without this hotfix, pressing Shift + Naga Button 1 outputs 1 instead of !. Naga Relay intercepts the button presses and re-emits them with the correct modifier state, so your keyboard modifiers (Shift, Cmd, Opt, Ctrl) work properly with Naga buttons.
Razer Synapse alternative | Razer Naga macOS support | Naga Trinity driver | Naga V2 HyperSpeed macOS | Gaming mouse button remapper | macOS Razer driver | Naga side buttons macOS | Naga modifiers not working fix
See SETUP.md for detailed setup and testing instructions.
Naga Relay is a lightweight daemon that captures input from Razer Naga mice and relays whitelisted button presses as keyboard events. It uses Apple's IOHIDManager for device-specific capture, meaning it cannot see your main keyboard.
+-------------------------------------------------------------+
| IOHIDManager (Device-Specific) |
+-------------------------------------------------------------+
| Main Keyboard --> System (untouched, invisible to daemon) |
| |
| Naga Keyboard --> IOHIDManager --> Whitelist --> CGEvent |
| | | |
| +-- SEIZED (exclusive) Query mods-+ |
| |
| - Cannot see main keyboard keystrokes |
| - Only receives events from Naga |
| - Queries modifiers on-demand (no tracking) |
| - Strict whitelist (12 inputs only) |
+-------------------------------------------------------------+
Only these 12 inputs are processed. All others are silently dropped:
| Naga Input | Output | HID Code |
|---|---|---|
| Button 1 | 1 |
0x1E |
| Button 2 | 2 |
0x1F |
| Button 3 | 3 |
0x20 |
| Button 4 | 4 |
0x21 |
| Button 5 | 5 |
0x22 |
| Button 6 | 6 |
0x23 |
| Button 7 | 7 |
0x24 |
| Button 8 | 8 |
0x25 |
| Button 9 | 9 |
0x26 |
| Button 10 | 0 |
0x27 |
| Button 11 | . |
0x37 |
| Button 12 | Backspace |
0x2A |
When you press a Naga button:
- The daemon queries the current modifier state from the system
- Creates a synthetic keyboard event with your key
- Attaches the current modifiers (Shift, Cmd, Opt, Ctrl)
- Posts the combined event
Example: Hold Shift on keyboard + press Naga button 1 = System receives Shift+1 = !
cd razer-naga-relay
swift build -c release# Normal operation
.build/release/nagarelay
# With debug logging
.build/release/nagarelay --debug
# With device seizure (recommended)
sudo .build/release/nagarelay- macOS 12.0+
- Swift 6.0+ (Xcode 16+ or Swift toolchain)
- Accessibility permission (System Preferences > Privacy & Security > Accessibility)
- Optional: sudo for exclusive device seizure
| Device | Connection | Vendor ID | Product ID |
|---|---|---|---|
| Razer Naga Trinity | USB | 0x1532 | 0x0067 |
| Razer Naga V2 HyperSpeed | USB | 0x1532 | 0x00B4 |
| Razer Naga V2 HyperSpeed | Bluetooth | 0x068E | 0x00B5 |
Run unit tests:
swift testRun specific suite:
swift test --filter KeyMappingTests- Whitelist enforcement (all 12 inputs)
- HID-to-virtual keycode mappings
- Modifier description helpers
- Error formatting
Naga Relay uses Apple's IOHIDManager API for device-specific input capture:
- Device Matching: Filters by Razer VendorID and Naga ProductIDs
- Device Seizure: Opens device with
kIOHIDOptionsTypeSeizeDevicefor exclusive access - Callback Registration: Receives raw HID values via input value callback
- Whitelist Filtering: Only 12 inputs pass through; all others dropped
- Event Synthesis: Creates CGEvent with current modifier state and posts to system
This approach cannot see your main keyboard - it only receives events from the Naga device.
Bluetooth Naga devices bypass IOHIDManager seizure, causing duplicate events. Naga Relay handles this with:
- Deduplication: Ignores identical events within 50ms window
- Event Tap: Blocks original Bluetooth HID events after relaying our version
- Event Marking: Our relayed events are marked so the tap lets them through
USB HID passthrough is not supported on Apple Silicon VMs:
- Tart/UTM: Apple Virtualization.framework lacks USB passthrough
- VMware Fusion: HID devices not reliably passed through on ARM64
Hardware integration tests must run directly on host macOS with physical Naga connected.
razer-naga-relay/
+-- Package.swift # Swift Package Manager config
+-- config.json # Reference config (not loaded at runtime)
+-- README.md
+-- SECURITY-AUDIT.md # Full security analysis
+-- SETUP.md # Setup and testing guide
+-- Sources/
| +-- NagaKeyRelay/
| | +-- main.swift # CLI entry point
| +-- NagaKeyRelayLib/
| +-- KeyMapping.swift # HID to virtual keycode mappings
| +-- NagaHIDRelay.swift # IOHIDManager relay engine
+-- Tests/NagaKeyRelayTests/
+-- KeyMappingTests.swift # Whitelist enforcement tests
+-- NagaHIDRelayTests.swift # Helper function tests
| Aspect | This Implementation |
|---|---|
| Main keyboard visibility | Cannot see any |
| Attack surface | Low (device-specific) |
| Modifier tracking | On-demand query |
| Whitelist | Mandatory (12 inputs) |
| Device isolation | Complete |
See SECURITY-AUDIT.md for a full line-by-line security analysis.