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=Falseand 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 RunAsbreaks 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.cmdlauncher tries.venv\Scripts\python.exe, thenpy -3, thenpython, each with-B -m nayactl, and works only once the package is installed in that interpreter;nayactl -v statuscrashed on a cp1252 console (an arrow character); always pass--sideSTATIC 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¶
-
nayactl, github.com/Qonfused/nayactl (README,
nayactl.cmd,discovery.py); comment on pull request #2 about the cp1252 crash. ↩↩↩ -
nayactl, pull request #2 (Windows serial settings
dsrdtr=Falseandwrite_timeout). ↩ -
create-legacy-firmware,
FLASHING-PROCEDURE.md, "Measured on hardware, both halves (2026-09-20)". ↩ -
Naya Create manual v1.1.x (see Manuals for archived copies). ↩
-
NayaFlow 1.25.1,
flow/flow-bg-server.exe: strings (the update dialog's cabling rules). ↩ -
NayaFlow 1.25.1 for macOS,
NayaFlow.app/Contents/core/NayaCore.app/Contents/MacOS/NayaCore: code signature (CodeDirectory flags, entitlements). ↩ -
Vendor release notes, NayaTech/NayaFlow-releases, mirrored in create-legacy-firmware
CHANGELOG.md. ↩↩↩↩ -
Vendor release notes of the beta channel, NayaTech/NayaFlow-beta-releases. ↩
-
Create Companion, github.com/create-collective/create-companion (README). ↩
-
createflow-dongle,
docs/findings.md. ↩ -
createflow-dongle, release 0.1.3 notes. ↩