feat(sensing-server): add secure opt-in OTLP log export

Import and harden the OpenTelemetry logging work from #1382. Preserve default stderr behavior, register and validate RuView semantic conventions, attach a published schema, enable TLS roots, pin demo images, and fix first-CSI node lifecycle reporting.

Supersedes #1382
Closes #1460

Co-authored-by: Jens Holdgaard Pedersen <jens@holdgaard.org>
This commit is contained in:
ruv
2026-07-28 22:42:18 -04:00
parent a70ca90525
commit 9fb5af7cf2
18 changed files with 1250 additions and 26 deletions
+106
View File
@@ -0,0 +1,106 @@
groups:
- id: registry.ruview
type: attribute_group
display_name: RuView attributes
brief: Attributes shared across RuView sensing telemetry.
attributes:
- id: ruview.node.id
type: int
stability: development
brief: The ESP32 mesh node the event pertains to.
note: >-
The one-byte node id carried in the ESP32 CSI / edge-vitals
frame header. Simulated frames use node id 1.
examples: [1, 2]
- id: ruview.csi.source
type: string
stability: development
brief: The data source that produced the sensing cycle.
note: >-
One of the sensing server's source labels: `esp32` (live CSI or
edge-vitals frames over UDP), `wifi` (host WiFi RSSI scanning),
or `simulated` (the built-in synthetic frame generator).
examples: ["esp32", "wifi", "simulated"]
- id: ruview.csi.frames_total
type: int
stability: development
brief: Sensing frames processed since process start (the server's tick counter).
examples: [100, 42000]
- id: ruview.csi.nodes_active
type: int
stability: development
brief: Nodes that delivered a frame within the liveness window.
examples: [0, 3]
- id: ruview.presence.state
type:
members:
- id: present
value: "present"
stability: development
brief: The classifier reports at least one person present.
- id: absent
value: "absent"
stability: development
brief: The classifier reports the space as empty.
stability: development
brief: The presence classification after smoothing and any adaptive-model override.
- id: ruview.motion.level
type:
members:
- id: absent
value: "absent"
stability: development
brief: No presence detected.
- id: present_still
value: "present_still"
stability: development
brief: Presence with little or no motion.
- id: present_moving
value: "present_moving"
stability: development
brief: Presence with moderate motion.
- id: active
value: "active"
stability: development
brief: Presence with high motion energy.
stability: development
brief: >-
The motion-level class attached to a sensing update (the
adaptive classifier's class set).
- id: ruview.inference.confidence
type: double
stability: development
brief: Confidence of the presence/motion classification, in [0.0, 1.0].
examples: [0.7, 0.95]
- id: ruview.persons.count
type: int
stability: development
brief: The estimated person count for the sensing cycle.
examples: [0, 2]
- id: ruview.vitals.breathing_rate_bpm
type: double
stability: development
brief: Estimated breathing rate in breaths per minute.
note: "`0.0` when no estimate was produced in this window — check the confidence attribute."
examples: [14.5]
- id: ruview.vitals.heart_rate_bpm
type: double
stability: development
brief: Estimated heart rate in beats per minute.
note: "`0.0` when no estimate was produced in this window — check the confidence attribute."
examples: [62.0]
- id: ruview.vitals.breathing_confidence
type: double
stability: development
brief: Confidence of the breathing-rate estimate, in [0.0, 1.0].
examples: [0.7]
- id: ruview.vitals.heartbeat_confidence
type: double
stability: development
brief: Confidence of the heart-rate estimate, in [0.0, 1.0].
examples: [0.7]
- id: ruview.model.id
type: string
stability: development
brief: The identifier of a loaded inference model.
examples: ["wifi-densepose-v1"]
+106
View File
@@ -0,0 +1,106 @@
groups:
# Log event names for the sensing server's curated telemetry — each
# instrumented `tracing` call site carries one of these as its explicit
# event name (never tracing's default `event <file>:<line>`), so the
# exported Logs signal stays registry-backed. Uncurated log lines keep
# their default names; only these events are part of the contract.
- id: event.ruview.node.online
type: event
name: ruview.node.online
stability: development
brief: >
A sensing node delivered its first frame (CSI or edge-vitals) —
either a new node joining the mesh or a previously evicted node
returning.
attributes:
- ref: ruview.node.id
requirement_level: required
- id: event.ruview.node.offline
type: event
name: ruview.node.offline
stability: development
brief: >
A sensing node was evicted after delivering no frames for the
staleness window (60 s).
attributes:
- ref: ruview.node.id
requirement_level: required
- id: event.ruview.csi.stats
type: event
name: ruview.csi.stats
stability: development
brief: >
Periodic CSI capture snapshot (every 100 sensing ticks): total
frames processed and currently active nodes.
attributes:
- ref: ruview.csi.frames_total
requirement_level: required
- ref: ruview.csi.nodes_active
requirement_level: required
- ref: ruview.csi.source
requirement_level: recommended
- id: event.ruview.presence.changed
type: event
name: ruview.presence.changed
stability: development
brief: >
The smoothed presence classification flipped between present and
absent. Emitted on transitions only, never per frame.
attributes:
- ref: ruview.presence.state
requirement_level: required
- ref: ruview.motion.level
requirement_level: recommended
- ref: ruview.inference.confidence
requirement_level: recommended
- ref: ruview.persons.count
requirement_level: recommended
- ref: ruview.csi.source
requirement_level: recommended
- id: event.ruview.vitals.estimate
type: event
name: ruview.vitals.estimate
stability: development
brief: >
Periodic vital-sign estimate (breathing / heart rate with
confidences), emitted on the ruview.csi.stats cadence when the
detector produced an estimate.
attributes:
- ref: ruview.vitals.breathing_rate_bpm
requirement_level: recommended
- ref: ruview.vitals.heart_rate_bpm
requirement_level: recommended
- ref: ruview.vitals.breathing_confidence
requirement_level: recommended
- ref: ruview.vitals.heartbeat_confidence
requirement_level: recommended
- ref: ruview.csi.source
requirement_level: recommended
- id: event.ruview.fall.detected
type: event
name: ruview.fall.detected
stability: development
brief: >
An ESP32 edge-vitals frame raised its fall flag. Edge-triggered on
the flag's rising edge per node, not re-emitted while it stays set.
attributes:
- ref: ruview.node.id
requirement_level: required
- id: event.ruview.mqtt.error
type: event
name: ruview.mqtt.error
stability: development
brief: >
An MQTT publish or connection error in the Home Assistant
discovery publisher; the publisher reconnects and retries.
attributes:
- ref: ruview.node.id
requirement_level: opt_in
- id: event.ruview.model.loaded
type: event
name: ruview.model.loaded
stability: development
brief: An inference model was loaded via the model-management API.
attributes:
- ref: ruview.model.id
requirement_level: required
+6
View File
@@ -0,0 +1,6 @@
name: ruview
description: RuView custom OpenTelemetry semantic conventions.
schema_url: https://raw.githubusercontent.com/ruvnet/RuView/main/semconv/schema/ruview-0.1.0.yaml
dependencies:
- name: otel
registry_path: https://github.com/open-telemetry/semantic-conventions/archive/refs/tags/v1.42.0.zip[model]
+11
View File
@@ -0,0 +1,11 @@
# OpenTelemetry schema file format version. This is independent of the RuView
# semantic convention version below.
file_format: 1.1.0
# The canonical URL where this schema file is published.
schema_url: https://raw.githubusercontent.com/ruvnet/RuView/main/semconv/schema/ruview-0.1.0.yaml
versions:
# Initial RuView semantic convention release. There are no prior versions to
# transform from.
0.1.0: