Muninn/docs/muninn.md
Meta Vens e9580009ea Retire --idunn-rudp-health and the daemon-health publisher
The flag family (--idunn-rudp-health, --idunn-daemon,
--idunn-health-contract), IdunnRudpHealthOptions, the health worker
thread, idunn_health_payload, the RUDP health connection and the
idunn.daemon_health document had no consumer since the previous Idunn
generation. The raven-muninn recipe never passed them; liveness is the
runtime-presence publisher. Removes the IdunnDaemonHealthRecord odin-core
symbol (three remain).

The daemon id is now derived from the host alone. The transport profile
and command-boundary records no longer carry health_publication, and
health_contract is null. The starfire, nightwing and raven health and
restart scripts stop passing and checking the flags; muninn.md follows.

Tests: the retired flags are unknown arguments; the boundary record
carries the host-derived daemon id and no health_publication.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:17:13 +02:00

252 lines
12 KiB
Markdown

# Muninn
Muninn is Odin's portable local telemetry Verse assembler.
It runs on a device body and publishes a typed CultMesh/CultCache surface for
what that body can sense or expose locally: screen capture, loopback audio,
microphones, cameras, and future sensors.
## Authority
- Owner: Muninn owns local telemetry discovery, stream affordance publication,
and explicit activation of requested local streams.
- Inputs: cheap local probes, operator or Verse activation requests, and local
capture tools such as FFmpeg or WASAPI helpers.
- Outputs: `muninn.telemetry_surface.v1`, active `muninn.capture_stream.v1`,
Quest access records, Move receipt records, and a CultMesh bytes stream
carrying live Move evidence frames for Mimir.
- Forbidden writers: daemon startup, Idunn keepalive, Mimir ingest, OBS, Odin,
Gjallar, and renderer bodies must not start capture by implication.
## Runtime
```powershell
cargo build -p muninn-daemon
muninn serve --store C:\Meta\Odin\state\muninn.telemetry.cc --interval-seconds 15
muninn --health --store C:\Meta\Odin\state\muninn.telemetry.cc
```
`serve` is cheap and idle. It publishes affordances, consumes addressed pending
`muninn.move_light_command.v1` records, and keeps the local Verse surface fresh
without starting FFmpeg, screen capture, microphone capture, or loopback
capture.
Muninn writes its stream affordance catalog as typed CultCache state inside:
```text
C:\Meta\Odin\state\muninn.telemetry.cc
```
The record is type `muninn.obs_stream_catalog` with schema
`muninn.obs_stream_catalog.v1` at key `obs`. Consumers discover the catalog
through Odin/CultMesh; Muninn does not publish a parallel OBS RUDP catalog.
Activation is explicit typed state:
```powershell
muninn request-stream --target-host cultmesh://odin/media/muninn-raven-av --media-transport rudp
```
`target_host` in the legacy command record field is a CultMesh URI, not a host
or IP. The activation child resolves `muninn.media.rudp.v1` endpoints from
Odin's provider catalog before opening the CultNet RUDP media lane. SRT, OBS
target flags, and standalone Raven activation scripts are archived; Mimir,
OBS, and other renderers are consumers of Odin/CultMesh discovery, not transport
owners.
## Quest Access And Unity Return Video
Quest hardware attached to Starfire is a Muninn local telemetry surface, not a
Mimir-owned preflight. Enable it by running Muninn on Starfire with ADB probing:
```powershell
muninn serve `
--store C:\Meta\Odin\state\muninn.telemetry.cc `
--host starfire `
--quest-adb `
--quest-serial 1WMHHB68PG1515
```
When `adb devices -l` reports the Quest as `device`, Muninn publishes
`muninn.quest_access.v1` at `quest-access` and
`muninn:<host>:quest-access:<serial>`. The record advertises:
- `muninn:<host>:quest-input`: Quest buttons/analog/controller input once a
Quest/OpenXR witness is running.
- `muninn:<host>:quest-poses`: headset and controller poses from that same
witness.
- `muninn:<host>:quest-warped-video-input`: warp-corrected video frames that
Brokkr can route from Starfire Unity editor play mode toward the Quest device.
ADB authorization proves local USB access only. It does not by itself expose
OpenXR poses or accept video frames. A Quest/OpenXR witness still owns headset
runtime sampling, while Brokkr owns the Unity editor adapter that sends
warp-corrected play-mode frames to Muninn's advertised Quest video input.
Read the current record with:
```powershell
muninn quest-access-status --store C:\Meta\Odin\state\muninn.telemetry.cc
```
For Starfire's local Quest-attached daemon, Idunn supervises the
`starfire-muninn` target through:
```powershell
E:\Projects\Odin\scripts\restart-starfire-muninn.cmd
E:\Projects\Odin\scripts\health-starfire-muninn.cmd
```
The restart script launches Muninn hidden with `--host starfire --quest-adb`.
If the CultCache store at `C:\Meta\Odin\state\starfire.muninn.telemetry.cc`
fails MessagePack decode on boot, the restart path archives the corrupt file,
clears the stale `.lock`, and relaunches the daemon instead of leaving the lane
dead.
The deployed loopback helper must accept Muninn's command contract:
```powershell
wasapi-loopback-capture.ps1 -Output stdout -Device Realtek -SampleRate 48000 -Channels 2
```
`stdout` is an alias for binary standard output, and `Device` is the requested
render-device hint. Current helper builds may ignore the hint and use the
default render endpoint, but they must accept the argument so Muninn's generated
mux command remains executable.
## Move Marker Candidates
`crates/muninn-move-tracker` is Muninn's native/Rust PS Move optical candidate
extractor. It owns dispatch planning, FFI, configuration validation, a CPU
mirror, and the HLSL 16px-tile luma reduction shader. It emits marker
candidates only: weighted centroid, radius, area, peak/mean luma, and score for
one camera frame.
Muninn owns publishing those candidates as `muninn.move_marker_candidate.v1`.
Muninn also publishes USB controller facts as
`muninn.move_controller_state.v1`: accelerometer, gyro, magnetometer, button,
trigger, battery, sequence, and source timestamp. Mimir is the consumer of both
sensor streams. Odin may discover and project the schemas, but Odin does not
own raw capture, candidate extraction, calibration, triangulation, IMU fusion,
prediction, or final 6DoF pose. Muninn does not synthesize wand pose; it
reports what the local body saw and read from USB.
The hot tracking path is a CultMesh stream, not CultCache polling. When
`serve` has one or more `--move-state` sources, it declares
`muninn:<host>:move-evidence` in Verse `mimir-live` and publishes
MessagePack `mimir.muninn_move_evidence_stream_frame.v1` bytes through a
shared-memory frame ring. The frame contains any marker candidates available
from local optical extraction plus the controller states read from USB. The
`muninn.move_marker_candidate.v1` and `muninn.move_controller_state.v1` records
remain receipts/debug state in the `.cc` store; Mimir drinks the stream and
owns association, calibration, fusion, prediction, and final pose.
On Linux hosts, enable the controller-state feed by passing one or more Move
joystick sources to `serve`:
```bash
muninn serve \
--store ~/.local/state/gamecult/muninn/muninn.telemetry.cc \
--host nightwing \
--move-state move-usb=/dev/input/by-id/usb-Sony_Computer_Entertainment_Motion_Controller-joystick
```
The published values are raw Linux joystick/HID counts. Mimir owns calibration,
axis interpretation, unit conversion, fusion, prediction, and resolved pose
publication. Hidraw remains the local output path for LED reports.
Use `--move-evidence-stream <stream-id>` or `--move-evidence-verse <verse-id>`
only when the default `muninn:<host>:move-evidence` / `mimir-live` address is
not the desired Mimir-facing stream identity.
## Move Light Commands
Muninn is also the local output owner for USB-attached PS Moves. When Mimir
wants structured light pulses for calibration or tracking, it publishes a typed
`muninn.move_light_command.v1` command over CultNet/CultMesh to the Muninn
daemon on the host that owns the Move. `serve` consumes `pending` commands
whose `host_id` matches the local Muninn host, writes PS Move HID report `0x06`
to the command's `hidraw_path`, and updates the same command record to
`running`, `completed`, or `failed`.
For operator smoke and bring-up, `request-move-light` writes that typed command
into the local Muninn store without touching HID directly:
```bash
muninn request-move-light \
--store ~/.local/state/gamecult/muninn/muninn.telemetry.cc \
--host nightwing \
--move move-usb \
--hidraw /dev/hidraw1 \
--color 35ff6c \
--duration-ms 0 \
--repeat-count 1
muninn move-light-status \
--store ~/.local/state/gamecult/muninn/muninn.telemetry.cc \
--host nightwing
```
Idunn keeps the Muninn daemon alive. Idunn does not learn a Move-specific
watcher, and Mimir does not write HID directly except through temporary smoke
scripts used to prove hardware behavior before a Muninn daemon is available.
`--health` reads the telemetry store and reports; it publishes nothing.
Quest ADB probing is a telemetry input, not daemon liveness. If `adb` is
missing or the Quest is unavailable, Muninn publishes `muninn.quest_access` as
`unavailable` and keeps serving the local telemetry surface.
## Host Deployments
Raven is deployed by Idunn since 2026-09-11. `deployment/idunn/raven-muninn.toml`
is the target declaration: the build step, the two artifacts (`muninn.exe`
and the loopback capture script) and the launch contract. Raven's paths and
advertised endpoints are argument bindings in the yggdrasil operator binding
(`gamecult-ops/idunn/yggdrasil/bindings/raven-muninn.toml.in`). The host
actuator (`idunn-host`, from the Idunn repo) runs in Raven's user session,
builds the exact frozen revision with Raven's own cargo, installs the sealed
release under `C:\GameCult\idunn\releases\raven-muninn`, and starts `serve`
as its child with the runtime bundle, the activation credential and the
provider identity path in the environment. `serve` then publishes its
runtime presence to Odin (`src/idunn_presence.rs`); Odin's correlation is
what admits it and keeps it alive. Deploying is `sudo idunn up raven-muninn`
on yggdrasil; the runbook is `gamecult-ops/runbooks/idunn-host-raven.md`.
No script on any workstation starts `serve` on Raven.
Raven is an operator-consented host: Muninn operations on Raven must be
background-only and must not create visible terminal windows. `.cmd` files may
exist only as manual compatibility entrypoints that call the same hidden VBS
launchers; neither Task Scheduler nor the hidden VBS layer may route through a
`cmdPath` trampoline on Raven.
Health must fail if the obsolete activation or video-proof tasks still exist on
Raven. If Raven is unreachable, the repo is prepared but the live scheduler is
not clean.
Nightwing Muninn is kept alive by the single Idunn supervisor through the
`nightwing-muninn` daemon target. Idunn learns that target through Odin's typed
daemon surface and invokes `scripts/health-nightwing-muninn.ps1` and
`scripts/restart-nightwing-muninn.ps1`; those scripts are health/restart
actuators only, not lifecycle owners. The binary is installed at
`/home/metacrat/.local/bin/muninn`, the store lives at
`/home/metacrat/.local/state/gamecult/muninn/muninn.telemetry.cc`, and the
restart actuator launches `serve --host nightwing --interval-seconds 15` with
`--move-state move-usb=/dev/input/by-id/usb-Sony_Computer_Entertainment_Motion_Controller-joystick`,
PID, and logs under
`/home/metacrat/.local/state/gamecult/muninn`.
With that Move state source attached, `serve` also publishes the
`muninn:nightwing:move-evidence` CultMesh stream frame body that Mimir's native
Move evidence reservoir consumes.
Nightwing source discovery must emit one Move source per controller id. The USB
pairing collection and Bluetooth joystick can expose the same controller as
separate `/dev/input/js*` paths; `nightwing-move-state-sources.sh` prefers the
Bluetooth `HID_ID=0005:0000054C:000003D5` path so Idunn health does not demand
fresh records from two faces of the same controller.
Each private PSMoveAPI Eye worker owns optical observation admission before the
parent aggregates evidence. Positions older than 50 ms, blobs below 2 px,
out-of-frame coordinates, implausible radius jumps, and frame-to-frame
teleports are rejected locally. Typed `muninn.move_tracker_health.v1` records
publish rejection counts by stale/radius/bounds/continuity reason.
PSMoveAPI's position age is freshness, not optical confidence. The aggregate
marker candidate therefore carries an explicit uncalibrated score of `0.5`
until the observer exposes a real fit-quality measure. Consumers must not treat
the thresholded color-blob radius as a calibrated physical sphere silhouette;
per-Eye radius response belongs to Mimir's calibration evidence.