Skip to content

Platforms

The operating-system behavior that trips up anyone scripting the Create: port names and exclusivity, dead and ghost ports, the boot pass through the bootloader, bootloader-port quirks, USB capture, Linux permissions and browsers. The one thing to know: our hardware work ran on Windows 11 (keyboard firmware 3.28.7, 3.35.4 and 3.41.0); macOS rows come from the vendor's software and documents or are inferred from our Windows work, and Linux rows from nayactl, createflow-dongle and OpenFlow's packaging. Nobody in this research has used a Create on Linux or macOS with our own tools.

At a glance

  • One program owns a port at a time on every OS; quit NayaFlow before scripting.
  • Every power-on passes through the bootloader for 1-1.7 s: scan twice before calling a half "in recovery".
  • On Windows use pyserial with dsrdtr=False and a write timeout.
  • A USB capture records everything typed; never publish a raw capture.

What the keyboard looks like to a host

Fact Evidence
On firmware 3.41.0 each half shows one CDC serial interface (MI_00); the left half also shows an HID interface (MI_02); the right half shows no HID while both halves are on USB (its keys travel over the split link). MEASURED (owner's board, 3.41.0, Windows, 2026-09-01)
On firmware 3.28.7 each half shows two CDC interfaces with the same USB serial (right MI_00 + MI_02, left MI_00 + MI_03); both look healthy but only one answers, and which one is not predictable (donor board: right MI_02, left MI_03). Group ports by USB serial and try the siblings. MEASURED (donor board, 3.28.7, Windows, 2026-09-19)
Every power-on passes through the bootloader for about 1-1.7 s (right at PID 0x00D3 for 0.98 s, left at 0x006F for 1.40 s on 3.41.0; 1.6-1.7 s on 3.28.7), so a single scan can report a half "in recovery" that is only booting: look twice. When one half powers on and re-links, the central re-enumerates for about 2 s (a reboot through the bootloader). MEASURED (owner's board 3.41.0, donor board 3.28.7, 2026-09-22)
Docking or undocking a module re-enumerates that half's USB; open handles go stale and USB device addresses change on every re-enumeration. NayaFlow relaunches itself when a module is docked again. MEASURED (owner's board, 3.41.0)

VID, PIDs and interfaces are on USB.

Windows

Symptom Cause and fix Evidence
"Access is denied" (error 5) when opening a COM port Another program holds it: NayaFlow's NayaCore, OpenFlow's backend, or a Python process left by a killed script. Quit it; check Task Manager for stray python.exe. MEASURED (owner's machine)
The first write blocks forever pyserial's DSR flow control: the keyboard never asserts DSR. Open with dsrdtr=False and a write_timeout. MEASURED (owner's machine, 3.41.0); nayactl pull request #22
"WriteFile failed (PermissionError(13, 'The device does not recognize the command.'))" A dead handle after a power cycle with the port still open. Close and reopen. MEASURED (owner's machine, 2026-09-10)
A COM port that looks live but never answers A "ghost" port after an unclean detach: the half came back on a new COM number while the old one stayed listed, even after an app restart. MEASURED (owner's machine, 2026-09-18)
Bootloader port: reset_input_buffer() fails with "ClearCommError failed / The device does not recognize the command" Drain by reading instead. The port can be refused for a moment right after it enumerates, and a second open milliseconds after the first is refused: use one discovery path and retry (OpenFlow tries 4 times 0.35 s apart). After the last upload chunk the port throws ClearCommError or times out for tens of seconds while the bootloader acts on the trailer, then re-enumerates. MEASURED (owner's machine, 2026-09-20); create-legacy-firmware3
A tool is slow against the bootloader Read what is available instead of waiting for a fixed length: a client that read 256 bytes for a 31-byte reply waited out its whole timeout on every request, rejected complete replies for missing base64 = padding, and reopened the port per request (first exchange: a timeout, then 0.974 s, then 0.078 s warm). Fixed, a round trip took 36 ms and a 648 KiB upload about 2 minutes instead of 1 h 50 min. MEASURED (owner's machine, 3.35.4/3.41.0, 2026-09-20)
LEDs and layer switching work, nothing types, no VID_37D1 device appears A failing cable or Y-cable leg: Device Manager shows "Unknown USB Device (Device Descriptor Request Failed)", code 43, VID/PID 0000:0002; two failed devices under one hub at the same instant means both legs of a Y-cable failed together. Reseat at the host end, try one plain USB-C cable to the left half, use a port on the machine itself. MEASURED (first-hand diagnosis on Windows, 2026-09-08)
Charging and updates fail through a hub or a Y-cable The vendor asks for USB 3.0 ports ("5V-1A" for charging) and two direct USB-C to USB-C cables; Y-cables "are known to cause issues" for firmware updates and pairing. DOC (Create manual v1.1.x p.4 and p.254; NayaFlow 1.25.1 update dialog5)
  • NayaFlow keeps its data in %APPDATA%\NayaFlow\; its background server's port changes at every launch and is found through that process's listening socket STATIC MEASURED (details on App data and NayaFlow and NayaCore).
  • USBPcap: start it from an already elevated PowerShell (it elevates itself, so -Verb RunAs breaks it); hub numbering moves between sessions; the IN endpoint (0x82) is logged one byte per transfer, so concatenate before decoding; a capture includes the HID interface and so records everything typed. In one setup a USB hub or dock gave the halves and the dongle power but no data: capture on a motherboard port MEASURED INFERRED (owner's machine, 2026-09-11).
  • nayactl on Windows: the nayactl.cmd launcher tries .venv\Scripts\python.exe, then py -3, then python, each with -B -m nayactl, and works only once the package is installed in that interpreter; nayactl -v status crashed on a cp1252 console (an arrow character); always pass --side STATIC MEASURED1.

Captures record keystrokes

Stop typing passwords while capturing, and never publish a raw capture: it also carries USB serial numbers and Bluetooth addresses.

macOS

Fact Evidence
Each half's port appears as a /dev/cu.usbmodem* node (how macOS names a USB CDC serial port). A half in its bootloader has two CDC ports, the SMP data port and a log port, so expect two nodes for it. While NayaFlow runs, opening a half's port fails with "Resource busy" (EBUSY): NayaCore opens its ports through Qt's serial-port module, which opens them exclusively. INFERRED STATIC (macOS naming of USB CDC ports; the two bootloader ports measured on Windows, 2026-09-16 and 2026-09-20, see Bootloader; the exclusive open, see Troubleshooting); not checked by us on a Mac
NayaFlow's data folder is ~/Library/Application Support/NayaFlow/; NayaCore sits inside NayaFlow.app/Contents/core/NayaCore.app/. STATIC INFERRED (the release zip's layout; the app name NayaFlow and Electron's userData convention)
Instrumenting NayaCore with DYLD_INSERT_LIBRARIES needs a re-signed copy: NayaCore is signed with the hardened runtime (CodeDirectory flag 0x10000), and its entitlements (allow-jit, allow-unsigned-executable-memory, disable-library-validation) do not allow DYLD environment variables. STATIC6
macOS has no F21-F24 key codes, so function-key schemes (Create Companion) use F14-F20 there. DOC9
Apple platforms hold non-Apple Bluetooth devices to a 15 ms minimum connection interval (the vendor's BLE v2 reaches 7.5 ms elsewhere). DOC (NayaFlow 1.19.1 release notes7)
Vendor-noted macOS issues: Cmd+Return created an empty window on macOS Tahoe 26.2 (disabled in 1.17.2); diagnostics failed on macOS 26 (fixed in 1.25.0); arm64 Macs auto-updated to the Intel build (fixed in beta 1.17.2). DOC (release notes78)

Linux

Fact Evidence
Each half is a /dev/ttyACM* port owned by root and group dialout (uucp on Arch), and so is a half in its bootloader; nayactl's README runs as root or asks for the dialout group, and calls Linux its most exercised platform. STATIC (OpenFlow's udev rule and README) REPORTED (nayactl README1); not measured by us
On Linux EACCES/EPERM on open means missing permission, not a busy port. STATIC (OpenFlow, a code note); not measured on a Linux machine with a keyboard attached
ModemManager probes new CDC serial ports for several seconds after plug-in and collides with the keyboard's protocol; the udev rule below tells it to ignore the keyboard. STATIC INFERRED (OpenFlow's rule and README); tested in packaging only
OpenFlow's Linux builds: the AppImage needs libfuse2 (libfuse2t64 on Ubuntu 24.04, libfuse2 on 22.04) or --appimage-extract-and-run; on Ubuntu 24.04 and later AppArmor blocks Electron's sandbox and the .deb installs an AppArmor profile; the builds target the glibc of Ubuntu 22.04, Debian 12, Fedora 36 and newer. STATIC (OpenFlow README)
What Naya shipped for Linux: an AppImage only for 0.0.1 to 0.1.1 (without a firmware service), and Linux NayaCore builds inside 1.15.0 to 1.17.3 ("Ubuntu 20.04+", X11 libraries) that embed older keyboard images (3.28.7) than Windows and macOS; the BLE v2 notes list Debian Linux among tested hosts. STATIC DOC (installers; 1.17.3 Linux README; 1.19.1 notes7); see History
nayactl's NayaCore (ZMQ) discovery is Linux-only: it looks for the shared-memory port in /dev/shm/qipc_sharedmemory_*<key>*, /tmp/<key>, /tmp/qipc_sharedmemory_*<key>* and checks /proc/*/cmdline; there is no ZMQ transport. STATIC1
createflow-dongle's flasher is tested on macOS, Windows and Ubuntu 24.04; its dongle on macOS and Ubuntu 26.04. DOC11

OpenFlow's udev rule (file /etc/udev/rules.d/70-openflow.rules for the AppImage, installed under /usr/lib/udev/rules.d/ by the .deb) STATIC (OpenFlow, link at release); tested in packaging only, not yet with a keyboard on a Linux seat:

ACTION!="remove", SUBSYSTEMS=="usb", ATTRS{idVendor}=="37d1", ENV{ID_MM_DEVICE_IGNORE}="1"
ACTION!="remove", SUBSYSTEM=="tty", ATTRS{idVendor}=="37d1", TAG+="uaccess"

Install it with sudo cp 70-openflow.rules /etc/udev/rules.d/ && sudo udevadm control --reload-rules && sudo udevadm trigger, then replug the halves. Do not run tools as root when a udev rule will do.

Browsers and mobile hosts

  • Web Serial (browser configurators) exists in Chromium-based desktop browsers only, over HTTPS or localhost INFERRED (web platform documentation; re-check before relying on it). See JavaScript recipes.
  • Phones and tablets can only be Bluetooth hosts (typing); configuration needs USB. The vendor lists Windows, macOS, Debian Linux, iPadOS, iOS and Android as tested hosts for BLE v2; each host slot advertises as "NayaCreate BLE n" from BLE v2 on, and some hosts need Bluetooth restarted to see the new name DOC (1.19.1 notes7) REPORTED (createflow-dongle: no configuration channel over Bluetooth10).

Open questions

  • OPEN OpenFlow and the udev rule with a keyboard on a Linux machine; our tools on macOS (details).
  • OPEN Whether 3.35.4 shows one or two CDC interfaces per half (not recorded).
  • OPEN NayaFlow on Linux after 1.17.3 (details).

Sources


  1. nayactl, github.com/Qonfused/nayactl (README, nayactl.cmd, discovery.py); comment on pull request #2 about the cp1252 crash. ↩↩↩

  2. nayactl, pull request #2 (Windows serial settings dsrdtr=False and write_timeout). ↩

  3. create-legacy-firmware, FLASHING-PROCEDURE.md, "Measured on hardware, both halves (2026-09-20)". ↩

  4. Naya Create manual v1.1.x (see Manuals for archived copies). ↩

  5. NayaFlow 1.25.1, flow/flow-bg-server.exe: strings (the update dialog's cabling rules). ↩

  6. NayaFlow 1.25.1 for macOS, NayaFlow.app/Contents/core/NayaCore.app/Contents/MacOS/NayaCore: code signature (CodeDirectory flags, entitlements). ↩

  7. Vendor release notes, NayaTech/NayaFlow-releases, mirrored in create-legacy-firmware CHANGELOG.md. ↩↩↩↩

  8. Vendor release notes of the beta channel, NayaTech/NayaFlow-beta-releases. ↩

  9. Create Companion, github.com/create-collective/create-companion (README). ↩

  10. createflow-dongle, docs/findings.md. ↩

  11. createflow-dongle, release 0.1.3 notes. ↩