Camera permission and review fixes (openpanel C3, C4)
C4: a device's `viewers` (superusers by default, or members) covers the image and video messages its ports publish. Anyone else gets none of their values on the socket, in the catalogue or a flow's state, 403 for their history, for the frames they hold (the last 8 included) and for TURN credentials, no bytes when asking for them, and `bye: forbidden` for a live view. A device credential stays bounded by its dashboards. The device page sets it in its Camera section, and a tile the viewer may not see says "No access to this camera" and asks the camera for nothing. C3: - A volatile upload is held to MAX_FRAME_BYTES (4 MiB, 413 above it, read without extra copies), and so is a frame on a device's socket. - A frame the engine holds in memory is written to the ring directory before a local worker is handed it, so load_artifact finds it. - A socket the bus dropped events for catches up with a snapshot, demand included, and a device's credential is checked again per frame, cached by the flow store revision and its nonce, so a lost event never keeps an unpaired device's frames coming. - A report is at most 64 KB once its named config_schemas are put in place. - A device away for 60 s has its true bool messages set to false. - Refused frames are logged by fixed keys, a frame is named after its message, `fluksio serve` waits 5 s for open connections, a tile with a stream URL creates no demand, the 8-name cap keeps the newest names. - Every container's log rotates (json-file, 3 x 10 MB).
This commit is contained in:
+28
-15
@@ -174,7 +174,10 @@ curl "$FLUKSIO/artifacts/<digest>" \
|
||||
The upload returns a reference with the content digest. Identical bytes are
|
||||
stored once. Node code uses `save_artifact` and `load_artifact` instead.
|
||||
`?volatile=true` holds the bytes in the engine's memory for a live frame, where
|
||||
newer frames push them out, instead of storing them.
|
||||
newer frames push them out, instead of storing them. Such a frame is at most
|
||||
`MAX_FRAME_BYTES` (4 MB), and a larger one is refused with 413. A camera only
|
||||
superusers may see answers 403 to anyone else for the frames its messages
|
||||
hold, the last 8 included (see [Who may watch a camera](../interface/dashboards.md#who-may-watch-a-camera)).
|
||||
|
||||
A superuser can remove stored bytes with `DELETE /artifacts/<digest>`. While
|
||||
a run, a current message or a flow input's initial value still references the
|
||||
@@ -200,8 +203,8 @@ on its own, so this is for reclaiming space sooner.
|
||||
| `GET` | `/devices/pair/{code}/device` | what is waiting on a code, before it is approved |
|
||||
| `POST` | `/devices/{id}/pair` | adopt by `{code}`, or by `{request}` from the pending list for a request from your network |
|
||||
| `POST` | `/devices/{id}/unpair` | revoke the device credential, one the portal issued included |
|
||||
| `PUT` | `/devices/{id}/report` | what the device has, from its own credential only; at most 16 KB, replaces the last report, read leniently (see [What a device reports](../interface/dashboards.md#what-a-device-reports)) |
|
||||
| `GET` | `/devices/{id}/ice` | STUN and TURN servers for a live view of the device, as `{ice_servers: [{urls, username?, credential?}], ttl}`, with TURN credentials of the caller's own; not for a device credential |
|
||||
| `PUT` | `/devices/{id}/report` | what the device has, from its own credential only; at most 16 KB, and 64 KB once its named `config_schemas` are put in place (413 above either), replaces the last report, read leniently (see [What a device reports](../interface/dashboards.md#what-a-device-reports)) |
|
||||
| `GET` | `/devices/{id}/ice` | STUN and TURN servers for a live view of the device, as `{ice_servers: [{urls, username?, credential?}], ttl}`, with TURN credentials of the caller's own; 403 for whoever may not watch its camera, and for a device credential |
|
||||
|
||||
`/panels/pair`, `/panels/pair/{code}` and `GET /panels/{id}` answer as their
|
||||
`/devices/` counterparts, for firmware that has not updated yet.
|
||||
@@ -254,7 +257,7 @@ The socket sends a snapshot followed by node, value, health, and run events.
|
||||
|
||||
A client asks for the bytes of the media it shows with
|
||||
`{"type": "media", "names": [...]}`, which replaces what it asked for before,
|
||||
for at most 8 messages. Each frame held in memory then arrives as a binary
|
||||
for at most 8 messages, the last 8 it names when it names more. Each frame held in memory then arrives as a binary
|
||||
message before the value naming it: four bytes of header length (big endian),
|
||||
a JSON header `{"type": "media", "name", "digest", "media_type", "ts"}`, then
|
||||
the bytes. Messages are sent without per-message compression.
|
||||
@@ -264,17 +267,26 @@ A device's socket is bounded to its own messages. Its snapshot carries
|
||||
`"uplink"`, `"binary"` on its own socket and `"http"` through the portal. When
|
||||
that set changes it is sent `{"type": "demand", "ports": [...]}`. A port has
|
||||
demand while a socket asks for the media of the message it is wired to, or a
|
||||
node of a running flow reads that message.
|
||||
node of a running flow reads that message. A socket that falls so far behind
|
||||
that events are dropped for it is sent a fresh snapshot, demand included, and
|
||||
is closed with 1008 if it was unpaired meanwhile.
|
||||
|
||||
A person who may not watch a camera (see
|
||||
[Who may watch a camera](../interface/dashboards.md#who-may-watch-a-camera))
|
||||
gets none of its values in the snapshot or after it, and no frames when asking
|
||||
for them. Switching the camera's setting sends that person's open sockets a
|
||||
fresh snapshot.
|
||||
|
||||
With a `binary` uplink the device sends a camera frame as a binary message in
|
||||
the same layout, with the header `{"type": "frame", "name": "<message>",
|
||||
"media_type": "image/jpeg", "ts": <device ms>}`. The message has to be one of
|
||||
its `in` ports wired as `image`. The frame is held in memory and published
|
||||
in one step, credited to its device node. A frame that does not qualify is
|
||||
logged and dropped and the socket stays open. With `http` it uploads with
|
||||
`PUT /artifacts?volatile=true&name=<message>` and publishes with
|
||||
`POST /messages/<message>`. A device that is unpaired or deleted while
|
||||
connected is closed with 1008.
|
||||
its `in` ports wired as `image`, and the whole message at most
|
||||
`MAX_FRAME_BYTES`. The frame is held in memory and published in one step,
|
||||
named after the message and credited to its device node. A frame that does not
|
||||
qualify is dropped, the reason logged once per socket, and the socket stays
|
||||
open. With `http` it uploads with `PUT /artifacts?volatile=true&name=<message>`
|
||||
and publishes with `POST /messages/<message>`. A device that is unpaired or
|
||||
deleted while connected is closed with 1008, at the latest with its next frame.
|
||||
|
||||
### Live view
|
||||
|
||||
@@ -294,10 +306,11 @@ the viewer. In order:
|
||||
| 4 | sends and hears `candidate`, the bare `candidate:...` line | hears and sends `candidate` |
|
||||
| 5 | sends `bye`, or hears `bye` or `busy` with a `reason` | hears `bye` with a `reason`, or sends `bye` or `busy` |
|
||||
|
||||
Any signed-in person may watch any device. A device speaks only for the
|
||||
sessions it was handed on that socket. The server answers `busy` itself once a
|
||||
device has 2 sessions, and `bye` with `offline` or `unsupported` when the
|
||||
device is not connected or reports no `live` port. A session the viewer has not
|
||||
A superuser may watch any device, anyone else a device whose camera is open
|
||||
to members, and `start` is answered `bye` with `forbidden` otherwise. A device
|
||||
speaks only for the sessions it was handed on that socket. The server answers
|
||||
`busy` itself once a device has 2 sessions, and `bye` with `offline` or
|
||||
`unsupported` when the device is not connected or reports no `live` port. A session the viewer has not
|
||||
answered within 10 seconds ends with `timeout`, and either socket closing ends
|
||||
its sessions with `closed` or `offline`. A socket that falls 64 signalling
|
||||
messages behind ends the session with `overflow` instead of losing one.
|
||||
|
||||
@@ -552,12 +552,20 @@ lights up on the canvas. `out` messages are read only. The node is shown
|
||||
degraded, *Offline*, in amber while the device is not connected: a warning on
|
||||
Home, not a fault, since a panel switched off or asleep breaks nothing.
|
||||
|
||||
A `bool` the device publishes, such as a camera's `person`, says something is
|
||||
so right now, and a device that went away cannot say it no longer is. Once the
|
||||
device has been away for 60 seconds, the same wait as before its node shows
|
||||
*Offline*, each of its `bool` messages that reads true is set to false by its
|
||||
device node. A device back within that wait keeps its values, so a restart of
|
||||
either side changes nothing. Its other values keep the last one it sent.
|
||||
|
||||
### What a device reports
|
||||
|
||||
A paired device says what it has with `PUT /devices/{id}/report`, from its own
|
||||
credential only. openpanel sends it after pairing and once per boot. The report
|
||||
replaces the one before and is at most 16 KB. It is read leniently, so a newer
|
||||
firmware still reports to an older instance:
|
||||
replaces the one before and is at most 16 KB, and 64 KB as kept, once named
|
||||
`config_schemas` are put in place. It is read leniently, so a newer firmware
|
||||
still reports to an older instance:
|
||||
|
||||
- keys this version does not know are dropped
|
||||
- a value it does not know for a display `shape` or a port `direction` reads as
|
||||
@@ -635,6 +643,29 @@ A device may fetch the last 8 artifacts each of its messages pointed at, not
|
||||
only the current one, so a screen fetching a camera frame by frame through the
|
||||
portal is not refused a frame the camera has just replaced.
|
||||
|
||||
#### Who may watch a camera
|
||||
|
||||
**Who may watch** in the Camera section of the device's page decides who sees
|
||||
what its camera sends, its `image` and `video` messages: **Superusers**, the
|
||||
default, or **All members**. A member of the portal is a member here. For
|
||||
anyone it leaves out:
|
||||
|
||||
- the messages' values are left out of the live socket, `GET /messages/` and a
|
||||
flow's state, and their history answers 403
|
||||
- the frames those messages hold, the last 8 included, answer 403, and a
|
||||
socket asking for their bytes gets none
|
||||
- a live view is refused with `forbidden`, and so are TURN credentials
|
||||
- a dashboard tile or the device's preview says *No access to this camera*
|
||||
|
||||
A wall panel shows the camera when one of its dashboards carries it, whoever
|
||||
watches the wall: a superuser assigned it the dashboards.
|
||||
|
||||
The setting covers the camera's own messages, not what a flow makes of them.
|
||||
Anyone who edits flows can wire a node that passes a picture on under another
|
||||
name, and node code reads whatever it is handed. How long a kept `snapshot`
|
||||
stays and a light on the device that shows the camera is on are not part of
|
||||
it yet.
|
||||
|
||||
#### Live view
|
||||
|
||||
A media tile showing a device's `camera`, and the preview on the device's
|
||||
|
||||
@@ -169,14 +169,18 @@ MIG entries are passed on as written.
|
||||
| `ARTIFACT_GC_GRACE_S` | `3600` | minimum age before cleanup |
|
||||
| `ARTIFACT_VOLATILE_DIR` | auto-detected | where local workers write live frames, under `/dev/shm` or the temporary directory |
|
||||
| `ARTIFACT_VOLATILE_BYTES` | `50331648` (48 MiB) | live-media ring size, in the engine's memory and again in that directory; `0` stores volatile saves durably |
|
||||
| `MAX_FRAME_BYTES` | `4194304` (4 MiB) | the largest live frame: a volatile upload past it gets 413, a device's frame on its socket is dropped; keep it under 16 MiB, the websocket's limit |
|
||||
|
||||
Cleanup removes durable artifacts no longer referenced by recorded runs or
|
||||
current messages, and pauses while a run is active. Volatile artifacts live in
|
||||
a bounded ring in the engine's memory, and the oldest disappear as it fills.
|
||||
Recorded run outputs are copied into durable storage. A worker in the engine's
|
||||
container writes its frames to `ARTIFACT_VOLATILE_DIR` instead, which the
|
||||
engine trims to the same size every few seconds. If workers publish frames
|
||||
under Docker, raise `shm_size` with the ring size.
|
||||
engine trims to the same size every few seconds. A frame the engine holds is
|
||||
written there too before a node in this container is handed it, so
|
||||
`load_artifact` finds it. If workers publish or read frames under Docker,
|
||||
raise `shm_size` with the ring size. The ring never holds more than its size
|
||||
plus one frame.
|
||||
|
||||
## Agents
|
||||
|
||||
|
||||
Reference in New Issue
Block a user