2026-06-08 20:28:39 +01:00
|
|
|
# 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.
|
2026-06-10 20:51:46 +01:00
|
|
|
- Outputs: `muninn.telemetry_surface.v1`, active `muninn.capture_stream.v1`,
|
2026-06-12 16:09:03 +01:00
|
|
|
Quest access records, Move receipt records, and a CultMesh bytes stream
|
|
|
|
|
carrying live Move evidence frames for Mimir.
|
2026-06-08 20:28:39 +01:00
|
|
|
- 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
|
2026-07-04 22:38:43 +01:00
|
|
|
muninn serve --store C:\Meta\Odin\state\muninn.telemetry.cc --interval-seconds 15 --idunn-rudp-health $env:IDUNN_RUDP_HEALTH --idunn-daemon starfire-muninn --idunn-health-contract muninn.cultnet-rudp-local-telemetry-and-quest-access
|
2026-06-08 20:28:39 +01:00
|
|
|
muninn --health --store C:\Meta\Odin\state\muninn.telemetry.cc
|
|
|
|
|
```
|
|
|
|
|
|
2026-06-11 07:58:38 +01:00
|
|
|
`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.
|
2026-06-08 20:28:39 +01:00
|
|
|
|
2026-07-04 22:38:43 +01:00
|
|
|
Muninn writes its stream affordance catalog as typed CultCache state inside:
|
2026-06-08 21:01:33 +01:00
|
|
|
|
|
|
|
|
```text
|
2026-06-08 21:31:26 +01:00
|
|
|
C:\Meta\Odin\state\muninn.telemetry.cc
|
2026-06-08 21:01:33 +01:00
|
|
|
```
|
|
|
|
|
|
2026-06-08 21:31:26 +01:00
|
|
|
The record is type `muninn.obs_stream_catalog` with schema
|
2026-07-04 22:38:43 +01:00
|
|
|
`muninn.obs_stream_catalog.v1` at key `obs`. Consumers discover the catalog
|
|
|
|
|
through Odin/CultMesh; Muninn does not publish a parallel OBS RUDP catalog.
|
2026-06-08 21:01:33 +01:00
|
|
|
|
2026-07-04 22:38:43 +01:00
|
|
|
Activation is explicit typed state:
|
2026-06-08 20:28:39 +01:00
|
|
|
|
|
|
|
|
```powershell
|
2026-07-04 22:38:43 +01:00
|
|
|
muninn request-stream --target-host cultmesh://odin/media/muninn-raven-av --media-transport rudp
|
2026-06-08 20:28:39 +01:00
|
|
|
```
|
|
|
|
|
|
2026-07-04 22:38:43 +01:00
|
|
|
`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.
|
2026-06-08 22:52:44 +01:00
|
|
|
|
2026-06-12 16:09:03 +01:00
|
|
|
## 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
|
|
|
|
|
```
|
|
|
|
|
|
2026-06-16 17:02:50 +01:00
|
|
|
The restart script launches Muninn hidden with `--host starfire --quest-adb`,
|
2026-07-04 22:38:43 +01:00
|
|
|
`--idunn-rudp-health` from explicit `-IdunnRudpHealth` or
|
|
|
|
|
`IDUNN_RUDP_HEALTH`, `--idunn-daemon starfire-muninn`, and
|
2026-06-16 17:02:50 +01:00
|
|
|
`--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.
|
2026-06-12 16:09:03 +01:00
|
|
|
|
2026-06-08 22:52:44 +01:00
|
|
|
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.
|
2026-06-10 20:51:46 +01:00
|
|
|
|
|
|
|
|
## 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`.
|
2026-06-11 17:01:19 +01:00
|
|
|
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.
|
2026-06-11 07:58:38 +01:00
|
|
|
|
2026-06-12 10:45:47 +01:00
|
|
|
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.
|
|
|
|
|
|
2026-06-11 18:03:44 +01:00
|
|
|
On Linux hosts, enable the controller-state feed by passing one or more Move
|
|
|
|
|
joystick sources to `serve`:
|
2026-06-11 17:48:15 +01:00
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
muninn serve \
|
|
|
|
|
--store ~/.local/state/gamecult/muninn/muninn.telemetry.cc \
|
|
|
|
|
--host nightwing \
|
2026-06-11 18:03:44 +01:00
|
|
|
--move-state move-usb=/dev/input/by-id/usb-Sony_Computer_Entertainment_Motion_Controller-joystick
|
2026-06-11 17:48:15 +01:00
|
|
|
```
|
|
|
|
|
|
2026-06-11 18:03:44 +01:00
|
|
|
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.
|
2026-06-12 10:45:47 +01:00
|
|
|
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.
|
2026-06-11 17:48:15 +01:00
|
|
|
|
2026-06-11 07:58:38 +01:00
|
|
|
## 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`.
|
|
|
|
|
|
2026-06-11 08:44:43 +01:00
|
|
|
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 \
|
2026-06-11 08:50:53 +01:00
|
|
|
--color 35ff6c \
|
2026-06-11 08:44:43 +01:00
|
|
|
--duration-ms 0 \
|
|
|
|
|
--repeat-count 1
|
2026-06-11 08:50:53 +01:00
|
|
|
|
|
|
|
|
muninn move-light-status \
|
|
|
|
|
--store ~/.local/state/gamecult/muninn/muninn.telemetry.cc \
|
|
|
|
|
--host nightwing
|
2026-06-11 08:44:43 +01:00
|
|
|
```
|
|
|
|
|
|
2026-06-11 07:58:38 +01:00
|
|
|
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.
|
2026-06-16 17:02:50 +01:00
|
|
|
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.
|
2026-06-16 00:07:55 +01:00
|
|
|
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.
|
2026-06-11 08:29:25 +01:00
|
|
|
|
|
|
|
|
## Host Deployments
|
|
|
|
|
|
|
|
|
|
Raven runs Muninn from `C:\Meta\Odin\Muninn`. `scripts/restart-muninn.ps1`
|
2026-07-04 22:38:43 +01:00
|
|
|
recreates only the `GameCult-Muninn` scheduled task. It also unregisters the
|
|
|
|
|
obsolete `GameCult-Muninn-Activate` and `GameCult-Muninn-VideoProof` tasks if
|
|
|
|
|
they are present. The serve launcher starts `muninn.exe` with
|
|
|
|
|
`-WindowStyle Hidden`, requires `--idunn-rudp-health` from explicit
|
|
|
|
|
`-IdunnRudpHealth` or `IDUNN_RUDP_HEALTH`, passes `--idunn-daemon muninn`, and
|
2026-06-16 17:02:50 +01:00
|
|
|
`--idunn-health-contract muninn.cultnet-rudp-remote-telemetry-health`, and
|
2026-07-04 22:38:43 +01:00
|
|
|
requires `-MediaTargetUri` / `MUNINN_MEDIA_TARGET_URI` to be a `cultmesh://`
|
|
|
|
|
URI resolved through Odin at activation time. It
|
2026-06-16 17:02:50 +01:00
|
|
|
redirects logs under `C:\Meta\Odin\logs\muninn`.
|
2026-06-16 01:27:45 +01:00
|
|
|
Raven is an operator-consented host: Muninn operations on Raven must be
|
2026-06-16 02:24:14 +01:00
|
|
|
background-only and must not create visible terminal windows. `.cmd` files may
|
2026-06-16 21:21:08 +01:00
|
|
|
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.
|
2026-07-04 22:38:43 +01:00
|
|
|
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.
|
2026-06-11 08:29:25 +01:00
|
|
|
|
2026-06-11 16:41:21 +01:00
|
|
|
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
|
2026-06-11 08:29:25 +01:00
|
|
|
`/home/metacrat/.local/state/gamecult/muninn/muninn.telemetry.cc`, and the
|
2026-06-11 16:41:21 +01:00
|
|
|
restart actuator launches `serve --host nightwing --interval-seconds 15` with
|
2026-06-11 18:03:44 +01:00
|
|
|
`--move-state move-usb=/dev/input/by-id/usb-Sony_Computer_Entertainment_Motion_Controller-joystick`,
|
2026-07-04 22:38:43 +01:00
|
|
|
`--idunn-rudp-health` supplied by explicit `-IdunnRudpHealth` or
|
|
|
|
|
`IDUNN_RUDP_HEALTH`,
|
2026-06-16 17:02:50 +01:00
|
|
|
`--idunn-daemon nightwing-muninn`,
|
|
|
|
|
`--idunn-health-contract muninn.cultnet-rudp-remote-telemetry-and-move-hid`,
|
2026-06-11 18:03:44 +01:00
|
|
|
PID, and logs under
|
2026-06-11 17:48:15 +01:00
|
|
|
`/home/metacrat/.local/state/gamecult/muninn`.
|
2026-06-12 10:45:47 +01:00
|
|
|
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.
|
2026-06-16 00:45:48 +01:00
|
|
|
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
|
2026-07-04 22:38:43 +01:00
|
|
|
actuator requires the same configured Idunn RUDP endpoint, but that command is
|
|
|
|
|
fallback proof only; the live keepalive contract is now published by the
|
|
|
|
|
long-running Nightwing `serve` process.
|
2026-07-14 01:06:57 +01:00
|
|
|
|
|
|
|
|
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.
|