Muninn/docs/muninn.md
Meta Vens 2742b4ddcf Correct stale health text, pin per-host boundary identity, note deploy order
The MuninnRecords comment claimed muninn-contracts decides every muninn.*
binding; today odin-core registers identical strings first and the
registration is inert until Odin deletes its copies. The help text no
longer describes the retired --idunn flags, the health-*.cmd stubs no
longer claim daemon-published health, and docs/muninn.md says to install
the binary and the scripts together. A test now pins daemon_id, state and
current_transport for raven, starfire and nightwing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 00:17:51 +02:00

12 KiB

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

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:

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:

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:

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:

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:

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:

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:

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:

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.

Starfire and Nightwing binaries and their scripts/*-muninn.ps1 actuators must be installed together: a binary that rejects the retired --idunn-rudp-health, --idunn-daemon and --idunn-health-contract flags fails to start under an old restart script, and a new script never passes them.

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.