275 lines
13 KiB
Markdown
275 lines
13 KiB
Markdown
# 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:
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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.
|