Files
LumaOps-Public/docs/SOURCE_AUDIT.md
T

13 KiB

LumaOps source audit

Status: completed on 2026-07-14 against the imported OpenRGB 1.0rc3 source tree.

Scope and method

The supplied archive was imported unchanged as commit a15d499 and tagged upstream-openrgb-1.0rc3 before LumaOps work started. The audit covered the build definition, command-line startup, SDK client/server implementation, protocol structures, settings and profile persistence, plugin loading, Linux hardware access, and the network-lighting controllers named in the project brief.

The imported tree contains 2,237 tracked files, including 1,045 C++ sources, 943 headers, 37 Qt UI definitions, 189 controller directories, and 201 detector implementations. OpenRGB vendors or embeds several dependencies, including hidapi, libusb, mbedTLS, httplib, hueplusplus, libe131, mdns, and nlohmann JSON.

Files reviewed in detail include:

  • README.md, CONTRIBUTING.md, LICENSE, and OpenRGB.pro
  • all required documents under Documentation/
  • NetworkServer.*, NetworkClient.*, and NetworkProtocol.*
  • PluginManager.*, SettingsManager.*, and ProfileManager.*
  • cli.*, ResourceManager.*, and platform startup sources
  • the controller and detector sources for the network protocols listed below

Confirmed versions

Component Version Source of truth
OpenRGB 1.0rc3 OpenRGB.pro sets SUFFIX = 1.0rc3
SDK protocol 5 NetworkProtocol.h sets OPENRGB_SDK_PROTOCOL_VERSION 5
Plugin API 4 OpenRGBPluginInterface.h sets OPENRGB_PLUGIN_API_VERSION 4

The LumaOps adapter must negotiate protocol 5. A peer that returns a different version is reported as incompatible instead of silently interpreting a newer or older packet layout.

Licensing and contribution constraints

OpenRGB is distributed under GPL-2.0. The source files commonly use the GPL-2.0-or-later SPDX identifier. All upstream license files, copyright notices, and SPDX headers remain intact. LumaOps is part of the combined work and is therefore also published under GPL-2.0-or-later in this repository.

CONTRIBUTING.md makes the human contributor responsible for submitted code and disallows AI authorship or co-authorship metadata. Commits use the existing repository identity and do not add AI attribution trailers.

Anyone distributing a LumaOps image must make the corresponding source, including the exact OpenRGB modifications and build instructions, available under the GPL. Merely running the software privately does not trigger source distribution obligations.

Build and headless operation

Upstream Linux build

Documentation/Compiling.md describes a qmake/Qt 5 build. The significant Debian/Ubuntu build dependencies are Qt 5 development tools, a C++ toolchain, pkg-config, libusb, hidapi, and mbedTLS. The upstream sequence is equivalent to:

mkdir build
cd build
qmake ../OpenRGB.pro
make -j"$(nproc)"

OpenRGB is still linked with Qt libraries in a headless build, but it creates a QApplication only when the GUI flag is selected. It therefore runs without an X11/Wayland display when started in server mode.

Verified headless command

The CLI parser and platform startup code confirm this production command:

openrgb \
  --server \
  --server-host 127.0.0.1 \
  --server-port 6742 \
  --config /config/openrgb \
  --noautoconnect

The directory passed to --config must exist before startup. --server-host must always be explicit because OpenRGB otherwise defaults to 0.0.0.0. --nodetect must not be supplied: it would defeat the required local hardware inventory. Starting with no server/CLI action would select the graphical UI.

The Linux startup path initializes ResourceManager, starts the internal server, waits for device detection, and then remains in WaitWhileServerOnline(). There is one upstream gap relevant to containers: SIGINT/SIGTERM handlers are registered only in the GUI startup branch. LumaOps therefore carries a small isolated patch that installs the same shutdown handler for headless server mode. This lets the supervisor stop OpenRGB cleanly instead of relying on the operating system's abrupt default termination.

The production container must verify at runtime that port 6742 is listening on loopback and must never publish or expose it through Docker.

SDK protocol audit

The SDK is an unauthenticated binary TCP protocol. It is appropriate only as an internal process boundary in the same container; it is not an internet or LAN security boundary.

Packets start with a 16-byte header:

  1. magic bytes ORGB;
  2. 32-bit device index;
  3. 32-bit packet ID;
  4. 32-bit payload length.

The implementation serializes the protocol's integers and packed structures in the native little-endian layout used by the x86_64 target. Notable request IDs are controller count (0), controller data (1), protocol negotiation (40), client name (50), device-list-changed (100), rescan (140), profile list and operations (150-153), and plugin operations (200-201). Update requests cover mode, device/zone/LED colours, custom mode, resizing zones, and segment changes.

Protocol 5 adds zone flags, controller flags, effects-only zones, alternative LED names, and segment-clear/add operations. The controller-data response is a nested variable-length structure containing strings, modes, zones, segments, LEDs, and colours. This makes strict bounds checking mandatory.

The internal adapter will enforce:

  • exact-length reads and the ORGB magic value;
  • protocol-5 negotiation before normal traffic;
  • a configurable hard maximum packet size (default 16 MiB);
  • bounded collection counts and bounded, NUL-terminated strings;
  • controller, zone, LED, mode, speed, brightness, and colour validation;
  • controller identity checks instead of trusting a stale device index;
  • one serialized write queue plus per-device locks;
  • connection and command timeouts, bounded reconnect backoff, and cancellation;
  • no write side effects during inventory;
  • safe handling of device-list change notifications and OpenRGB restarts.

The server source also checks controller indexes and validates declared payload sizes for update packets. LumaOps performs the same checks before transmitting, so malformed application input never reaches OpenRGB.

Existing Python client evaluation

The current openrgb-python package was inspected at upstream commit dbbe58336268e273e2604f6f10b9e2f2003d6d84. It still declares protocol version 4, does not implement the protocol-5 surface required by this release, does not provide the required rescan request, and contains receive paths that use a single unbounded socket read for a declared packet length. Its repository also does not provide the parser/serialization test coverage required here.

Decision: LumaOps uses a small first-party SDK protocol-5 adapter behind a connector interface. No other application layer imports packet structures. This keeps the binary protocol auditable and lets tests replay captured and synthetic packets without replacing the real production adapter.

Settings, profiles, and plugins

On Linux, OpenRGB normally stores data under $XDG_CONFIG_HOME/OpenRGB or $HOME/.config/OpenRGB. With the verified command above, all OpenRGB-owned state is instead rooted at /config/openrgb:

Data Location
Main settings /config/openrgb/OpenRGB.json
Normal profiles /config/openrgb/*.orp
Controller size profile /config/openrgb/sizes.ors
User plugins /config/openrgb/plugins/
System plugins build-time platform directory, normally /usr/lib/openrgb/plugins

Profiles match controller identity fields rather than relying only on discovery order. Profile deletion removes the corresponding profile file. Settings are written directly as JSON and OpenRGB itself does not make an atomic backup, so LumaOps backs up the OpenRGB directory together with its own data before restores, migrations, or destructive maintenance.

Plugin enablement is stored in the Plugins section of OpenRGB.json. A plugin must match Plugin API 4 exactly. User-supplied plugins execute native code in the OpenRGB process and are therefore treated as trusted administrator extensions, not as sandboxed add-ons.

Some network-controller configuration is also stored in OpenRGB.json. It can include credentials such as a Philips Hue username/client key or Espurna API key. /config/openrgb must therefore receive the same restrictive filesystem permissions, backup handling, and diagnostic redaction as LumaOps secrets.

LumaOps state is deliberately separate under /config/lumaops and /data so OpenRGB upstream files and application migrations have independent lifecycles.

Existing OpenRGB network support

The audit distinguishes automatic discovery from controllers that are created from manually saved settings even when their source class is named a detector.

Family Existing OpenRGB path Transport and discovery notes
DDP DDP network controller Manual target stored in settings; UDP, default port 4048
E1.31/sACN E1.31 controller Manual unicast/multicast configuration; standard E1.31 UDP transport
Philips Hue Hue controller via hueplusplus Bridge discovery plus manual bridge data; REST/entertainment setup; username and client key are persistent secrets
Philips WiZ WiZ controller UDP port 38899
Nanoleaf Nanoleaf controller HTTP API for setup/state and external-control UDP streaming; external port may be negotiated, with 60222 used by supported paths
LIFX LIFX controller LAN protocol over UDP port 56700
Govee Govee controller LAN discovery multicast 239.255.255.250:4001, local discovery port 4002, control UDP port 4003
TP-Link Kasa Kasa controller Local TCP protocol on port 9999
Yeelight Yeelight controller LAN discovery/manual target; TCP port 55443
Espurna Espurna controller Manual IP/port/API key; HTTP-style API over TCP
Elgato lighting Key Light and Light Strip controllers Local HTTP/TCP API on port 9123

Host networking may be needed for broadcast, multicast, or mDNS discovery on an Unraid host. Bridge networking remains the default. A host-network deployment still binds the SDK to 127.0.0.1; only the LumaOps web port is intended for LAN access.

Native WLED and Home Assistant connectors remain outside OpenRGB and use the same LumaOps capability model. Duplicate ownership is prevented by assigning one management owner per physical device.

Hardware access on Linux and Unraid

USB

OpenRGB needs access to hidraw/libusb devices. A normal Linux installation can install generated udev rules at /usr/lib/udev/rules.d/60-openrgb.rules. In a container, the host kernel still owns udev and permissions. The supported deployment passes /dev/bus/usb explicitly and, where needed, selected hidraw devices. Running the whole container as privileged or mounting all of /dev is not the production default.

SMBus/I2C

The host loads i2c-dev plus its chipset driver, commonly i2c-i801 on Intel or i2c-piix4 on AMD. Only discovered /dev/i2c-* nodes that are actually needed are passed through. Some boards require vendor-specific modules; some kernel/ACPI workarounds can be unsafe and are documented as diagnostics rather than enabled automatically. SPD-related controllers can conflict with kernel memory sensor drivers, so LumaOps does not unload modules or write to SMBus as part of inventory.

Serial devices

Optional serial controllers use individually mapped /dev/ttyUSB* or /dev/ttyACM* devices. Their group/permission requirements remain host-specific.

The first-run inventory is read-only. Actual validation starts with one selected device, a static low-intensity colour, and a low command frequency.

Architectural conclusions

  1. Preserve the upstream source layout and keep LumaOps under lumaops/, docker/, docs/, and tests/.
  2. Carry only the documented headless signal patch and unambiguous HID-interface fallback in OpenRGB Core; keep all other policy, persistence, connectors, and UI work outside the core.
  3. Build OpenRGB from the imported source in the production multi-stage image.
  4. Run OpenRGB and FastAPI under a real init/supervisor; serve the built React application from FastAPI so only one web port is public.
  5. Use an internal protocol-5 adapter for production and a clearly marked mock adapter only for tests and intentional development mode.
  6. Store stable LumaOps UUIDs from a fingerprint of durable identity fields plus persisted identity aliases; never expose discovery order as identity.
  7. Keep OpenRGB at 127.0.0.1:6742, including under host networking, and reject non-loopback production configuration unless an administrator explicitly opts into the documented remote-endpoint risk.
  8. Treat both /config/openrgb and /config/lumaops as sensitive persistent state and redact them from diagnostics.
  9. Model connector capabilities generically so WLED, Home Assistant, MQTT, remote agents, and future protocols do not require brand-specific UI paths.

This audit clears the project to proceed with the LumaOps architecture and MVP implementation while retaining OpenRGB 1.0rc3 as the actual hardware engine.