Watch device
WS /v1/devices/{id}/watch — upgrade, validate, then relay. The credential is offered as the
omega.token.<ops_…> subprotocol, NEVER as a query parameter (see [token_from_subprotocols]).
Validation happens BEFORE the upgrade so a rejected client gets a real HTTP status rather than a
WebSocket that immediately closes. Note the deliberate asymmetry with the rest of /v1: this
route authenticates with the SESSION token, not an API key. The session row is the tenancy claim —
its org and device are read from the row, never from the request.
Headers
Two subprotocols: omega.operator.v1, omega.token.<ops_…>. The credential travels here, NOT in the query string, so it is never recorded by request logging or error reporting.
Path Parameters
Device id (dev_<uuid>)
Response
WebSocket upgrade (echoes omega.operator.v1). Server→client: {"t":"frame","data":<base64 jpeg>,"w":…,"h":…}. Client→server: {"t":"mouse"|"scroll"|"key"|"char", …} — relayed ONLY for a control session that currently HOLDS the device's takeover lease.