# 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 --idunn-rudp-health 127.0.0.1:17870 --idunn-daemon starfire-muninn --idunn-health-contract muninn.cultnet-rudp-local-telemetry-and-quest-access 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 also writes an OBS stream 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`. On Starfire, `health-muninn.ps1` syncs Raven's `.cc` store to the same local path so the OBS plugin can read CultCache directly without learning Raven SSH. The current Raven A/V path is a compatibility proof, not the final hot media shape. Its rebuild map lives in [`docs/muninn-media-streaming.md`](muninn-media-streaming.md): Muninn should publish encoded video access units and audio packets as typed CultMesh media frames with frame ids, deadlines, receiver feedback, and explicit audio/video policy instead of treating MPEG-TS bytes as the transport's unit of truth. Activation is explicit: ```powershell .\scripts\activate-muninn-raven-av-srt.ps1 ``` That Raven activation starts the requested screen and Realtek loopback stream and publishes `muninn.capture_stream.v1`. It is still a background-only Raven actuator: the remote scheduled task must launch through the hidden WScript / noninteractive PowerShell trampoline and must not create visible terminal windows on Raven. Mimir and OBS are stream consumers; they do not own Muninn. ## 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::quest-access:`. The record advertises: - `muninn::quest-input`: Quest buttons/analog/controller input once a Quest/OpenXR witness is running. - `muninn::quest-poses`: headset and controller poses from that same witness. - `muninn::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`, `--idunn-rudp-health 127.0.0.1:17870`, `--idunn-daemon starfire-muninn`, and `--idunn-health-contract muninn.cultnet-rudp-local-telemetry-and-quest-access`. 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::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 ` or `--move-evidence-verse ` only when the default `muninn::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. When `serve` is launched with `--idunn-rudp-health`, `--idunn-daemon`, and `--idunn-health-contract`, the long-running Muninn body publishes `idunn.daemon_health` directly to Idunn over `cultnet.transport.rudp.v0` on its normal cadence. `--health` keeps the same publication path for manual proof and compatibility probes, but the live owner is the daemon's `serve` process. 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 runs Muninn from `C:\Meta\Odin\Muninn`. `scripts/restart-muninn.ps1` recreates the `GameCult-Muninn` scheduled task with `wscript.exe` as the task action and `start-muninn-serve-hidden.vbs` as its argument. It also repairs `GameCult-Muninn-Activate` and `GameCult-Muninn-VideoProof` to execute hidden VBS launchers whose bodies call PowerShell entrypoints directly, not `.cmd` payloads. The serve VBS launches `start-muninn-serve.ps1` with noninteractive hidden PowerShell. The PowerShell launcher starts `muninn.exe` with `-WindowStyle Hidden`, passes `--idunn-rudp-health 10.77.0.2:17870`, `--idunn-daemon muninn`, and `--idunn-health-contract muninn.cultnet-rudp-remote-telemetry-health`, and redirects logs under `C:\Meta\Odin\logs\muninn`. 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. The standalone repair actuator for live Raven task drift is `E:\Projects\Odin\scripts\repair-raven-muninn-task-actions.ps1`. It must register `GameCult-Muninn`, `GameCult-Muninn-Activate`, and `GameCult-Muninn-VideoProof` with `wscript.exe` as the task action and the corresponding `*-hidden.vbs` launcher as arguments while also verifying that the hidden VBS bodies reference `.ps1` launchers instead of `.cmd` payloads. 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`, `--idunn-rudp-health 10.77.0.2:17870`, `--idunn-daemon nightwing-muninn`, `--idunn-health-contract muninn.cultnet-rudp-remote-telemetry-and-move-hid`, 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. The Nightwing health actuator publishes `nightwing-muninn` health over RUDP to Starfire Idunn at `10.77.0.2:17870`, but that command is fallback proof only; the live keepalive contract is now published by the long-running Nightwing `serve` process.