Files
LumaOps-Public/docs/SOURCE_AUDIT.md
T

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.