The three muninn crates, their seventeen actuator scripts, the two Raven capture-mux wrappers that called one of them, and the two Muninn docs all leave this workspace. GameCult/Muninn has carried them since 2026-09-09 and has moved on (media stream advertisement, receivers dialling the producer); this copy was a fork drifting behind under the same name, and gamecult-ops now binds every Muninn target to the Muninn repository. Odin keeps the Muninn records in odin-core until they are moved; that is the remaining piece of the extraction. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
323 lines
16 KiB
Markdown
323 lines
16 KiB
Markdown
# Odin Architecture
|
|
|
|
## Objective
|
|
|
|
Odin is the central all-seer node for GameCult's CultMesh world: every Verse can discover every other Verse, learn which schemas they speak, and ask for a translation route when their local document shape differs.
|
|
|
|
## Current Mechanism
|
|
|
|
```text
|
|
Eve/CultUI provider surfaces
|
|
+ provider advertisements
|
|
-> Odin observation cycle
|
|
-> Verse-owned service records
|
|
-> Provider-owned interface records
|
|
-> Odin state document
|
|
-> CultMesh/CultCache persistence
|
|
-> Eve dashboard state
|
|
-> Eve, browser, compact TUI, and future renderers
|
|
```
|
|
|
|
This first path proves the operator surface and persistent state. It does not yet pretend to be full peer exchange.
|
|
|
|
## Rust Target Spine
|
|
|
|
The target Odin machine is Rust-first and typed-state-first:
|
|
|
|
```text
|
|
Verse / host / device / provider inputs
|
|
-> ingest ports
|
|
-> normalization
|
|
-> typed Odin records
|
|
-> CultMesh node
|
|
-> CultCache .cc persistence
|
|
-> CultNet/CultMesh document registry
|
|
-> Odin Eve/CultUI deck projection
|
|
-> Gjallar Yggdrasil aggregate Eve composition
|
|
-> compact display feeds
|
|
```
|
|
|
|
The first Rust core lives in `crates/odin-core`:
|
|
|
|
- `documents.rs`: typed Odin records and the CultMesh document set.
|
|
- `ports.rs`: narrow ingest traits plus clock injection for deterministic tests.
|
|
- `pipeline.rs`: collection and normalization from input observations to typed
|
|
Odin records.
|
|
- `repository.rs`: `OdinRepository` abstraction, in-memory mock repository, and
|
|
CultMesh-backed repository.
|
|
|
|
The Rust spine owns the future architecture. The CommonJS daemon remains the
|
|
legacy operational body until each organ crosses this typed boundary.
|
|
|
|
## Runtime Body
|
|
|
|
Odin's executable body is split by ownership:
|
|
|
|
- `crates/odin-core`: Rust target core. Owns typed Odin documents, ingest
|
|
ports, normalization, and CultMesh/CultCache repository boundaries. This is
|
|
the replacement spine; JavaScript remains legacy runtime scaffolding until
|
|
each organ has crossed the typed boundary.
|
|
- `src/odin-coordinator.cjs`: process lifecycle, serialized refresh loop,
|
|
persistence, health, and transport wiring. Refreshes must not overlap because
|
|
a refresh publishes Odin's daemon health.
|
|
- `src/odin/config.cjs`: runtime paths, refresh intervals, and CultLib module path setup.
|
|
- `src/odin/documents.cjs`: CultCache/CultMesh document definitions accepted by Odin.
|
|
- `src/odin/idunn-rudp.cjs`: daemon-owned Odin provider health publication to
|
|
Idunn over the canonical CultNet RUDP `schema` channel.
|
|
- `src/odin/probes.cjs`: local Docker/ADB debug lowerings for Starfire and
|
|
Android edge visibility. Remote Verse health is not probed here; it comes
|
|
from provider-owned CultMesh/CultNet records.
|
|
- `src/odin/interfaces.cjs`: provider advertisements, CultMesh interface
|
|
bindings, accepted interface projection, and renderer-lowering metadata.
|
|
Renderer routes are lowerings outside Odin, not Odin-hosted transport.
|
|
- `src/odin/layout.cjs`: `odin.interface_layout.v1` read/write and merge policy.
|
|
- `src/odin/marquee.cjs`: canonical marquee tape assembly from Stonks securities and ordered VoidBot poem lines.
|
|
- `src/odin/surface.cjs`: `gamecult.eve.surface.v1` tree projection.
|
|
- `src/odin/state.cjs`: one refresh's input records into Odin's provider catalog/proxy state.
|
|
The entrypoint is not allowed to grow new probe, surface, provider, layout, or renderer policy. If a new owner is needed, name the owner and its invariant before adding code.
|
|
|
|
The accepted document registry is also the persistence admission boundary.
|
|
Unknown schemas may be observed for diagnostics but are not silently persisted
|
|
as untyped truth. Heimdall access discovery therefore registers
|
|
`gamecult.eve.provider_advertisement.v1`,
|
|
`heimdall.command_boundary.v1`,
|
|
`gamecult.eve.plugin_advertisement.v1`, and
|
|
`heimdall.transport_profile.v1`. Their globally unique record keys prevent one
|
|
schema from overwriting another in the shared catalog. Odin returns the
|
|
redacted route metadata; it does not proxy private auth operations or own any
|
|
claim, completion, token, or app secret.
|
|
|
|
Gjallar is the Yggdrasil-resident aggregate compositor for what Odin can show.
|
|
Its runtime lives in `/srv/gjallar/current` and consumes Odin's accepted
|
|
provider-state snapshot. Gjallar owns enumeration, tiling, and publication of
|
|
the typed `gjallar.overview` Eve surface. Eve clients own graphical, terminal,
|
|
and framebuffer lowering. Gjallar must not own the underlying registry, probe,
|
|
provider truth, client pixels, or translation decisions.
|
|
|
|
Idunn is the named keepalive organ for daemon continuity. Its current Rust
|
|
body lives in `crates/idunn-daemon` and `crates/odin-core/src/idunn.rs`. Idunn may read
|
|
Odin-owned service records and provider advertisements, then bring daemons up
|
|
after reboots or crashes, watch health, emit keepalive observations, restart
|
|
requests, denied-action records, and operator alarms. When human action is
|
|
needed, Idunn uses CultMesh to request a Bifrost-owned operator
|
|
notification crossing. VoidBot's `voidbot.operator-dm` command `owner.dm.send`
|
|
is a demoted compatibility delivery actuator, not the owner. The target command
|
|
lives in Bifrost's Verse; any still-VoidBot delivery path must be invoked by
|
|
Bifrost or documented as migration debt. Idunn must not own Verse discovery,
|
|
schema truth, provider dashboards, identity grants, Discord delivery, owner-DM
|
|
delivery, or renderer layout. Keepalive loops belong in Idunn, not Odin's
|
|
coordinator or individual daemons.
|
|
|
|
Muninn is the portable local telemetry Verse assembler. Its Rust body lives in
|
|
the `GameCult/Muninn` repository and publishes `muninn.telemetry_surface.v1`
|
|
through CultMesh/CultCache. Muninn may run on Raven, Nightwing, Starfire, or any future
|
|
device body. It names locally accessible telemetry affordances: screen capture,
|
|
loopback audio, microphones, cameras, and future sensors. Muninn does not start
|
|
expensive capture streams merely because the daemon is alive. The default
|
|
`serve` posture publishes an idle typed surface; explicit activation, such as
|
|
`muninn activate` for Raven A/V over SRT, is the only path that starts FFmpeg,
|
|
WASAPI loopback, video capture, or similar resource-consuming workers.
|
|
|
|
Muninn owns local telemetry discovery and stream activation boundaries. It does
|
|
not own Mimir's normalized ingest ledger, OBS rendering, Gjallar composition,
|
|
Odin discovery truth, or Idunn keepalive policy. Active stream records such as
|
|
`muninn.capture_stream.v1` are evidence of requested streams, not permission for
|
|
startup to burn capture resources.
|
|
|
|
Move optical marker extraction belongs to Muninn because it is sensor stream
|
|
exposure, not Mimir fusion or Odin registry truth. The native helper lives at
|
|
`crates/muninn-move-tracker`; Muninn may publish per-frame candidates as
|
|
`muninn.move_marker_candidate.v1`. USB Move controller facts are
|
|
`muninn.move_controller_state.v1` receipts. Those records are not the hot
|
|
tracking transport: Muninn bundles marker candidates and controller states into
|
|
a CultMesh bytes stream frame with metadata schema
|
|
`mimir.muninn_move_evidence_stream_frame.v1`. Mimir consumes that stream into
|
|
tracking buffers and later fusion. Odin indexes the schema and projection
|
|
surface only.
|
|
|
|
Bifrost is the bridge for Persona speech and other public/owner-facing
|
|
crossings. When a Persona interpreter decides a Persona speaks, the accepted
|
|
side effect is a Bifrost CultMesh command or document that names actor,
|
|
authority, target surface, context, policy result, and receipt path. VoidBot
|
|
observes Discord, preserves room cognition, moderates, and may provide
|
|
compatibility delivery, but it is not the owner of swarm speech transport.
|
|
VoidBot's repo search, Discord history search, archive lookup, and source
|
|
retrieval are required native CultCache/CultMesh service surfaces. Any remaining
|
|
VoidBot-local or MCP-only implementation is migration debt. MCP is the bridge
|
|
for external agentic access, not the native path for GameCult agents that
|
|
already have CultMesh affordances.
|
|
|
|
## Target Mechanism
|
|
|
|
```text
|
|
Verse announcement
|
|
-> CultNet hello and schema catalog exchange
|
|
-> Odin registry
|
|
-> compatibility and translation index
|
|
-> subscriptions / worker routing / dashboard projection
|
|
```
|
|
|
|
## Invariants
|
|
|
|
- Odin owns the accepted registry of known Verses.
|
|
- A Verse owns its own schemas and authority model; Odin indexes and translates, it does not silently rewrite local truth.
|
|
- Device clients own sensor and media capture; Mimir owns the normalized ingest ledger; Odin owns the aggregate operator projection.
|
|
- Muninn advertises local telemetry affordances cheaply and starts capture only
|
|
after an explicit activation request.
|
|
- Muninn's live Move evidence is a CultMesh stream frame body; CultCache
|
|
Move records are receipts/debug state and must not become Mimir's hot
|
|
tracking path.
|
|
- Translation paths must name source schema, target schema, lossiness, authority, and version.
|
|
- Service presentation flows are CultMesh/Eve/CultUI interface projections. Odin aggregates those projection graphs; it does not replace them with nameplate summaries.
|
|
- Renderers lower surfaces only. If a renderer fixes network truth, the machine is split-brained.
|
|
- CultCache is the durable state substrate; CultNet is the wire vocabulary; CultMesh is the Verse and peer-consensus layer.
|
|
- The Eve surface carries explicit `verse` and `service` nodes plus provider-owned retained interface trees. Compact renderers may derive visual facets from those nodes, but may not invent observation truth.
|
|
- Rust organs must accept mocked inputs through narrow traits. Unit tests prove
|
|
local invariants; pipeline smokes prove adjacent typed handoff; full daemon
|
|
boots are not the only test path.
|
|
- JSON is not state authority. It is allowed only for schema publication,
|
|
debugging, compatibility export, or external xenos boundaries.
|
|
|
|
## Test Surfaces
|
|
|
|
Current Rust verification:
|
|
|
|
- `pipeline_collects_from_injected_ports`: proves ingest ports and clock injection.
|
|
- `memory_repository_supports_fast_unit_tests`: proves repository consumers can test without CultMesh.
|
|
- `cultmesh_repository_round_trips_typed_records`: proves typed Odin records
|
|
persist through CultMesh/CultCache and reload from `.cc`.
|
|
|
|
## Service Architecture Contract
|
|
|
|
Odin is the witness for the GameCult service contract:
|
|
|
|
```text
|
|
durable service state -> CultCache .cc
|
|
shared local visibility -> CultMesh
|
|
interactive presentation -> Eve GUI/TUI DSL
|
|
discovery and aggregation -> Odin
|
|
renderer bodies -> Eve clients, browser, compact TUI, native surfaces, overlays
|
|
```
|
|
|
|
When Odin sees a service, it should be able to answer:
|
|
|
|
- What Verse owns this service?
|
|
- Which typed schemas does it publish?
|
|
- Where is its durable `.cc` state or CultCache-compatible store?
|
|
- Which CultMesh documents or providers make it visible locally?
|
|
- Which Eve GUI/TUI surface represents its meaningful presentation and controls?
|
|
- Which command boundary accepts, denies, forwards, or reconciles user intent?
|
|
- Which fields are stale, predicted, denied, or authoritative?
|
|
|
|
This is not a reporting nicety. It is how Odin prevents services from becoming
|
|
private little islands with separate websites, dashboards, state formats, and
|
|
separate command languages.
|
|
|
|
## Current Service Surface
|
|
|
|
Odin currently publishes service squares for:
|
|
|
|
- Starfire: Odin, Docker, ADB, Odin CultCache, and running Docker containers.
|
|
- Nightwing: SSH, Eve broker, Eve browser reference, visible TUI, Docker unit state, and NVIDIA GPU state.
|
|
- EVE: SSH/native Eve reachability.
|
|
- Periwinkle: ADB reachability.
|
|
- Periwinkle: local ADB edge status plus any provider-owned surface or advertisement Odin receives through CultMesh/Odin discovery.
|
|
- Raven: SSH reachability.
|
|
- Yggdrasil: compatibility host reachability plus nginx,
|
|
StreamPixels, Heimdall, Repixelizer, and Bifrost systemd state until those
|
|
daemon surfaces publish health and command boundaries over CultNet RUDP.
|
|
|
|
Gjallar consumes Odin's accepted CultMesh/Eve state and composes every visible
|
|
provider surface into one typed aggregate on Yggdrasil. EveCanvas, browser, and
|
|
TUI clients lower that aggregate. If Odin starts deciding tile composition, the
|
|
composition owner has leaked upward. If providers tune themselves for one
|
|
client instead of emitting clean Eve/CultUI surfaces, provider truth has leaked
|
|
downward.
|
|
|
|
## Current Interface Surface
|
|
|
|
Odin discovers provider advertisements and interface bindings from daemon-owned
|
|
CultMesh/CultCache stores plus live CultNet/RUDP announcements. Providers own
|
|
their compositions; Odin embeds each provider's `surface.root` as an
|
|
`interface` child with provenance, version, status, source witness, and layout
|
|
metadata.
|
|
|
|
This is the model for future services: if a service publishes an operator interface, ingest the Eve/CultUI composition graph and lower it. Do not collapse it into a service-status tile unless the graph is unavailable and the tile is explicitly a temporary probe.
|
|
|
|
The expected provider output is Eve DSL or an equivalent
|
|
`gamecult.eve.surface.v1` retained tree. GUI and TUI are lowerings of the same
|
|
interactive language; they are not separate dashboard products. Huginn's `.cc`
|
|
inspection surface is the current clean example: Huginn inspects CultCache bytes
|
|
and emits Eve DSL, while Eve or any other runtime owns presentation.
|
|
|
|
Provider advertisements are the promotion path out of probing. Odin's CJS
|
|
document set already accepts `gamecult.eve.provider_advertisement.v1` alongside
|
|
`gamecult.eve.interface_binding.v1` and `gamecult.eve.surface_state.v1`.
|
|
Daemons should publish advertisements that name service id, Verse id, schema
|
|
catalog, `.cc` witnesses, Eve surface keys, command boundaries, nested Verses,
|
|
style capabilities, freshness, and redaction policy. Once an advertisement is
|
|
available, Odin should prefer it over LAN scans, hardcoded deck URLs, private
|
|
layout files, host probes, or web-dashboard scraping. Daemon health and
|
|
provider state should publish through `cultnet.transport.rudp.v0`;
|
|
product/debug routes are outside-Odin lowerings only and must not become
|
|
daemon-owned truth.
|
|
|
|
Provider advertisements should also publish semantic CultMesh addresses in this
|
|
shape:
|
|
|
|
```text
|
|
asgard.<machine>.<service>/<resource>
|
|
```
|
|
|
|
Examples:
|
|
|
|
```text
|
|
asgard.starfire.odin/eve/providers
|
|
asgard.starfire.bifrost/eve/tui
|
|
asgard.starfire.bifrost/eve/gui
|
|
asgard.yggdrasil.streampixels/eve/tui
|
|
asgard.yggdrasil.streampixels/eve/gui
|
|
```
|
|
|
|
The canonical service may omit the current machine when identity should survive
|
|
relocation, such as `asgard.bifrost`. Located service addresses name the current
|
|
host, such as `asgard.starfire.bifrost` now and
|
|
`asgard.yggdrasil.bifrost` after migration. CultNet routes are transport
|
|
metadata for resolving those names. Native daemon transport is CultNet over the
|
|
shared RUDP profile; renderer URLs belong outside Odin and are not service
|
|
identity.
|
|
|
|
The canonical contract lives in
|
|
`E:\Projects\Eve\docs\provider-advertisement-contract.md`.
|
|
|
|
Odin persists operator layout intent as `odin.interface_layout.v1` in the Odin CultMesh store under record key `odin.providers`. The ignored `scratch/odin/interface-layout.json` file is migration input only; Odin no longer writes layout truth back to that sidecar. Layout intents name the provider id and request focus, move, resize, or visibility changes; renderers are input devices for those intents, not local layout owners.
|
|
|
|
Odin now derives dense top-level layout intent from each provider's retained
|
|
`surface.root` tree: element count, leaf count, branch count, depth, text-cell
|
|
pressure, and list-like branches. Provider explicit preferred sizes are capped
|
|
at the Odin wrapper boundary unless the current intent is fullscreen, so stale
|
|
layout files cannot keep empty panels huge. The renderer should use
|
|
`props.tree`, `props.layout.signalWeight`, and `props.packing` to allocate space
|
|
to nested signal, then recursively lower provider children. Flattening a
|
|
provider surface into one log/list is a compatibility failure when the retained
|
|
tree has children.
|
|
|
|
## Observation Surface
|
|
|
|
Odin no longer tails Mimir JSONL artifacts for observation truth. Device and
|
|
media observations must arrive as provider-owned CultMesh/Odin records or
|
|
retained Eve/CultUI surfaces; Odin may project those accepted records, but it
|
|
does not scrape artifact logs to synthesize observation streams.
|
|
|
|
## First Translation Model
|
|
|
|
Odin's translation registry should start as data, not magic:
|
|
|
|
- `sourceSchema`
|
|
- `targetSchema`
|
|
- `translationKind`: `identity`, `projection`, `lossyProjection`, `adapter`, or `unsupported`
|
|
- `owner`
|
|
- `version`
|
|
- `notes`
|
|
|
|
No regex tribunals for meaning. Natural-language schema interpretation can be assisted by models later, but accepted translation routes must be inspectable typed state.
|