Authentication and API keys

The HTTP API, the Runtime Lifecycle API, and the WebSocket API all use the same API key mechanism. API keys (also called ApiKeys) live in apikeys.json; each key carries a role that governs what it can do — across all three surfaces.

How it works

API keys are provided via the apikey HTTP header or the ?apikey= query parameter. The header is checked first; if empty, the query parameter is used as fallback.

# Header
curl -H "apikey: my-secret-key" http://localhost:8080/api/getstatistics

# Query parameter
curl http://localhost:8080/api/getstatistics?apikey=my-secret-key

Keys are validated against apikeys.json, located in the application directory (next to the executable).

apikeys.json format

apikeys.json accepts two forms, and the two can be mixed in one file.

Legacy form — each key is a bare string and gets full (admin) access. This is unchanged and still works exactly as before:

{
  "Keys": [
    "my-secret-key",
    "another-key"
  ]
}

Scoped form — each key is an object that declares a role (and an optional descriptive label):

{
  "Keys": [
    { "key": "abc123", "role": "operator", "label": "studio-panel" },
    { "key": "def456", "role": "viewer" },
    { "key": "ghi789", "role": "admin" }
  ]
}

Role resolution rules:

  • A bare string, or an object with no role, ⇒ admin (legacy full access).
  • An object with an unknown / garbage role string fails closed to viewer — it is never silently promoted to admin.
  • label is optional and purely descriptive (handy for auditing which key is which).

A backup (apikeys.bak) is created automatically on startup. If apikeys.json is missing but apikeys.bak exists, the backup is restored. If neither file exists, an empty apikeys.json is created.

Roles & permissions

Each key maps to one of four roles — viewer, operator, editor, admin — that form a fixed ladder, where each higher role is a superset of the ones below it.

Role Grants
viewer read
operator read, control
editor read, control, config, builder
admin read, control, config, builder, admin (everything)

What each permission covers:

  • read — monitoring / inspection: statistics, get property, list objects, images / snapshots, status, capabilities, events / SSE, WebSocket monitoring subscriptions.
  • control — operating the running engine: start, stop, exit, setproperty, invokecommand, switcher/*, layer/setsource, scene/disablerender, source reload / clear, media play, target executecommand, alarms/clear, and the WebSocket SetPropertyValueByObjectId / ExecuteCommandByObjectId messages.
  • config — changing persisted project / settings, which can carry secrets: project save / load / upload, project setproperty, project getxml, settings get / getjson / set, scriptengine execute, connector trigger. Deliberately strict: settings reads and project-XML dumps require config (not read) because they can expose secrets such as stream keys.
  • builder — entity CRUD: input / scene / layer / target / operator / connector create / delete / reorder / list, types/*, batch. These are also gated by the existing EnableBuilderApi setting — the gate and this permission are checked independently, so enabling the Builder API does not grant a lesser key access.
  • admin — debug/, tests/, autotest set*, WebSocket disconnect / disconnectall, license/validate, discovery.

Each endpoint section below states its required permission (and the minimum role that grants it) with a Requires: note.

Validation behavior

Every request resolves to one of four outcomes, depending on whether the key is valid and whether its role covers the endpoint's required permission.

Scenario Result
No keys defined (empty Keys array), or apikeys.json missing / unreadable Open mode — request proceeds as admin (unchanged legacy behavior)
Valid key whose role grants the endpoint's required permission 200 — request proceeds
Valid key whose role does not grant the required permission 403 Forbidden — the body / JSON states the required permission
Key provided but unknown / invalid 401 Unauthorized — request rejected

So: if you define keys, callers must provide a valid one and hold a role that covers the endpoint. If you don't define any keys, the API is open to all callers as admin. Key comparison is case-sensitive.

WebSocket authentication

The WebSocket API uses the same apikeys.json keys and the same roles. A client presents its key on the connection handshake — either as an ?apikey= query parameter on the WebSocket URL or an apikey header. When keys are defined and the RequireWebSocketAuth setting is enabled (the default), a missing or invalid key is rejected at connect time (the server sends an Error message and closes the socket). Once connected, per-message operations are gated by the connecting key's role: subscriptions require read, while the SetPropertyValueByObjectId and ExecuteCommandByObjectId messages require control. Setting RequireWebSocketAuth to false restores the pre-RBAC behaviour (any client may connect). Open mode (no keys defined) always connects as admin. See the WebSocket API for the full message-level details.