Publish LumaOps source

This commit is contained in:
LumaOps release export
2026-09-03 01:18:36 +02:00
commit 7f1c0e5f71
2363 changed files with 501543 additions and 0 deletions
+256
View File
@@ -0,0 +1,256 @@
# LumaOps architecture
## System shape
LumaOps is a single-container, local-first control plane around the imported
OpenRGB 1.0rc3 hardware engine.
```text
Browser
│ HTTP / SSE on WEB_PORT (bridge default 1223)
▼
FastAPI application ───── SQLite + encrypted connector secrets
│
├── command service ─── audit, idempotency, rate limits, locks
│ │
│ └── connector registry
│ ├── OpenRGB connector ── SDK v5 TCP ── OpenRGB Core
│ ├── WLED connector HTTP/UDP (optional)
│ ├── Home Assistant REST/WS (optional)
│ └── future connectors
│
└── built React application
Container supervisor
├── OpenRGB: 127.0.0.1:6742 only
└── FastAPI: APP_HOST:APP_PORT
```
Only the internal `APP_PORT` is mapped to `WEB_PORT`; the defaults are
`1223:8080`. The OpenRGB SDK has no authentication or encryption and is never a
public container port.
## Repository boundaries
- The imported OpenRGB tree remains at the repository root.
- `lumaops/backend` contains the FastAPI package and Python tests.
- `lumaops/frontend` contains the React/TypeScript application.
- `lumaops/backend/src/lumaops_backend/migrations` contains ordered SQLite migrations.
- `docker` contains entrypoint/supervision, health, and Unraid assets.
- `docs` contains LumaOps operational and design documentation.
- A minimal OpenRGB core patch is isolated and documented separately.
## Runtime lifecycle
1. Tini becomes PID 1 and forwards container signals.
2. The LumaOps supervisor creates persistent directories and validates their
permissions.
3. It starts OpenRGB with an explicit loopback address and persistent config
path, then captures OpenRGB output in `/logs/openrgb.log`.
4. It starts FastAPI regardless of whether hardware detection succeeds.
5. FastAPI runs migrations, creates an application instance ID/keyfile if
needed, and starts the connector registry.
6. The OpenRGB connector negotiates SDK protocol 5 and synchronizes inventory.
7. A device-list event or reconnect schedules another read-only inventory sync.
8. On SIGTERM/SIGINT, FastAPI stops accepting work, connector tasks are
cancelled, OpenRGB receives SIGTERM, and the supervisor waits before a
bounded SIGKILL fallback.
An OpenRGB failure degrades hardware functions but does not take down the web
application. The supervisor restarts OpenRGB with bounded backoff. Repeated
failure opens a circuit and surfaces a recovery action in Diagnostics.
## Backend modules
The application follows ports-and-adapters boundaries rather than exposing SDK
types through the API:
- `connectors.base`: normalized capabilities, inventory, state, health, errors,
and connector contract.
- `connectors.openrgb.protocol`: bounded SDK-v5 parser and serializer.
- `connectors.openrgb.adapter`: connection, handshake, request correlation,
reconnect, profile operations, and change events.
- `connectors.mock`: deterministic test/development implementation that can be
enabled only by an explicit non-production setting.
- `services.inventory`: stable identities and reconciliation.
- `services.commands`: validation, emergency stop, idempotency, locks, rate
limiting, execution records, and audit.
- `services.scenes`: capture/apply/preview and compensating rollback.
- `services.automations`: schedule evaluation, cooldown, conflict keys, and run
history.
- `services.setup`: read-only environment and hardware checks.
- `services.diagnostics`: redacted support bundle generation.
- `api`: versioned resource routers under `/api/v1`, plus SSE.
## Connector contract
Every connector exposes:
- lifecycle: `start`, `stop`, `test`, and `health`;
- discovery and inventory without implicit writes;
- normalized capabilities and current state;
- validated `set_state` operations;
- optional profile/preset operations;
- timeouts, bounded retries, rate limits, and normalized error categories;
- a configuration schema whose secret fields are explicitly marked;
- an inventory-change event callback.
The UI operates only on normalized devices and capability flags. It never
contains an OpenRGB packet, WLED segment, or Home Assistant service-call path.
## Device identity and ownership
An inventory record has an immutable internal UUID. A connector provides a
candidate fingerprint from the most durable available fields, in priority
order: connector ID, hardware serial, stable location/path, MAC/IP plus model,
then a guarded composite of vendor/model/name. The `device_identities` table
persists all observed aliases so a controller moving to another discovery index
keeps its UUID.
`controller_index` is volatile routing data and is updated during each OpenRGB
sync. Before every write, the adapter compares the stored fingerprint with the
current controller at that index. Mismatch cancels the command and triggers a
resync.
One physical light has one management owner: OpenRGB, native connector, Home
Assistant, or unmanaged. Possible duplicates are surfaced for administrator
resolution and are never written through two connectors concurrently.
## Normalized capability model
Capabilities are explicit booleans plus ranges:
- power and restore;
- RGB, colour temperature, and brightness;
- effect/mode, speed, direction, and multiple colours;
- per-zone, per-segment, and per-LED control;
- presets/profiles;
- read-only or experimental state.
Commands are intersected with capabilities. Unsupported properties result in a
422 response before any connector is called. Brightness, speed, indices,
colours, list lengths, and update frequency are range-checked twice: at the API
boundary and in the connector.
## Command path and safety
Every mutation follows this path:
```text
API validation
→ authentication/CSRF policy
→ idempotency lookup
→ emergency-stop/read-only/block policy
→ stable identity resolution
→ capability validation
→ connector + device locks
→ token-bucket rate limit
→ connector timeout/circuit breaker
→ result + audit record + realtime event
```
Commands have UUIDs and record request ID, actor, target, sanitized desired
state, start/end time, duration, result, and normalized error. Secret values and
raw environment data are never command payloads.
The emergency stop cancels queued effects, prevents new hardware writes, and
sends one bounded all-off attempt only when the administrator requests it. It
does not loop on failing hardware.
## Scenes and rollback
LumaOps scenes are application-level, versioned desired-state documents. They
can contain OpenRGB devices, connector devices, groups, and explicit off
entries. OpenRGB profiles remain separate objects.
Scene application first resolves targets and snapshots retrievable prior state.
Commands then execute in deterministic connector/device order. If a required
item fails, already changed devices receive compensating commands where their
prior state is known. The result reports applied, skipped, failed, and rolled
back targets; rollback is best effort and never hides the original failure.
## Automation model
Rules store a trigger document, action list, timezone, enabled state, cooldown,
and conflict key. MVP trigger types are time, weekday, manual, webhook, and
device online/offline. Sunrise/sunset becomes active once coordinates are
configured. Temperature and Home Assistant events use the same trigger
interface when a provider exists.
A single scheduler claims due rules transactionally. The conflict key prevents
overlapping runs for the same scene/group/device. All attempts create an
`automation_runs` record, including skipped cooldown/conflict decisions.
## Data and migrations
SQLite uses foreign keys, WAL mode, a busy timeout, UTC timestamps, and explicit
transactions. Ordered SQL migrations are recorded in `schema_migrations`.
Before a migration marked destructive, the runner creates a timestamped online
backup in `/data/backups` and aborts if that backup fails.
Core tables cover settings, connectors, encrypted secrets, devices, identity
aliases, rooms, groups, membership/tags, scenes/items, automations/runs,
commands/results, audit events, health samples, discovery runs, ignored
devices, idempotency keys, and setup state.
Soft deletion is used for user-managed resources and history-bearing inventory.
High-volume health and activity history has a configurable retention policy.
## API and realtime updates
REST resources live below `/api/v1`. Responses carry `X-Request-ID`; failures
use one stable shape:
```json
{
"error": {
"code": "device_offline",
"message": "Het apparaat is niet bereikbaar.",
"request_id": "...",
"details": {},
"recovery": ["Controleer de connector", "Voer een nieuwe scan uit"]
}
}
```
List endpoints use `limit`, `offset`, filtering, and documented sort fields.
Writes that could be retried accept `Idempotency-Key`. Server-Sent Events at
`/api/v1/events` distribute health, inventory, command, and audit changes.
FastAPI also serves the compiled SPA and its hashed assets. API and health paths
are excluded from SPA fallback.
## Configuration and secrets
Non-secret settings come from environment variables and the settings table;
environment values win for deployment-critical addresses and paths. Connector
secrets are encrypted with Fernet-compatible authenticated encryption using a
persistent key from `LUMAOPS_SECRET_KEY` or `/config/lumaops/secret.key`.
The keyfile is created with mode 0600 when absent. Losing it makes connector
secrets unrecoverable but does not corrupt inventory, scenes, or audit data.
Rotation decrypts all values in one transaction using the old key and reencrypts
them with the new key after a backup.
## Health model
`/health/live` reports only whether the web process can serve requests.
`/health/ready` verifies database access and migrations. `/api/v1/health`
aggregates the supervisor heartbeat, OpenRGB process, SDK protocol/connection,
database, connectors, and persistent-directory writability.
Aggregate states are `healthy`, `degraded`, and `unhealthy`. Missing hardware or
SDK is degraded, not a web-process crash. The container healthcheck treats a
working but degraded application as alive while exposing the degradation in the
detailed API.
## Deployment invariants
- x86_64 Linux and one production image.
- No compiler, source tree, npm cache, or build tool in the runtime stage.
- `/config`, `/data`, and `/logs` are persistent and writable.
- No default `privileged: true`; only explicit device mappings.
- Bridge networking by default; documented host mode for LAN discovery.
- No SaaS dependency and no automatic reverse-proxy or external exposure.
- Mock mode is rejected when `LUMAOPS_ENV=production` and is visibly marked in
every page when explicitly enabled elsewhere.
+30
View File
@@ -0,0 +1,30 @@
# Back-up en herstel
Een volledige LumaOps-back-up bevat drie consistente onderdelen:
- /data met lumaops.db en beheerde databaseback-ups;
- /config/openrgb met OpenRGB-instellingen, profielen en plugins;
- /config/lumaops met secret.key.
Zonder secret.key zijn versleutelde connectorsecrets niet herstelbaar. Bewaar de sleutel samen met de database in een versleutelde, toegangsgelimiteerde back-up. Deel diagnostiekexports niet als back-up; die bevatten bewust geen secrets.
## Back-up
Stop voor een volledig consistente bestandssnapshot kort de container:
docker compose stop lumaops
tar -C /mnt/user/appdata -czf /mnt/user/backups/lumaops-YYYYMMDD-HHMMSS.tgz lumaops
docker compose start lumaops
De UI maakt atomair een SQLite-herstelpunt via de SQLite backup API. Gebruik dat vóór configuratiewijzigingen, maar kopieer periodiek ook beide configmappen.
## Herstel
1. Stop LumaOps.
2. Maak een veiligheidskopie van de huidige appdata.
3. Herstel alle drie de onderdelen met dezelfde eigenaar PUID/PGID.
4. Start de container.
5. Controleer migrations, SDK health, inventaris en connectorsecrets.
Een databaseback-up uit de UI maakt vóór restore automatisch een extra safety-back-up en controleert SQLite-integriteit. Roteer de encryptiesleutel alleen met een expliciete decrypt/re-encryptmigratie; simpelweg secret.key vervangen maakt bestaande ciphertext onleesbaar.
+19
View File
@@ -0,0 +1,19 @@
# Connectoren ontwikkelen
Alle hardware- en netwerkbronnen implementeren de connectorcontracten onder lumaops/backend/src/lumaops_backend/connectors. De rest van backend en frontend werkt alleen met genormaliseerde Device, DeviceState, Capabilities en ConnectorHealth-objecten.
Een connector levert minimaal id, kind, configuratieschema, start/stop, discovery, inventory, state lezen/schrijven, connectietest, health en identify. Fouten worden vertaald naar stabiele LumaOps-foutcategorieën. Secrets worden via SecretStore versleuteld en verschijnen nooit in configschema, logs of responses.
Ontwikkelvolgorde:
1. Voeg modellen en adapter toe zonder merkcode in de UI.
2. Normaliseer stabiele identiteit met bron, vendor/model, serienummer, locatie of netwerkidentiteit; gebruik nooit alleen een lijstindex.
3. Declareer capabilities conservatief en stuur geen niet-ondersteunde velden.
4. Voeg timeouts, begrensde reconnect-backoff, rate limiting en health toe.
5. Schrijf contracttests, parserfixtures en fouttests.
6. Registreer de connector in ConnectorRegistry en toon hem automatisch via /api/v1/connectors.
OpenRGB is de referentie-implementatie. De packetcodec is geïsoleerd in connectors/openrgb/protocol.py en de async I/O in adapter.py. Een nieuwe connector mag die binaire details niet naar services lekken.
WLED en Home Assistant zijn de eerste geplande uitbreidingen. Detecteer dubbele eigenaars voor WLED via DDP/E1.31 en laat owner één van openrgb, native, home_assistant of unmanaged zijn. MQTT, ESPHome, HTTP en remote agents passen in hetzelfde contract.
+24
View File
@@ -0,0 +1,24 @@
# OpenRGB Core-wijzigingen
LumaOps beperkt wijzigingen aan OpenRGB Core tot de onderstaande twee patches.
## Gecontroleerd afsluiten in headless modus
`startup/main_FreeBSD_Linux_MacOS.cpp` installeert handlers voor SIGINT en SIGTERM.
De headless server installeert handlers voor SIGINT en SIGTERM. De handler zet alleen een sig_atomic_t-vlag; de normale applicatiedraad roept daarna NetworkServer::StopServer aan. Daardoor kan Tini/supervisor de SDK-server gecontroleerd stoppen zonder async-signal-unsafe netwerk- of Qt-code in de signal handler.
## Veilige fallback voor HID-interfaceherkenning
`ResourceManager.cpp` en `ResourceManager.h` voegen een losse vergelijking zonder
interface-index toe wanneer een detector geen exacte HID-interface vindt. De fallback
wordt alleen automatisch geregistreerd als precies één detector overeenkomt. Bij nul of
meerdere kandidaten verandert er niets; zo kan een ambigu apparaat nooit stilzwijgend
aan de verkeerde detector worden gekoppeld.
Deze patch ondersteunt hardware waarvan de door Linux gemelde interface-index afwijkt,
waaronder de geteste Aura- en geheugencontrollers. De bestaande exacte match blijft
altijd de eerste keuze.
Bij iedere upstreamupdate moeten beide patches opnieuw worden beoordeeld. Verwijder een
forkpatch zodra upstream gelijkwaardig gedrag implementeert.
+52
View File
@@ -0,0 +1,52 @@
# Hardwaretoegang
OpenRGB praat rechtstreeks met USB, HID, SMBus/I²C en soms seriële controllers. Geef LumaOps alleen de benodigde nodes.
## USB
De Compose-standaard geeft /dev/bus/usb door. Inventariseer op Unraid met:
lsusb
find /dev/bus/usb -maxdepth 2 -type c -ls
OpenRGB gebruikt de ingebouwde 60-openrgb.rules als referentie. In een container zijn expliciete device mappings en hostpermissies bepalend. Koppel stabiele fysieke apparaten bij voorkeur aan bus/VID/PID/serienummer; de LumaOps UUID is niet alleen op OpenRGB controller-index gebaseerd.
## I²C en SMBus
Laad op de host eerst i2c-dev en de chipsetdriver, bijvoorbeeld i2c-i801 voor veel Intel-systemen of i2c-piix4 voor sommige AMD-systemen:
modprobe i2c-dev
modprobe i2c-i801
ls -l /dev/i2c-*
i2cdetect -l
Voeg daarna uitsluitend de benodigde adapter toe:
devices:
- /dev/i2c-0:/dev/i2c-0
Adapterindices kunnen na hardware- of kernelwijzigingen veranderen. Controleer i2cdetect -l na een Unraid-update. Voer geen willekeurige i2cset-opdrachten uit; de OpenRGB-detectielogica is de enige hardwarelaag.
## LumaOps op de Widefrog Unraid-host
De huidige host gebruikt deze beperkte hardwaretoegang:
- `/dev/hidraw0`: ASUSTeK AURA LED Controller `0b05:18f3`;
- `/dev/i2c-0`: Intel I801 SMBus voor Corsair Vengeance RGB DDR4;
- `/dev/bus/usb`: USB-transport voor ondersteunde OpenRGB-apparaten.
Voer als root eenmalig `scripts/unraid-hardware-setup.sh` uit. Het script installeert een minimale udev-regel, geeft alleen deze apparaten toegang voor de Unraid-groep `users` en registreert zichzelf idempotent in `/boot/config/go`. Daarna kan de niet-root LumaOps-container de apparaten gebruiken zonder privileged modus of een volledige `/dev`-mount.
## Serieel
Voor controllers op /dev/ttyUSB0 of /dev/ttyACM0 voegt men de specifieke node als read/write device toe. Controleer dialout/uucp-groepsrechten op de host. Mount nooit standaard de volledige /dev-map.
## Diagnoseprofiel
Alleen wanneer een device in privileged mode wel werkt:
docker compose -f docker-compose.yml -f docker/compose.diagnostic.yml up -d
Stop eerst de normale container. Het diagnoseprofiel is onveilig en tijdelijk. Identificeer de ontbrekende node/permissie, stop het profiel en keer terug naar expliciete devices. Voeg geen brede capabilities toe zonder aantoonbare noodzaak.
De eerste fysieke schrijfactie hoort een statische, gematigde kleur op één niet-experimenteel apparaat te zijn. Gebruik geen snelle effecten tijdens validatie.
+67
View File
@@ -0,0 +1,67 @@
# OpenRGB upstream synchroniseren
De repository heeft `origin` voor deze LumaOps-repo en `upstream` voor de officiële OpenRGB GitLab-repository. Tag `upstream-openrgb-1.0rc3` markeert de originele baseline.
Eisen:
- OpenRGB blijft de core hardware-engine en volgt upstream-architectuur.
- Forkwijzigingen blijven minimaal.
- Afwijkingen t.o.v. upstream worden gelogd in [CORE_PATCHES](docs/CORE_PATCHES.md) en [SOURCE_AUDIT](docs/SOURCE_AUDIT.md).
1. Controleer de upstream-remote.
```bash
git remote -v
git fetch --tags upstream
```
2. Maak een onderhoudsbranch.
```bash
git switch -c maintenance/openrgb-VERSIE
```
3. Breng upstream binnen via een gecontroleerde merge.
```bash
git merge --no-commit upstream/master
```
4. Valideer dat alleen geautoriseerde core-bestanden afwijken.
```bash
OPENRGB_BASELINE_TAG=upstream-openrgb-1.0rc3 ./scripts/openrgb-upstream-guard.sh
```
Of op Windows:
```powershell
$env:OPENRGB_BASELINE_TAG="upstream-openrgb-1.0rc3"
.\scripts\openrgb-upstream-guard.ps1
```
Deze controle draait automatisch in CI via:
- `/.github/workflows/openrgb_upstream_compat_guard.yml` op `pull_request` en `push` voor `main`/`master`.
5. Voer daarna de inhoudelijke review uit op:
- `NetworkProtocol.*`
- `NetworkServer.*`
- `PluginManager.*`
- `RGBController.*`
- `SettingsManager.*`
- `ProfileManager.*`
- `cli.*`
- alle controller- en detectorregistraties.
6. Pas de forkpatch (headless stop-handler) opnieuw toe of verwijder hem als upstream dat gedrag inmiddels bevat.
7. Bepaal compatibiliteitstesten:
- compileer OpenRGB-core,
- draai backend/frontendtests,
- bouw de container,
- voer read-only inventarisatie uit op hardware,
- voer daarna minimaal één non-destructieve kleuractie op fysiek apparaat uit.
8. Als stap 4 mislukt, stop en onderzoek de drift direct.
Gebruik nooit een blinde subtree-overschrijving.
+22
View File
@@ -0,0 +1,22 @@
# Public release boundary
De ontwikkelrepository bevat in oudere commits interne deployment-URL's en mag
daarom niet met volledige historiek openbaar worden gemaakt. Publiceer in plaats
daarvan een gecontroleerde, parentless bronexport van een gereviewde releasecommit:
```sh
bash scripts/export-public-source.sh /path/to/new-lumaops-public
gitleaks git /path/to/new-lumaops-public --config /path/to/new-lumaops-public/.gitleaks.toml --redact
```
De export neemt de actuele getrackte bron op, verwijdert de private automatische
deploymentworkflow, weigert runtime-secrets, bekende interne markers en onverwacht
grote bestanden, en schrijft een SHA-256-manifest. Het resultaat heeft één rootcommit
zonder ouders en tagt die als `public-release-baseline`, zodat de upstream-guard ook
toekomstige publieke bijdragen kan begrenzen. Controleer daarna de manifest-, test-, build-, lint- en secretscan
voordat de nieuwe publieke remote wordt toegevoegd.
Het vaste securitycontact is `security@itworx.tech`. Voor de eerste release
moeten alleen de publieke project- en support-URL nog naar de definitieve naam
van de nieuwe publieke exportrepository wijzen; de private ontwikkelrepository
blijft daarvan gescheiden.
+19
View File
@@ -0,0 +1,19 @@
# Ontwerp voor LumaOps Agent
Het MVP bevat geen agent. Een handmatig remote OpenRGB SDK-endpoint past later in de connectorlaag, maar het kale SDK-protocol hoort nooit over internet: het biedt geen TLS, authenticatie of autorisatie.
De geplande agent draait op Windows of Linux naast een lokale OpenRGB-instance. Hij maakt zelf een uitgaande TLS-verbinding naar de hub, gebruikt een afzonderlijk revocable token, rapporteert genormaliseerde inventory/health en accepteert alleen schema-gevalideerde opdrachten. De hub bewaart geen OS- of OpenRGB-beheerderswachtwoorden.
Vereiste eigenschappen:
- device-bound identiteit en tokenrotatie;
- TLS met certificate validation en optionele pinning;
- command-idempotency, sequence numbers en deadlines;
- lokale allowlist, rate limiter en kill switch;
- offline veilige toestand en begrensde reconnect-backoff;
- audit aan agent- en hubzijde;
- geen inkomende luisterpoort op de remote host;
- versie/capability negotiation voor rolling upgrades.
Datamodel: agent, agent_connection, connector_instance en device_identity blijven afzonderlijk. Apparaten behouden hun interne UUID wanneer een agent reconnect of controllerindices wijzigen. Compromittering van één token mag geen andere agent of centrale beheerrechten ontsluiten.
+97
View File
@@ -0,0 +1,97 @@
# Security model
LumaOps controls hardware. Treat its web access, persistent configuration, and
connector credentials as administrator-level assets.
## Safe defaults
- OpenRGB binds only to `127.0.0.1:6742`; Docker never publishes 6742.
- Authentication is enabled for the default LAN bind and requires a unique
administrator token of at least 32 characters.
- Bridge networking, unprivileged runtime users, and selected device mappings
are preferred over host networking or privileged mode.
- Inventory and discovery do not write to devices.
- Mock mode cannot start in production.
- Unknown, blocked, read-only, or identity-mismatched devices reject writes.
- Mutations are rate-limited, locked per device, time-bounded, and audited.
## OpenRGB SDK boundary
The SDK has no authentication, authorization, or TLS. Anyone who can reach it
can control attached hardware. Never add a compose `ports` entry for 6742 and
never bind it to a LAN address. A manually configured remote SDK endpoint is
accepted only with a persistent warning and is suitable solely for a protected
LAN or tunnel.
## Application access
Authentication is mandatory for a production process that binds to a non-loopback
address. Authenticated mode uses an HttpOnly, Secure (when HTTPS), SameSite cookie
and a separate CSRF token for state-changing requests. The administrator token can
also be supplied as an API bearer token and must be stored outside source control.
`TRUSTED_PROXIES` is empty by default. Forwarded headers are honored only from
explicit proxy addresses. Setting `EXTERNAL_ACCESS=true` without authentication
is a startup error.
TLS terminates at an administrator-managed reverse proxy. LumaOps does not
provision DNS, certificates, port forwarding, or Nginx Proxy Manager.
## Secrets at rest
Connector secrets are authenticated-encrypted with the persistent LumaOps key.
Use one of:
1. `LUMAOPS_SECRET_KEY` supplied through a protected container secret; or
2. `/config/lumaops/secret.key`, generated on first start with restrictive
permissions.
Back up the key with appdata and store a second protected offline copy. If it is
lost, encrypted connector credentials cannot be recovered and must be entered
again. Database contents, device metadata, scenes, and audit records remain
usable.
OpenRGB may place Hue or Espurna credentials directly in
`/config/openrgb/OpenRGB.json`. Protect and back up that directory as sensitive
data too.
Key rotation creates a database backup, decrypts and reencrypts every connector
secret inside one transaction, verifies all ciphertext, atomically replaces the
keyfile, and then removes the old key only when the administrator confirms the
backup.
## Logging and diagnostics
Structured logging redacts known secret field names, authorization/cookie
headers, query tokens, encryption keys, and connector credentials. Do not enable
raw HTTP or SDK packet logging in production. Request, connector, device, and
command identifiers are logged instead.
The diagnostic bundle uses an allowlist. It includes versions, health,
redacted schema-level configuration, device metadata, device-node visibility,
and recent normalized errors. It excludes environment dumps, database files,
cookies, access tokens, secret ciphertext, raw OpenRGB settings, serial numbers
unless explicitly selected, and arbitrary log tails.
## Hardware risk controls
- Validate controller identity immediately before writes.
- Clamp neither invalid input nor unknown enum values silently; reject them.
- Respect advertised mode and zone capabilities.
- Limit realtime updates and stop effects when the backend connection ends.
- Use a static low-brightness colour for first physical validation.
- Do not unload host drivers, change ACPI policy, flash firmware, or probe SMBus
addresses destructively.
- Treat user plugins as trusted native code with the same privileges as OpenRGB.
The temporary privileged compose profile exists only to diagnose permissions.
After identifying the required `/dev/bus/usb`, hidraw, I2C, or serial nodes,
return to explicit mappings and remove privileged access.
## Vulnerability reporting and updates
Never publish live deployment values, environment files or diagnostic exports.
Review OpenRGB upstream changes, Python/npm lockfiles, and base-image security updates
regularly. Build an updated image from source, run the complete test/smoke suite,
back up appdata, and then replace the running container. Never update OpenRGB in
place inside a running container.
+274
View File
@@ -0,0 +1,274 @@
# 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.
+28
View File
@@ -0,0 +1,28 @@
# Testen
Backend:
cd lumaops/backend
python -m ruff format --check src tests
python -m ruff check src tests
python -m mypy src
python -m pytest
Frontend:
cd lumaops/frontend
npm ci
npm run lint
npm run test
npm run build
npm audit
Productie:
docker compose build --pull
docker compose up -d
docker compose ps
docker exec lumaops /opt/venv/bin/python /opt/lumaops/healthcheck.py
Gebruik CONNECTOR_MODE=mock uitsluitend met LUMAOPS_ENV=test of development. De Settings-validatie weigert mockmodus in productie en de UI toont een permanente waarschuwing. Fysieke tests beginnen met inventory en daarna één statische kleur op lage frequentie.
+33
View File
@@ -0,0 +1,33 @@
# Probleemoplossing
## Webapp niet bereikbaar
Controleer `docker compose ps`, de externe poort 1223/`WEB_PORT`, de interne `APP_PORT` (standaard 8080) en `/logs/backend.log`. De rootfilesystem is read-only; `/config`, `/data`, `/logs`, `/tmp` en `/run` moeten schrijfbaar zijn. Controleer PUID/PGID en appdata-eigenaarschap.
## OpenRGB of SDK offline
Bekijk /logs/openrgb.log en /logs/supervisor.log. De supervisor herstart OpenRGB met begrensde backoff; de webapp blijft beschikbaar en health wordt degraded. Controleer dat alleen 127.0.0.1:6742 gebruikt wordt:
docker exec lumaops python -c "import socket; print(socket.create_connection(('127.0.0.1',6742),2))"
Publiceer 6742 niet om het probleem te omzeilen. Controleer versie 1.0rc3 en SDK-protocol 5.
## Geen USB- of I²C-apparaten
Doorloop de setupwizard opnieuw. Vergelijk host- en containerinventaris. Voeg /dev/bus/usb of alleen de benodigde /dev/i2c-* toe. Laad i2c-dev plus chipsetdriver op de host. Gebruik het privileged diagnoseprofiel alleen kort om een permissieverschil te bevestigen.
## Network lights niet gevonden
Bridge networking laat niet elk broadcast/multicast/mDNS-protocol door. Test de host-network override. Houd OPENRGB_HOST op 127.0.0.1. Controleer VLAN/firewall en of discovery is ingeschakeld.
## Login werkt niet via HTTP
Secure cookies vereisen HTTPS. Gebruik een reverse proxy met TLS. Alleen voor tijdelijke LAN-diagnose kan SECURE_COOKIES=false. EXTERNAL_ACCESS=true zonder AUTH_ENABLED wordt bij startup geweigerd.
## Database of secrets
SQLite-integriteitsfouten: stop de container en herstel een gecontroleerde back-up. Een ontbrekende secret.key kan niet cryptografisch worden gereconstrueerd; herstel dezelfde sleutel of configureer connectorsecrets opnieuw.
## Diagnosepakket
Download /api/v1/diagnostics/export via de UI. Het pakket bevat versies, schema zonder geheimen, hardwaretoegang, SDK-status, inventory, health en recente fouten. Request-id uit een foutmelding koppelt frontend, API en logs.
+59
View File
@@ -0,0 +1,59 @@
# LumaOps op Unraid
## Voorbereiding
LumaOps bouwt één image met OpenRGB Core, de FastAPI-backend en de statische React-app. De veilige standaard is bridge networking, een read-only rootfilesystem en alleen expliciete hardwaredevices. Alle Linux-capabilities worden verwijderd, behalve CHOWN, SETUID en SETGID die de root-entrypoint kort nodig heeft om volumes en PUID/PGID in te stellen; na de overstap draait de applicatie als Unraid nobody/users via PUID=99 en PGID=100.
Maak appdata aan:
mkdir -p /mnt/user/appdata/lumaops/{openrgb,config,data,logs}
Kloon de repository, kopieer .env.example naar .env en bouw:
git clone REPOSITORY-URL
cd LumaOps
cp .env.example .env
# Genereer een unieke token met: openssl rand -base64 32
# en vervang LUMAOPS_ADMIN_TOKEN in .env.
docker compose build
docker compose up -d
docker compose ps
Pas in docker-compose.yml de hostpaden aan naar /mnt/user/appdata/lumaops wanneer Compose vanuit een andere map draait. Voor de Unraid GUI kan docker/unraid-lumaops.xml als templatebasis worden gebruikt. Bij een lokale build blijft Repository lumaops:0.1.0.
## Netwerkmodi
Bridge is standaard en publiceert alleen de webinterface: extern `WEB_PORT=1223` naar intern `APP_PORT=8080`. Open dus `http://UNRAID-IP:1223`. Poort 6742 ontbreekt doelbewust bij `ports`.
Voor OpenRGB-netwerkcontrollers die UDP broadcast, multicast of mDNS nodig hebben:
docker compose -f docker-compose.yml -f docker/compose.host-network.yml up -d
De override verwijdert de webpoortmapping omdat host networking die niet gebruikt. LumaOps luistert dan rechtstreeks op `WEB_PORT` (standaard 1223). `OPENRGB_HOST` blijft 127.0.0.1, ook in host mode.
## Bijwerken
git fetch origin
git pull --ff-only origin main
docker compose build --pull
docker compose up -d --remove-orphans
docker image prune
Maak vooraf een appdata-back-up. Databaseback-ups in de UI vervangen geen kopie van de volledige appdata. Een rollback gebruikt een vorige Git-commit/tag en daarna dezelfde build/up-opdrachten.
## Productiecontrole
docker compose ps
docker inspect --format "{{json .State.Health}}" lumaops
docker exec lumaops /opt/venv/bin/python /opt/lumaops/healthcheck.py
docker exec lumaops sh -c "ss -ltn 2>/dev/null || true"
Verwacht: web bereikbaar op hostpoort 1223, SDK alleen op 127.0.0.1:6742 en health healthy. Een ontbrekend hardwaredevice mag de webapp niet neerhalen; readiness wordt degraded/unhealthy met hersteladvies.
## Authenticatie
Login staat standaard aan en vereist een unieke `LUMAOPS_ADMIN_TOKEN` van minstens
32 tekens. Voor rechtstreekse HTTP-toegang op een vertrouwd LAN blijft
`SECURE_COOKIES=false`; zet dit op `true` achter HTTPS. Zet bij internettoegang ook
`EXTERNAL_ACCESS=true` en voeg alleen bekende proxyadressen toe aan
`TRUSTED_PROXIES`. Publiceer LumaOps nooit rechtstreeks zonder TLS.
+63
View File
@@ -0,0 +1,63 @@
# Verificatierapport 0.1.0
Dit rapport beschrijft de lokale releasecontrole van LumaOps op 15 juli 2026. De
geteste OCI-image is `lumaops:0.1.0` voor x86_64 Linux.
## Resultaten
- De volledige OpenRGB 1.0rc3 Core compileert in de multi-stage Docker-build.
- Een schone productiecontainer migreert SQLite naar `0001_initial` en start
gezond met een echte OpenRGB SDK-protocol-5-verbinding.
- De webinterface is op de host uitsluitend gepubliceerd als `1223:8080`.
- De SDK-listener is uitsluitend `127.0.0.1:6742`; poort 6742 is niet door
Docker gepubliceerd. Een rauwe SDK-v5-handshake is succesvol uitgevoerd.
- Het rootfilesystem is read-only. Alleen `/config`, `/data`, `/logs`, `/tmp`
en `/run` zijn schrijfbaar gemaakt.
- OpenRGB en de backend draaien na de entrypoint als UID 1000 in de rooktest.
De runtime-image bevat geen gcc, g++, make, qmake of cmake.
- De ingebouwde container-healthcheck rapporteert `healthy`.
- Een opgeslagen instelling blijft na een containerrestart aanwezig.
- Na een geforceerde beëindiging van OpenRGB bleef `/health/live` HTTP 200
geven; de supervisor startte OpenRGB opnieuw en de connector herstelde.
- Een normale containerstop eindigde met exitcode 0 en stopte backend en
OpenRGB gecontroleerd.
- Een schone testcontainer met de expliciete mockadapter vond twee apparaten.
Set-state, inventarisverversing, scène capture/apply/duplicate/export en het
auditlog zijn via de versie-API uitgevoerd.
## Geautomatiseerde kwaliteitscontroles
Backend:
ruff format --check . # 34 bestanden correct geformatteerd
ruff check . # geslaagd
mypy src # 31 bronbestanden, geen fouten
pytest # 14 geslaagd
Frontend:
npm run lint # geslaagd, nul waarschuwingen
npm test # 3 geslaagd
npm run build # TypeScript en Vite geslaagd
npm audit # 0 kwetsbaarheden
De productiefrontend gaf HTTP 200. De componenttests dekken onder meer de
genormaliseerde inventaris, filtering en de snelle kleuractie via de echte
versie-API-contracten.
## Compose- en templatecontrole
`docker compose config` is uitgevoerd voor de standaard-, host-network- en
diagnostische varianten. Standaard is alleen hostpoort 1223 gepubliceerd. In
host mode luistert de webapp rechtstreeks op 1223. De diagnostische override is
bewust privileged en is niet de productieconfiguratie. Het Unraid XML-template
is als XML geparseerd en verwijst eveneens naar poort 1223.
## Grenzen van deze controle
Er zijn geen fysieke RGB-apparaten of Unraid-hostdevice-nodes aan deze
ontwikkelmachine gekoppeld. De echte Core en SDK zijn met nul controllers
getest; hardwarewrites zijn uitsluitend tegen de expliciete mockadapter gedaan.
Volg daarom vóór productiegebruik de niet-destructieve inventariscontrole uit
`HARDWARE_ACCESS.md`, en test daarna één ondersteund apparaat met een
statische kleur en lage commandofrequentie.