Quarantine Mimir's Eve operator surface: remove EveDashboard, EveBrowserReference and their contract documents

Parked at tag parked/Mimir-eve-surface per thing2:ruling:eve-dashboards-quarantined; rebuild on Thing 2. Mimir.EveSensorReceiver (camera frame ingest) stays. map.yaml no longer calls a live listener a tombstone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-22 20:14:47 +02:00
commit 43cebb6fb9
15 changed files with 19 additions and 4584 deletions

View file

@ -44,7 +44,6 @@
"authorities": {
"configurationCalibrationLaunchStatus": "Mimir",
"programComposition": "Mimir",
"operatorSurfaces": "Eve",
"sitePublication": "Yggdrasil publisher daemon",
"denseVisualFusion": "Fensalir",
"gpuMaterialAndBrushReconciliation": "Fensalir",
@ -97,10 +96,8 @@
]
},
"operator": {
"owner": "Mimir/Eve",
"owner": "Mimir",
"surfaces": [
"eve-gui-compositor",
"eve-tui-operator",
"cultmesh-command-surface"
]
},
@ -120,7 +117,6 @@
"native-reservoir",
"Fensalir",
"Faust",
"Eve",
"Yggdrasil publisher daemon"
],
"neverInCompatibilityEdges": [

View file

@ -29,7 +29,7 @@ Inputs become outputs like this:
- OBS can receive ordinary media-source URLs for compatibility; it does not need
a plugin for this bridge path.
- Audio remains separately mixable at the compatibility edge, but the durable
control surface belongs to Mimir/Eve.
control surface belongs to Mimir.
- Ports are stable and derived from one base port plus explicit offsets.
- Secrets, if used, stay in local config and are not committed.

View file

@ -16,7 +16,7 @@ Timestamp for this pass: 2026-05-23, starting at 23:24 Europe/London.
Mimir collects local and network audio/video samples into bounded rolling
runtime buffers, uses active/passive timing evidence to place audio streams on a
canonical timeline, and lets Mimir own program composition while Fensalir lowers
pixels, Faust/native DSP moves audio, Eve exposes operator surfaces, and
pixels, Faust/native DSP moves audio, and
Yggdrasil publishes the public feed.
## Current Mechanism
@ -61,8 +61,6 @@ reflection taps.
program video output.
- Faust/native DSP will own hot resampling, fractional delay, separation,
spatialization, and stems.
- Eve GUI/TUI lowerers render Mimir operator surfaces without owning scene
truth.
- The Yggdrasil-facing publisher daemon consumes Mimir program output without
owning composition.
- OBS is a compatibility sink only.
@ -91,7 +89,6 @@ flowchart TD
Probes["native/probes/*"] --> FrameEvents["MimirFrameEventProcessStreamSource"]
FrameEvents --> Sources
Runtime --> Scene["Mimir program scene graph"]
Scene --> Eve["Eve operator surfaces"]
Scene --> Publisher["Yggdrasil site publisher"]
Scene -. "compatibility" .-> Obs["OBS adapter"]
Reservoir["native/reservoir"] -. "future lower ABI" .-> Hub
@ -108,7 +105,7 @@ flowchart TD
- `native/probes/*` are measurement and diagnostic tools.
- `scripts/*` are bridge utilities, not the final synchronized program path.
- `docs/mimir-program-composition.md` is the authority map for Muninn capture
hosts, Mimir composition, Eve operation, Yggdrasil publication, and OBS
hosts, Mimir composition, Yggdrasil publication, and OBS
compatibility.
## Stage 0: Solution And App Host

View file

@ -365,14 +365,12 @@ a named invariant that the native runtime cannot protect yet.
- Windows bridge scripts for sender discovery/start/stop and simple OBS Media
Source compatibility ingest.
- `src/Mimir.Runtime/Synchronization` now contains the first typed program
composition contracts: `mimir.program_scene.v1`, `mimir.program_output.v1`,
and `mimir.eve_operator_surface.v1`. These make Mimir's scene graph,
Yggdrasil/site publication route, and Eve operator control/preview surface
composition contracts: `mimir.program_scene.v1` and `mimir.program_output.v1`.
These make Mimir's scene graph and Yggdrasil/site publication route
manifestable through CultMesh.
- `docs/mimir-program-composition.md` is the current authority map for the new
stream-program architecture: Muninn observes on Starfire/Nightwing/Raven-class
hosts, Mimir consumes selected streams and composes the program, Eve lowers
controls/previews/stats, Yggdrasil publishes to the site, and OBS is
hosts, Mimir consumes selected streams and composes the program, Yggdrasil publishes to the site, and OBS is
compatibility-only.
- `src/Mimir.CultMeshMedia` is the first explicit CultMesh media/body bridge:
`relay` hosts the document relay on CultNet RUDP `3075`, `send` reads an
@ -399,23 +397,13 @@ a named invariant that the native runtime cannot protect yet.
capture still defaults to FFmpeg desktop frames plus Mimir's WASAPI loopback
capture muxed as H.264/AAC MPEG-TS; DirectShow audio remains an explicit
fallback.
- `src/Mimir.EveDashboard` is archived until the dashboard returns as a pure
CultMesh/Odin publisher and Eve lowering. The old HTTP/WebSocket deck broker,
local health route, socket command channel, and provider route catalog are not
daemon transport. Dashboard state must be published as typed CultMesh/Eve
documents through Odin, and dashboard commands must arrive as typed Odin/
CultMesh command documents.
- `src/Mimir.EveBrowserReference` serves static browser lowerings and can
publish its own `idunn.daemon_health` record over
`cultnet.transport.rudp.v0`. It remains a renderer/reference surface, not
program authority.
- Documentation for OBS receiver setup, native rebuild boundaries, the viable
stream app, and the Mimir Face.
## Temporary
- Audio and video may still traverse separate OBS/SRT endpoints during bridge
testing, but Mimir/Eve owns independent controls.
testing, but Mimir owns independent controls.
- The CultMesh media bridge still lowers to local UDP for compatibility sinks
because OBS is not a CultMesh consumer. Network transit between Raven,
Yggdrasil, and Starfire is the Odin-discovered CultMesh/CultNet path;
@ -533,7 +521,7 @@ proxy impairment profiles and long soak remain the completion gate.
9. Implement the Mimir program scene graph as the shared commit primitive for
source subscription, transforms, crop, chroma key, visibility, layer order,
preview, and output publication. Import the current OBS scene only as an
initial mirror, then make Eve GUI/TUI the operator surface.
initial mirror; the operator surface returns on Thing 2.
10. Add the Yggdrasil-facing site publisher daemon that consumes the Mimir
program output and publishes it without owning a second composition.
11. Lower `AquariumBufferFieldFrame` spline tube fields into Fensalir compute:

View file

@ -49,7 +49,7 @@ distributed capture surfaces into one realtime coherent volumetric field. The
field is the product. The bridge scripts are scaffolding and probes.
The live target is one native five-second rolling reservoir feeding Mimir-owned
program composition, Fensalir GPU fusion, Faust/native DSP, Eve operator
program composition, Fensalir GPU fusion, Faust/native DSP, operator
surfaces, and a Yggdrasil site publisher. OBS is compatibility only. Retention
has one owner. Typed views are indexes, not private histories.
@ -70,7 +70,6 @@ Invariants:
program texture output.
- Faust/native DSP owns hot audio alignment, room suppression, voice separation,
stems, and spatial bed generation.
- Eve lowerers render operator controls and preview without owning scene truth.
- The Yggdrasil publisher daemon ships the Mimir program to the site without
owning composition.
- FFmpeg owns simple capture, encode, and transport in V1 bridge mode.

View file

@ -11,14 +11,14 @@ temporary compatibility sink only.
- Owner: Mimir owns the program scene graph and subscription policy.
- Inputs: Muninn-published local and remote monitor, window, camera, audio,
loopback, and health streams.
- Outputs: a Mimir program video surface, aligned audio stems or final bus, Eve
GUI/TUI operator surfaces, and a Yggdrasil-facing site publication feed.
- Outputs: a Mimir program video surface, aligned audio stems or final bus,
and a Yggdrasil-facing site publication feed.
- Derived state: OBS receives a compatibility feed when useful; it does not own
scene truth, broadcast truth, preview truth, stats, source selection, or
publication.
- Forbidden writers: OBS scenes, browser dashboards, site daemons, and Muninn
capture hosts must not independently decide the program composition.
- Shared paths: direct operator edits, remote Eve commands, preset loads,
- Shared paths: direct operator edits, remote commands, preset loads,
calibration-driven source selection, and recovery scripts must commit through
the same Mimir scene graph command surface.
- Deletion line: old OBS-owned composition docs and config names are demoted to
@ -39,7 +39,6 @@ flowchart TD
F --> I["Faust/native DSP"]
H --> J["program video surface"]
I --> K["program audio stems/bus"]
G --> L["Eve GUI/TUI operator surfaces"]
J --> M["Yggdrasil site publisher daemon"]
K --> M
M --> N["GameCult site"]
@ -68,22 +67,7 @@ The current OBS scene can be mirrored as an initial Mimir scene:
active.
After import, OBS is not the editor of record. It may display the Mimir program
feed, but operator edits happen through Mimir/Eve command surfaces.
## Eve Operator Surface
Mimir publishes Eve GUI/TUI documents for:
- live program preview;
- source catalog and subscription state;
- layer transform, crop, chroma key, visibility, and ordering;
- audio stem gain/mute/route controls;
- stream health, latency, buffer depth, timing confidence, and publication
status;
- emergency scene selection and publisher recovery commands.
Eve lowerers render those controls on any device without owning the underlying
scene graph.
feed, but operator edits happen through Mimir command surfaces.
## Site Publisher

View file

@ -28,7 +28,6 @@ flowchart TD
N --> F["Faust/native DSP"]
E --> G["Mimir program video"]
F --> H["program stems + spatial bed"]
E --> I["Eve GUI/TUI operator surfaces"]
G --> J["Yggdrasil site publisher"]
H --> J
G -. "compatibility" .-> K["OBS adapter"]
@ -53,17 +52,10 @@ Ownership:
Fensalir-facing wand pose stream.
- Mimir owns program composition, source subscription policy, preview/control
state, stats, and publication intent.
- Mimir's old Eve dashboard broker is archived; it does not publish health,
provider catalogs, or command surfaces while the socket path is cut. The
browser reference remains a client lowering, not daemon truth. Dashboard state
must return as typed CultMesh/Eve documents through Odin; product/debug render
surfaces are client lowerings and compatibility evidence, not daemon truth.
- Fensalir owns dense visual fusion, material/brush/splat reconciliation,
D3D12 interop, runtime UI lowering, and local program texture output.
- Faust/native DSP owns hot audio alignment, suppression, separation,
spatialization, and stem generation.
- Eve GUI/TUI lowerers render Mimir's operator surfaces on any device without
owning scene truth.
- The Yggdrasil-facing publisher daemon consumes Mimir program output and
publishes it to the site without owning composition.
- OBS is a temporary compatibility sink, not a composition or broadcast
@ -302,7 +294,7 @@ evidence.
authority map for stream-program output. Muninn runs on Starfire, Nightwing, and
Raven-class capture hosts to publish local stream capabilities and selected
media bodies. Mimir consumes only the streams it needs for calibration and
composition, commits one typed scene graph, emits Eve GUI/TUI operator surfaces,
composition, commits one typed scene graph,
and publishes program output locally plus through a Yggdrasil-facing site
publisher.
@ -439,14 +431,6 @@ an RUDP contract exists.
Mimir's current daemon-health publishers are local RUDP witnesses:
- `src/Mimir.EveDashboard` is a fail-closed archive tombstone. It no longer
starts a TCP listener, serves `/eve/deck`, accepts WebSocket commands, or
publishes daemon health. `mimir.eve_dashboard_state` must return through Odin/
CultMesh state publication before this service can be redeployed.
- `src/Mimir.EveBrowserReference` serves a static browser lowering and, when
configured with `--idunn-rudp-health` or
`MIMIR_EVE_BROWSER_REFERENCE_IDUNN_RUDP_HEALTH`, sends its own
`idunn.daemon_health` record over the same RUDP document path.
Invariant: health publication is not ownership. The daemon reports what it can
honestly observe about itself; Idunn decides keepalive action through the

View file

@ -1477,10 +1477,6 @@ static int RunPerfectMachineProfileSmoke()
"mimir-site-program",
scene.SceneId,
MimirProgramPublicationConfigurations.YggdrasilSiteProgram);
var eveSurface = MimirCultMeshContractFactory.CreateOperatorSurface(
"mimir-eve-gui-compositor",
scene.SceneId,
MimirProgramPublicationConfigurations.OperatorSurfaces[0]);
var observations = new[]
{
new MimirBioacousticWordObservation(0, MimirBioacousticTimeline.Default.EventForIndex(0).StartSeconds * MimirBioacousticTimeline.SampleRate + 317.375, 0.95),
@ -1565,7 +1561,7 @@ static int RunPerfectMachineProfileSmoke()
Console.WriteLine(
$"perfect-machine-actuator source={actuator.SourceId} delay={actuator.TargetDelaySamples:0.000} ratio={actuator.ResampleRatio:0.000000000} controls={actuator.FaustControls.Length}");
Console.WriteLine(
$"perfect-machine-program scene={scene.SceneId} layers={scene.Layers.Length} output={output.OutputId} route={output.PublisherRoute} eveCommands={eveSurface.CommandTopics.Length}");
$"perfect-machine-program scene={scene.SceneId} layers={scene.Layers.Length} output={output.OutputId} route={output.PublisherRoute}");
Console.WriteLine(
$"perfect-machine-clock anchors={clock?.AnchorCount ?? 0} coverage={clock?.AnchorCoverage ?? 0:0.000} offset={clock?.SourceOffsetSamples ?? double.NaN:0.000} confidence={clock?.Confidence ?? 0:0.000}");
Console.WriteLine(
@ -3927,11 +3923,6 @@ static async Task<int> RunPerfectMachineContractSmokeAsync(string outputPath)
scene.SceneId,
MimirProgramPublicationConfigurations.YggdrasilSiteProgram,
createdAt);
var eveSurface = MimirCultMeshContractFactory.CreateOperatorSurface(
"mimir-eve-gui-compositor",
scene.SceneId,
MimirProgramPublicationConfigurations.OperatorSurfaces[0],
createdAt);
var starfireMove = MimirTrackingObservation.PsMove(
"starfire:move:primary",
"psmove:starfire:00",
@ -4004,10 +3995,6 @@ static async Task<int> RunPerfectMachineContractSmokeAsync(string outputPath)
output,
new CultRecordHandle<MimirProgramOutputDocument>(new CultRecordKey($"mimir-program-output:{output.OutputId}")))
.ConfigureAwait(false);
await cache.UpsertAsync(
eveSurface,
new CultRecordHandle<MimirEveOperatorSurfaceDocument>(new CultRecordKey($"mimir-eve-surface:{eveSurface.SurfaceId}")))
.ConfigureAwait(false);
await cache.UpsertAsync(
starfireMove,
new CultRecordHandle<MimirTrackingObservation>(new CultRecordKey($"mimir-move-tracking:{starfireMove.ObservationId}")))
@ -4023,7 +4010,7 @@ static async Task<int> RunPerfectMachineContractSmokeAsync(string outputPath)
await cache.FlushAsync().ConfigureAwait(false);
Console.WriteLine(
$"perfect-machine-contract-smoke path={outputPath} codebookMotifs={codebook.Motifs.Length} decoder={decoder.Configuration.Id} pathBands={pathState.BandResponses.Length} actuatorControls={actuator.FaustControls.Length} sceneLayers={scene.Layers.Length} programOutput={output.OutputId} eveCommands={eveSurface.CommandTopics.Length} moveTracking=2 fusedMovePose={fusedMovePose.PoseId} authority={fusedMovePose.FusionAuthorityId} consumer={fusedMovePose.ConsumerContract} hosts={starfireMove.HostId},{nightwingMove.HostId}");
$"perfect-machine-contract-smoke path={outputPath} codebookMotifs={codebook.Motifs.Length} decoder={decoder.Configuration.Id} pathBands={pathState.BandResponses.Length} actuatorControls={actuator.FaustControls.Length} sceneLayers={scene.Layers.Length} programOutput={output.OutputId} moveTracking=2 fusedMovePose={fusedMovePose.PoseId} authority={fusedMovePose.FusionAuthorityId} consumer={fusedMovePose.ConsumerContract} hosts={starfireMove.HostId},{nightwingMove.HostId}");
return 0;
}
@ -4045,11 +4032,6 @@ static async Task<int> ImportObsProgramSceneAsync(string inputPath, string scene
scene.SceneId,
MimirProgramPublicationConfigurations.YggdrasilSiteProgram,
importedAt);
var eveSurface = MimirCultMeshContractFactory.CreateOperatorSurface(
"mimir-eve-gui-compositor",
scene.SceneId,
MimirProgramPublicationConfigurations.OperatorSurfaces[0],
importedAt);
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(outputPath)) ?? ".");
using var cache = await CultCacheMessagePack.OpenAsync(outputPath).ConfigureAwait(false);
@ -4061,10 +4043,6 @@ static async Task<int> ImportObsProgramSceneAsync(string inputPath, string scene
output,
new CultRecordHandle<MimirProgramOutputDocument>(new CultRecordKey($"mimir-program-output:{output.OutputId}")))
.ConfigureAwait(false);
await cache.UpsertAsync(
eveSurface,
new CultRecordHandle<MimirEveOperatorSurfaceDocument>(new CultRecordKey($"mimir-eve-surface:{eveSurface.SurfaceId}")))
.ConfigureAwait(false);
await cache.FlushAsync().ConfigureAwait(false);
var visible = scene.Layers.Count(layer => layer.Visible);

View file

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>Mimir.EveBrowserReference</AssemblyName>
<RootNamespace>Mimir.EveBrowserReference</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(CultLibRoot)\src\GameCult.Caching.MessagePack\GameCult.Caching.MessagePack.csproj" />
<ProjectReference Include="$(CultLibRoot)\src\GameCult.Networking\GameCult.Networking.csproj" />
</ItemGroup>
</Project>

View file

@ -1,734 +0,0 @@
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using GameCult.Caching;
using GameCult.Caching.MessagePack;
using GameCult.Networking;
using MessagePack;
var port = ParseInt(args, "--port", 8891);
var bind = ParseString(args, "--bind", Environment.GetEnvironmentVariable("MIMIR_EVE_BROWSER_REFERENCE_BIND") ?? "0.0.0.0");
var directory = ParseString(args, "--directory", Environment.GetEnvironmentVariable("MIMIR_EVE_BROWSER_REFERENCE_DIRECTORY") ?? Directory.GetCurrentDirectory());
var cultCacheWitnessPath = ParseString(
args,
"--cultcache-witness",
Environment.GetEnvironmentVariable("MIMIR_EVE_BROWSER_REFERENCE_CULTCACHE_WITNESS") ?? DefaultPaths.CultCacheWitnessPath);
var idunnRudpHealth = ParseString(args, "--idunn-rudp-health", Environment.GetEnvironmentVariable("MIMIR_EVE_BROWSER_REFERENCE_IDUNN_RUDP_HEALTH") ?? "");
var idunnDaemon = ParseString(args, "--idunn-daemon", Environment.GetEnvironmentVariable("MIMIR_EVE_BROWSER_REFERENCE_IDUNN_DAEMON") ?? "nightwing-eve-browser-reference");
var idunnHealthContract = ParseString(args, "--idunn-health-contract", Environment.GetEnvironmentVariable("MIMIR_EVE_BROWSER_REFERENCE_IDUNN_HEALTH_CONTRACT") ?? "nightwing.cultnet-rudp-browser-reference-health");
using var verseRuntime = EveBrowserReferenceVerseRuntime.Create(cultCacheWitnessPath);
using var server = new EveBrowserReferenceServer(
IPAddress.Parse(bind),
port,
Path.GetFullPath(directory),
verseRuntime,
new IdunnRudpHealthOptions(idunnRudpHealth, idunnDaemon, idunnHealthContract));
await server.RunAsync().ConfigureAwait(false);
static int ParseInt(IReadOnlyList<string> args, string name, int fallback)
{
for (var index = 0; index < args.Count - 1; index++)
{
if (string.Equals(args[index], name, StringComparison.OrdinalIgnoreCase) &&
int.TryParse(args[index + 1], CultureInfo.InvariantCulture, out var value))
{
return value;
}
}
return fallback;
}
static string ParseString(IReadOnlyList<string> args, string name, string fallback)
{
for (var index = 0; index < args.Count - 1; index++)
{
if (string.Equals(args[index], name, StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrWhiteSpace(args[index + 1]))
{
return args[index + 1];
}
}
return fallback;
}
internal static class DefaultPaths
{
public static string CultCacheWitnessPath =>
OperatingSystem.IsWindows()
? @"E:\Projects\Mimir\state\eve-browser-reference.service.cc"
: "/var/lib/gamecult/eve-browser-reference/cultcache/eve-browser-reference.service.cc";
}
internal sealed class EveBrowserReferenceServer(
IPAddress bindAddress,
int port,
string documentRoot,
EveBrowserReferenceVerseRuntime verseRuntime,
IdunnRudpHealthOptions idunnRudpHealth) : IDisposable
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
private readonly TcpListener listener = new(bindAddress, port);
private readonly CancellationTokenSource stopping = new();
private readonly object idunnRudpPublishLock = new();
private string idunnRudpPublishStatus = string.IsNullOrWhiteSpace(idunnRudpHealth.Endpoint) ? "disabled" : "idle";
private string idunnRudpPublishError = "";
private string idunnRudpPublishObservedAt = "";
private bool idunnRudpPublishInFlight;
private DateTimeOffset lastHealthPublishAttemptAt = DateTimeOffset.MinValue;
private DateTimeOffset lastWitnessPublishAttemptAt = DateTimeOffset.MinValue;
public async Task RunAsync()
{
if (!Directory.Exists(documentRoot))
{
throw new DirectoryNotFoundException($"Eve browser reference document root does not exist: {documentRoot}");
}
listener.Start();
Console.WriteLine($"Mimir Eve browser reference serving {documentRoot} on http://{bindAddress}:{port}/");
Console.WriteLine(string.IsNullOrWhiteSpace(idunnRudpHealth.Endpoint)
? "Mimir Eve browser reference Idunn RUDP health publisher disabled."
: $"Mimir Eve browser reference Idunn RUDP health publisher targeting {idunnRudpHealth.Endpoint} as {idunnRudpHealth.DaemonId}.");
Console.WriteLine($"Mimir Eve browser reference witness store publishing to {verseRuntime.WitnessPath}");
_ = Task.Run(PublishHealthLoopAsync);
while (!stopping.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync(stopping.Token).ConfigureAwait(false);
_ = Task.Run(() => HandleAsync(client));
}
}
private async Task PublishHealthLoopAsync()
{
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
while (!stopping.IsCancellationRequested && await timer.WaitForNextTickAsync(stopping.Token).ConfigureAwait(false))
{
var observedAt = DateTimeOffset.UtcNow;
PublishWitnessIfDue(observedAt);
QueueIdunnRudpHealthIfDue(observedAt);
}
}
private async Task HandleAsync(TcpClient tcpClient)
{
await using var stream = tcpClient.GetStream();
var request = await ReadHttpRequestAsync(stream).ConfigureAwait(false);
if (request.Path == "/health")
{
var health = JsonSerializer.Serialize(new
{
ok = true,
documentRoot,
port,
transport = "static-http-lowering",
cultCacheWitness = verseRuntime.GetWitnessStatus(),
idunnRudpHealth = GetIdunnRudpHealthStatus(),
}, JsonOptions);
await WriteHttpResponseAsync(stream, "200 OK", "application/json", Encoding.UTF8.GetBytes(health)).ConfigureAwait(false);
return;
}
var filePath = ResolveStaticFilePath(request.Path);
if (filePath == null)
{
await WriteHttpResponseAsync(stream, "404 Not Found", "text/plain", Encoding.UTF8.GetBytes("not found")).ConfigureAwait(false);
return;
}
var bytes = await File.ReadAllBytesAsync(filePath, stopping.Token).ConfigureAwait(false);
await WriteHttpResponseAsync(stream, "200 OK", ContentType(filePath), bytes).ConfigureAwait(false);
}
private IdunnRudpHealthStatus GetIdunnRudpHealthStatus()
{
lock (idunnRudpPublishLock)
{
return new IdunnRudpHealthStatus(
idunnRudpHealth.Endpoint,
idunnRudpHealth.DaemonId,
idunnRudpHealth.HealthContract,
idunnRudpPublishStatus,
idunnRudpPublishError,
idunnRudpPublishInFlight,
idunnRudpPublishObservedAt);
}
}
private void PublishWitnessIfDue(DateTimeOffset observedAt)
{
if (lastWitnessPublishAttemptAt != DateTimeOffset.MinValue &&
observedAt - lastWitnessPublishAttemptAt < TimeSpan.FromSeconds(5))
{
return;
}
lastWitnessPublishAttemptAt = observedAt;
verseRuntime.Publish(
CreateDaemonHealthRecord(observedAt),
documentRoot,
port,
observedAt);
}
private void QueueIdunnRudpHealthIfDue(DateTimeOffset observedAt)
{
if (string.IsNullOrWhiteSpace(idunnRudpHealth.Endpoint))
{
idunnRudpPublishStatus = "disabled";
return;
}
if (lastHealthPublishAttemptAt != DateTimeOffset.MinValue &&
observedAt - lastHealthPublishAttemptAt < TimeSpan.FromSeconds(5))
{
return;
}
var health = CreateDaemonHealthRecord(observedAt);
var observedAtText = health.ObservedAt;
lock (idunnRudpPublishLock)
{
if (idunnRudpPublishInFlight)
{
return;
}
idunnRudpPublishInFlight = true;
lastHealthPublishAttemptAt = observedAt;
idunnRudpPublishObservedAt = observedAtText;
if (!string.Equals(idunnRudpPublishStatus, "published", StringComparison.Ordinal))
{
idunnRudpPublishStatus = "publishing";
idunnRudpPublishError = "";
}
}
_ = Task.Run(() =>
{
try
{
IdunnRudpHealthPublisher.Publish(idunnRudpHealth.Endpoint, health);
lock (idunnRudpPublishLock)
{
idunnRudpPublishStatus = "published";
idunnRudpPublishError = "";
idunnRudpPublishInFlight = false;
}
}
catch (Exception error)
{
lock (idunnRudpPublishLock)
{
if (!string.Equals(idunnRudpPublishStatus, "published", StringComparison.Ordinal))
{
idunnRudpPublishStatus = "publish-error";
}
idunnRudpPublishError = error.Message;
idunnRudpPublishInFlight = false;
}
}
});
}
private IdunnDaemonHealthRecord CreateDaemonHealthRecord(DateTimeOffset observedAt)
{
var observedAtText = observedAt.ToString("O", CultureInfo.InvariantCulture);
return new IdunnDaemonHealthRecord
{
DaemonId = idunnRudpHealth.DaemonId,
State = "healthy",
Detail = $"Nightwing Eve browser reference serving static lowering; root={documentRoot}; port={port}; witness={verseRuntime.WitnessPath}",
ObservedAt = observedAtText,
HealthContract = idunnRudpHealth.HealthContract,
PublicationSource = "daemon-published",
Transport = "cultnet.transport.rudp.v0",
};
}
private string? ResolveStaticFilePath(string path)
{
var requestPath = path.Split('?', 2)[0];
if (requestPath == "/")
{
requestPath = "/index.html";
}
var relativePath = Uri.UnescapeDataString(requestPath.TrimStart('/'))
.Replace('/', Path.DirectorySeparatorChar);
var candidate = Path.GetFullPath(Path.Combine(documentRoot, relativePath));
if (!candidate.StartsWith(documentRoot.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.Ordinal) &&
!string.Equals(candidate, documentRoot, StringComparison.Ordinal))
{
return null;
}
return File.Exists(candidate) ? candidate : null;
}
private static string ContentType(string path) =>
Path.GetExtension(path).ToLowerInvariant() switch
{
".html" => "text/html; charset=utf-8",
".css" => "text/css; charset=utf-8",
".js" => "application/javascript; charset=utf-8",
".json" => "application/json; charset=utf-8",
".png" => "image/png",
".jpg" or ".jpeg" => "image/jpeg",
".svg" => "image/svg+xml",
_ => "application/octet-stream",
};
private static async Task<HttpRequest> ReadHttpRequestAsync(NetworkStream stream)
{
var buffer = new byte[8192];
var length = await stream.ReadAsync(buffer).ConfigureAwait(false);
if (length == 0)
{
return new HttpRequest("/");
}
var text = Encoding.ASCII.GetString(buffer, 0, length);
var firstLineEnd = text.IndexOf("\r\n", StringComparison.Ordinal);
var firstLine = firstLineEnd >= 0 ? text[..firstLineEnd] : text;
var parts = firstLine.Split(' ', StringSplitOptions.RemoveEmptyEntries);
return new HttpRequest(parts.Length >= 2 ? parts[1] : "/");
}
private static async Task WriteHttpResponseAsync(NetworkStream stream, string status, string contentType, byte[] body)
{
var header = Encoding.ASCII.GetBytes(
$"HTTP/1.1 {status}\r\nContent-Type: {contentType}\r\nContent-Length: {body.Length}\r\nConnection: close\r\n\r\n");
await stream.WriteAsync(header).ConfigureAwait(false);
await stream.WriteAsync(body).ConfigureAwait(false);
}
public void Dispose()
{
stopping.Cancel();
listener.Stop();
stopping.Dispose();
}
}
internal sealed record HttpRequest(string Path);
internal sealed record IdunnRudpHealthOptions(string Endpoint, string DaemonId, string HealthContract);
internal sealed record CultCacheWitnessStatus(
string Path,
string Status,
string Error,
string ObservedAtUtc,
IReadOnlyList<string> PublishedSchemas);
internal sealed record IdunnRudpHealthStatus(
string Endpoint,
string Daemon,
string Contract,
string Status,
string Error,
bool InFlight,
string ObservedAtUtc);
[CultDocument("idunn.daemon_health", "idunn.daemon_health.v1")]
[MessagePackObject(AllowPrivate = true)]
internal sealed class IdunnDaemonHealthRecord
{
[Key(0)] public string DaemonId { get; set; } = "nightwing-eve-browser-reference";
[Key(1)] public string State { get; set; } = "active";
[Key(2)] public string Detail { get; set; } = "";
[Key(3)] public string ObservedAt { get; set; } = "";
[Key(4)] public string HealthContract { get; set; } = "nightwing.cultnet-rudp-browser-reference-health";
[Key(5)] public string PublicationSource { get; set; } = "daemon-published";
[Key(6)] public string Transport { get; set; } = "cultnet.transport.rudp.v0";
}
internal sealed class EveBrowserReferenceVerseRuntime : IDisposable
{
private static readonly CultRecordKey ManifestKey = new("nightwing-eve-browser-reference");
private static readonly CultRecordKey StaticSurfaceKey = new("nightwing-eve-browser-reference");
private static readonly CultRecordKey CommandBoundaryKey = new("nightwing-eve-browser-reference");
private static readonly CultRecordKey TransportProfileKey = new("nightwing-eve-browser-reference");
private static readonly string[] PublishedSchemas =
[
"mimir.eve_browser_reference_manifest.v1",
"mimir.eve_browser_reference_static_surface.v1",
"mimir.eve_browser_reference_command_boundary.v1",
"mimir.eve_browser_reference_transport_profile.v1",
"idunn.daemon_health.v1",
];
private readonly CultDocumentRegistry registry = new();
private readonly string witnessPath;
private readonly object publishLock = new();
private string witnessStatus = "starting";
private string witnessError = string.Empty;
private string witnessObservedAtUtc = string.Empty;
private EveBrowserReferenceVerseRuntime(string witnessPath)
{
this.witnessPath = Path.GetFullPath(witnessPath);
}
public string WitnessPath => witnessPath;
public static EveBrowserReferenceVerseRuntime Create(string witnessPath)
{
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(witnessPath)) ?? ".");
return new EveBrowserReferenceVerseRuntime(witnessPath);
}
public CultCacheWitnessStatus GetWitnessStatus()
{
lock (publishLock)
{
return new CultCacheWitnessStatus(
witnessPath,
witnessStatus,
witnessError,
witnessObservedAtUtc,
PublishedSchemas);
}
}
public void Publish(
IdunnDaemonHealthRecord healthRecord,
string documentRoot,
int port,
DateTimeOffset observedAt)
{
var observedAtUtc = observedAt.ToString("O", CultureInfo.InvariantCulture);
try
{
lock (publishLock)
{
var records = new List<CultPersistedRecord>();
var schemas = new Dictionary<string, CultSchemaCatalogEntry>(StringComparer.Ordinal);
var assets = EnumerateRelativeFiles(documentRoot);
var fixtures = assets
.Where(path => path.StartsWith("fixtures/", StringComparison.Ordinal))
.ToArray();
AddRecord(records, schemas, ToManifestDocument(documentRoot, port, assets), ManifestKey, observedAtUtc);
AddRecord(records, schemas, ToStaticSurfaceDocument(documentRoot, assets, fixtures, observedAtUtc), StaticSurfaceKey, observedAtUtc);
AddRecord(records, schemas, ToCommandBoundaryDocument(observedAtUtc), CommandBoundaryKey, observedAtUtc);
AddRecord(records, schemas, ToTransportProfileDocument(observedAtUtc), TransportProfileKey, observedAtUtc);
AddRecord(records, schemas, healthRecord, new CultRecordKey(healthRecord.DaemonId), observedAtUtc);
WriteSnapshot(new CultPersistedStoreSnapshot
{
FormatVersion = "cultcache.store.v1",
SchemaCatalog = schemas.Values.OrderBy(entry => entry.SchemaName, StringComparer.Ordinal).ToArray(),
Records = records.OrderBy(entry => entry.Key, StringComparer.Ordinal).ToArray(),
});
witnessStatus = "published";
witnessError = string.Empty;
witnessObservedAtUtc = observedAtUtc;
}
}
catch (Exception error)
{
lock (publishLock)
{
witnessStatus = "publish-error";
witnessError = error.Message;
witnessObservedAtUtc = observedAtUtc;
}
}
}
public void Dispose()
{
}
private void AddRecord<T>(
List<CultPersistedRecord> records,
Dictionary<string, CultSchemaCatalogEntry> schemas,
T document,
CultRecordKey key,
string observedAtUtc) where T : class
{
var descriptor = registry.GetRequired<T>();
var payload = MessagePackSerializer.Serialize(typeof(T), document, CultNetSchemaMessageSerialization.Options);
schemas[descriptor.SchemaId] = descriptor.ToCatalogEntry();
records.Add(new CultPersistedRecord
{
Key = key.Value,
SchemaId = descriptor.SchemaId,
StoredAt = observedAtUtc,
Payload = payload,
});
}
private void WriteSnapshot(CultPersistedStoreSnapshot snapshot)
{
var payload = CultDocumentMessagePackSerialization.SerializeSnapshot(snapshot);
var directory = Path.GetDirectoryName(witnessPath) ?? ".";
Directory.CreateDirectory(directory);
var tempPath = Path.Combine(directory, $".{Path.GetFileName(witnessPath)}.{Guid.NewGuid():N}.tmp");
try
{
using (var stream = new FileStream(
tempPath,
FileMode.CreateNew,
FileAccess.Write,
FileShare.None,
bufferSize: 4096,
FileOptions.WriteThrough))
{
stream.Write(payload, 0, payload.Length);
stream.Flush(true);
}
if (File.Exists(witnessPath))
{
File.Replace(tempPath, witnessPath, null);
}
else
{
File.Move(tempPath, witnessPath);
}
}
finally
{
if (File.Exists(tempPath))
{
File.Delete(tempPath);
}
}
}
private static EveBrowserReferenceManifestRecord ToManifestDocument(
string documentRoot,
int port,
IReadOnlyList<string> assets) =>
new()
{
DaemonId = "nightwing-eve-browser-reference",
SiteTitle = "Nightwing Eve Browser Reference",
DocumentRoot = documentRoot,
EntryPoint = "index.html",
Port = port,
Assets = assets.ToArray(),
SurfaceTransport = "static-http-lowering",
};
private static EveBrowserReferenceStaticSurfaceRecord ToStaticSurfaceDocument(
string documentRoot,
IReadOnlyList<string> assets,
IReadOnlyList<string> fixtures,
string observedAtUtc) =>
new()
{
DaemonId = "nightwing-eve-browser-reference",
UpdatedAtUtc = observedAtUtc,
DocumentRoot = documentRoot,
EntryPoint = "index.html",
Assets = assets.ToArray(),
FixtureDocuments = fixtures.ToArray(),
CurrentCutLine = "Static browser lowering assets are published into the daemon-owned witness store so Nightwing and Odin can reason about the lowering body without treating the HTTP service probe as lifecycle truth.",
};
private static EveBrowserReferenceCommandBoundaryRecord ToCommandBoundaryDocument(string observedAtUtc) =>
new()
{
DaemonId = "nightwing-eve-browser-reference",
UpdatedAtUtc = observedAtUtc,
Mode = "static-read-only-lowering",
WritesAccepted = false,
OperatorInputAuthority = "served browser assets only; no write path is accepted by this daemon",
LifecycleAuthority = "idunn.local-command.restart + compatibility.systemd.nightwing-eve-browser-reference.service",
AcceptedCommands = [],
RejectedCommands =
[
"surface-mutation",
"provider-catalog-ownership",
"daemon-health-override",
"service-restart",
],
};
private EveBrowserReferenceTransportProfileRecord ToTransportProfileDocument(string observedAtUtc) =>
new()
{
DaemonId = "nightwing-eve-browser-reference",
UpdatedAtUtc = observedAtUtc,
CurrentState = "partial-rudp-health-and-provider-store-live",
InputTransport = "compatibility.http-static-lowering",
OutputTransport = "daemon-owned-cultcache-boundary-store + daemon-published-rudp-health",
HealthContract = "nightwing.cultnet-rudp-browser-reference-health",
IdunnRudpHealth = "idunn.health-published-separately",
WitnessPath = witnessPath,
CurrentCutLine = "The Nightwing Eve browser reference runtime now owns its manifest, static surface witness, command boundary, and transport profile in a daemon-owned CultCache store; systemd and HTTP remain compatibility witnesses while projections learn to consume the typed store directly.",
};
private static string[] EnumerateRelativeFiles(string root)
{
if (!Directory.Exists(root))
{
return [];
}
return Directory
.EnumerateFiles(root, "*", SearchOption.AllDirectories)
.Select(path => Path.GetRelativePath(root, path).Replace('\\', '/'))
.OrderBy(path => path, StringComparer.Ordinal)
.ToArray();
}
}
[CultDocument("mimir.eve_browser_reference_manifest", "mimir.eve_browser_reference_manifest.v1")]
[MessagePackObject(AllowPrivate = true)]
internal sealed class EveBrowserReferenceManifestRecord
{
[Key(0)]
[CultName]
public string DaemonId { get; set; } = string.Empty;
[Key(1)] public string SiteTitle { get; set; } = string.Empty;
[Key(2)] public string DocumentRoot { get; set; } = string.Empty;
[Key(3)] public string EntryPoint { get; set; } = string.Empty;
[Key(4)] public int Port { get; set; }
[Key(5)] public string[] Assets { get; set; } = [];
[Key(6)] public string SurfaceTransport { get; set; } = string.Empty;
}
[CultDocument("mimir.eve_browser_reference_static_surface", "mimir.eve_browser_reference_static_surface.v1")]
[MessagePackObject(AllowPrivate = true)]
internal sealed class EveBrowserReferenceStaticSurfaceRecord
{
[Key(0)]
[CultName]
public string DaemonId { get; set; } = string.Empty;
[Key(1)] public string UpdatedAtUtc { get; set; } = string.Empty;
[Key(2)] public string DocumentRoot { get; set; } = string.Empty;
[Key(3)] public string EntryPoint { get; set; } = string.Empty;
[Key(4)] public string[] Assets { get; set; } = [];
[Key(5)] public string[] FixtureDocuments { get; set; } = [];
[Key(6)] public string CurrentCutLine { get; set; } = string.Empty;
}
[CultDocument("mimir.eve_browser_reference_command_boundary", "mimir.eve_browser_reference_command_boundary.v1")]
[MessagePackObject(AllowPrivate = true)]
internal sealed class EveBrowserReferenceCommandBoundaryRecord
{
[Key(0)]
[CultName]
public string DaemonId { get; set; } = string.Empty;
[Key(1)] public string UpdatedAtUtc { get; set; } = string.Empty;
[Key(2)] public string Mode { get; set; } = string.Empty;
[Key(3)] public bool WritesAccepted { get; set; }
[Key(4)] public string OperatorInputAuthority { get; set; } = string.Empty;
[Key(5)] public string LifecycleAuthority { get; set; } = string.Empty;
[Key(6)] public string[] AcceptedCommands { get; set; } = [];
[Key(7)] public string[] RejectedCommands { get; set; } = [];
}
[CultDocument("mimir.eve_browser_reference_transport_profile", "mimir.eve_browser_reference_transport_profile.v1")]
[MessagePackObject(AllowPrivate = true)]
internal sealed class EveBrowserReferenceTransportProfileRecord
{
[Key(0)]
[CultName]
public string DaemonId { get; set; } = string.Empty;
[Key(1)] public string UpdatedAtUtc { get; set; } = string.Empty;
[Key(2)] public string CurrentState { get; set; } = string.Empty;
[Key(3)] public string InputTransport { get; set; } = string.Empty;
[Key(4)] public string OutputTransport { get; set; } = string.Empty;
[Key(5)] public string HealthContract { get; set; } = string.Empty;
[Key(6)] public string IdunnRudpHealth { get; set; } = string.Empty;
[Key(7)] public string WitnessPath { get; set; } = string.Empty;
[Key(8)] public string CurrentCutLine { get; set; } = string.Empty;
}
internal static class IdunnRudpHealthPublisher
{
private const uint ConnectionId = 0x1d0d0001;
public static void Publish(string endpoint, IdunnDaemonHealthRecord health)
{
var remote = ParseEndpoint(endpoint);
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(new IPEndPoint(IPAddress.Any, 0));
socket.Blocking = false;
socket.ReceiveTimeout = 100;
socket.SendTimeout = 1000;
using var transport = new CultNetRudpSocketTransportConnection(new CultNetRudpSocketTransportOptions
{
RuntimeId = "mimir-eve-browser-reference",
Socket = socket,
Mode = CultNetRudpSocketMode.Client,
RemoteEndPoint = remote,
ConnectionId = ConnectionId,
InitialSequence = 1,
ResendDelayMs = 100,
});
if (!transport.ConnectAndWait(Array.Empty<byte>(), TimeSpan.FromSeconds(5), TimeSpan.FromMilliseconds(20)))
{
throw new TimeoutException($"timed out connecting Mimir Eve browser reference RUDP health publisher to {endpoint}");
}
var observedAt = string.IsNullOrWhiteSpace(health.ObservedAt)
? DateTimeOffset.UtcNow.ToString("O", CultureInfo.InvariantCulture)
: health.ObservedAt;
health.ObservedAt = observedAt;
var message = new CultNetDocumentPutRawMessage
{
MessageId = $"mimir-eve-browser-reference-health:{health.DaemonId}:{observedAt.Replace(':', '-')}",
Document = new CultNetRawDocumentRecord
{
SchemaId = "idunn.daemon_health",
RecordKey = health.DaemonId,
StoredAt = observedAt,
PayloadEncoding = "messagepack",
Payload = MessagePackSerializer.Serialize(health, CultNetSchemaMessageSerialization.Options),
SourceRuntimeId = "mimir-eve-browser-reference",
SourceRole = "daemon-health-publisher",
Tags = ["cultnet.transport.rudp.v0"],
},
};
transport.SendSchema(CultNetSchemaMessageSerialization.Serialize(message));
}
private static IPEndPoint ParseEndpoint(string value)
{
var trimmed = value.Trim();
var separator = trimmed.LastIndexOf(':');
if (separator <= 0 || separator == trimmed.Length - 1)
{
throw new ArgumentException($"Idunn RUDP endpoint must be host:port, got '{value}'.", nameof(value));
}
var host = trimmed[..separator];
var portText = trimmed[(separator + 1)..];
if (!int.TryParse(portText, CultureInfo.InvariantCulture, out var port))
{
throw new ArgumentException($"Idunn RUDP endpoint port must be numeric, got '{value}'.", nameof(value));
}
var addresses = Dns.GetHostAddresses(host);
var address = addresses.FirstOrDefault(static candidate => candidate.AddressFamily == AddressFamily.InterNetwork)
?? throw new ArgumentException($"Idunn RUDP endpoint host did not resolve to IPv4, got '{value}'.", nameof(value));
return new IPEndPoint(address, port);
}
}

View file

@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>Mimir.EveDashboard</AssemblyName>
<RootNamespace>Mimir.EveDashboard</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(CultLibRoot)\src\GameCult.Caching.MessagePack\GameCult.Caching.MessagePack.csproj" />
<ProjectReference Include="$(CultLibRoot)\src\GameCult.Mesh\GameCult.Mesh.csproj" />
<ProjectReference Include="$(CultLibRoot)\src\GameCult.Networking\GameCult.Networking.csproj" />
<ProjectReference Include="..\Mimir.Runtime\Mimir.Runtime.csproj" />
</ItemGroup>
</Project>

File diff suppressed because it is too large Load diff

View file

@ -157,153 +157,6 @@ public sealed record MimirProgramOutputDocument(
[property: Key(5)] string PublisherRoute,
[property: Key(6)] bool DiagnosticOnly);
[CultDocument("mimir.eve_operator_surface", "mimir.eve_operator_surface.v1")]
[MessagePackObject]
public sealed record MimirEveOperatorSurfaceDocument(
[property: Key(0)]
[property: CultName]
string SurfaceId,
[property: Key(1)] string UpdatedAtUtc,
[property: Key(2)] string SceneId,
[property: Key(3)] bool CanPreviewProgram,
[property: Key(4)] bool CanEditComposition,
[property: Key(5)] bool CanSelectSources,
[property: Key(6)] bool CanShowStats,
[property: Key(7)] string[] CommandTopics);
[CultDocument("mimir.eve_dashboard_manifest", "mimir.eve_dashboard_manifest.v1")]
[MessagePackObject]
public sealed record MimirEveDashboardManifestDocument(
[property: Key(0)]
[property: CultName]
string ProviderId,
[property: Key(1)] string Title,
[property: Key(2)] string Description,
[property: Key(3)] string Version,
[property: Key(4)] string Endpoint,
[property: Key(5)] string[] Capabilities,
[property: Key(6)] bool UsesCultMesh,
[property: Key(7)] string Transport);
[CultDocument("mimir.eve_dashboard_state", "mimir.eve_dashboard_state.v1")]
[MessagePackObject]
public sealed record MimirEveDashboardStateDocument(
[property: Key(0)]
[property: CultName]
string ProviderId,
[property: Key(1)] string Title,
[property: Key(2)] long Version,
[property: Key(3)] string UpdatedAtUtc,
[property: Key(4)] string SelectedNodeId,
[property: Key(5)] string LutPreset,
[property: Key(6)] MimirEveDashboardNodeSnapshot[] Nodes,
[property: Key(7)] MimirEveDashboardSurfaceSnapshot? Surface);
[MessagePackObject]
public sealed record MimirEveDashboardNodeSnapshot(
[property: Key(0)] string Id,
[property: Key(1)] string Label,
[property: Key(2)] string Kind,
[property: Key(3)] bool Visible,
[property: Key(4)] double X,
[property: Key(5)] double Y,
[property: Key(6)] double Z,
[property: Key(7)] double Rotation,
[property: Key(8)] double Scale,
[property: Key(9)] double Width,
[property: Key(10)] double Height,
[property: Key(11)] string Health,
[property: Key(12)] string? ProviderId,
[property: Key(13)] string? Command,
[property: Key(14)] string? Endpoint);
[MessagePackObject]
public sealed record MimirEveDashboardSurfaceSnapshot(
[property: Key(0)] string Schema,
[property: Key(1)] string Id,
[property: Key(2)] string Title,
[property: Key(3)] MimirEveDashboardUiElementSnapshot Root,
[property: Key(4)] MimirEveDashboardSurfaceAssetSnapshot[] Assets);
[MessagePackObject]
public sealed record MimirEveDashboardSurfaceAssetSnapshot(
[property: Key(0)] string Id,
[property: Key(1)] string Kind,
[property: Key(2)] string Uri);
[MessagePackObject]
public sealed record MimirEveDashboardUiElementSnapshot(
[property: Key(0)] string Id,
[property: Key(1)] string Kind,
[property: Key(2)] string? Role,
[property: Key(3)] string? Text,
[property: Key(4)] string? AssetRef,
[property: Key(5)] string? AssetUri,
[property: Key(6)] string? BindNodeId,
[property: Key(7)] string? CommandId,
[property: Key(8)] MimirEveDashboardUiLayoutSnapshot? Layout,
[property: Key(9)] MimirEveDashboardUiStyleSnapshot? Style,
[property: Key(10)] MimirEveDashboardUiMetricSnapshot? Metric,
[property: Key(11)] MimirEveDashboardUiElementSnapshot[] Children,
[property: Key(12)] MimirEveDashboardUiBindingSnapshot? Binding = null);
[MessagePackObject]
public sealed record MimirEveDashboardUiBindingSnapshot(
[property: Key(0)] string DocumentSchema,
[property: Key(1)] string DocumentId,
[property: Key(2)] string Path,
[property: Key(3)] string ValueKind,
[property: Key(4)] string Access,
[property: Key(5)] string Authority,
[property: Key(6)] string? CommandId);
[MessagePackObject]
public sealed record MimirEveDashboardUiLayoutSnapshot(
[property: Key(0)] string Direction,
[property: Key(1)] double? Width,
[property: Key(2)] double? Height,
[property: Key(3)] double? Grow,
[property: Key(4)] double? Gap,
[property: Key(5)] double? Padding,
[property: Key(6)] string? Overflow,
[property: Key(7)] double? MinWidth = null,
[property: Key(8)] double? MinHeight = null,
[property: Key(9)] double? PreferredWidth = null,
[property: Key(10)] double? PreferredHeight = null,
[property: Key(11)] double? Priority = null,
[property: Key(12)] string? Density = null,
[property: Key(13)] string? ViewportMode = null);
[MessagePackObject]
public sealed record MimirEveDashboardUiStyleSnapshot(
[property: Key(0)] string Variant,
[property: Key(1)] string? Tone);
[MessagePackObject]
public sealed record MimirEveDashboardUiMetricSnapshot(
[property: Key(0)] string Label,
[property: Key(1)] double Value,
[property: Key(2)] string Tone);
[CultDocument("mimir.eve_dashboard_command", "mimir.eve_dashboard_command.v1")]
[MessagePackObject]
public sealed record MimirEveDashboardCommandDocument(
[property: Key(0)]
[property: CultName]
string CommandId,
[property: Key(1)] string DeviceId,
[property: Key(2)] string ClientId,
[property: Key(3)] string ProviderId,
[property: Key(4)] string Type,
[property: Key(5)] string NodeId,
[property: Key(6)] double? X,
[property: Key(7)] double? Y,
[property: Key(8)] double? Rotation,
[property: Key(9)] double? Scale,
[property: Key(10)] bool? Visible,
[property: Key(11)] long Sequence,
[property: Key(12)] long DeviceTimestampNs);
[CultDocument("mimir.eve_sensor_observation", "mimir.eve_sensor_observation.v1")]
[MessagePackObject]
public sealed record MimirEveSensorObservationDocument(
@ -482,29 +335,6 @@ public static class MimirCultMeshContractFactory
publication.SitePublisherRoute,
publication.DiagnosticOnly);
public static MimirEveOperatorSurfaceDocument CreateOperatorSurface(
string surfaceId,
string sceneId,
MimirProgramControlSurface surface,
DateTimeOffset? updatedAt = null) =>
new(
surfaceId,
(updatedAt ?? DateTimeOffset.UtcNow).ToString("O"),
sceneId,
surface.CanPreviewProgram,
surface.CanEditComposition,
surface.CanSelectSources,
surface.CanShowStats,
CommandTopics:
[
"mimir.program.scene.select",
"mimir.program.layer.transform",
"mimir.program.layer.crop",
"mimir.program.layer.chroma_key",
"mimir.program.source.subscribe",
"mimir.program.output.publish"
]);
private static MimirBioacousticDecoderConfigurationSnapshot Snapshot(MimirBioacousticDecoderConfiguration configuration) =>
new(
configuration.Id,

View file

@ -20,8 +20,6 @@ public enum MimirProgramAudioPublicationKind
public enum MimirProgramControlPublicationKind
{
EveGui,
EveTui,
CultMeshCommandSurface
}
@ -70,25 +68,9 @@ public static class MimirProgramPublicationConfigurations
public static IReadOnlyList<MimirProgramControlSurface> OperatorSurfaces { get; } =
[
new(
"eve-gui-compositor",
"Primary Eve GUI composition surface: preview, source selection, transforms, crop/key controls, audio stem controls, and stream health.",
MimirProgramControlPublicationKind.EveGui,
CanPreviewProgram: true,
CanEditComposition: true,
CanSelectSources: true,
CanShowStats: true),
new(
"eve-tui-operator",
"Compact Eve TUI operator surface for source health, live stats, scene selection, and emergency command paths.",
MimirProgramControlPublicationKind.EveTui,
CanPreviewProgram: false,
CanEditComposition: false,
CanSelectSources: true,
CanShowStats: true),
new(
"cultmesh-command-surface",
"Typed CultMesh command surface consumed by Eve lowerers and trusted devices; Mimir remains the scene authority.",
"Typed CultMesh command surface consumed by trusted devices; Mimir remains the scene authority.",
MimirProgramControlPublicationKind.CultMeshCommandSurface,
CanPreviewProgram: false,
CanEditComposition: true,

View file

@ -652,16 +652,6 @@ accepted_design:
receiver. The old `CultMesh.StartNodeAsync`/`CultMesh.ConnectClient`
node/client path and raw sender/receiver host-port config are no longer
transport authorities for this media lane.
- `src/Mimir.EveDashboard` is archived as a fail-closed tombstone. It no
longer opens a TCP listener, serves `/eve/deck`, accepts WebSocket commands,
or publishes Idunn health. Its replacement must publish provider
advertisement, retained state, command boundary, and transport profile as
typed CultMesh/Odin documents; product/debug render surfaces remain client
lowerings.
- `src/Mimir.EveBrowserReference` serves a static Eve/browser lowering and
can publish its own `idunn.daemon_health` record over
`cultnet.transport.rudp.v0`. It is a renderer/reference health witness, not
an authority over Mimir program state or Idunn keepalive decisions.
- `research/perfect-machine-study-2026-05-23/` preserves the architecture
rumination, optimization ledger, research references, source/hotspot audits,
Fensalir/native boundary maps, benchmark plan, calibration-session spec,
@ -690,13 +680,7 @@ active_subgoals:
replaces them.
- Launch Raven's CultMesh sender directly on Raven because SSH to Raven over
WireGuard timed out during the initial relay deployment pass.
- Keep Mimir Eve dashboard and browser reference aligned with Odin/Idunn
doctrine: Odin accepts Verse/service visibility, Idunn owns keepalive, and
Mimir surfaces publish typed CultMesh/Odin state rather than private
watchdog loops, product status probes, or socket brokers.
- Rebuild `src/Mimir.EveDashboard` only after daemon truth, provider catalogs,
command boundaries, and typed program document paths use explicit CultMesh/
Odin documents. `Mimir.EveSensorReceiver`, `Mimir.VerseRecorder`, the
- `Mimir.EveSensorReceiver`, `Mimir.VerseRecorder`, the
Nightwing typed witness publisher, and `start-nightwing-move-tracking.ps1`
are archived so the old witness subscription lane cannot retake transport
authority. `src/Mimir.CultMeshMedia` has completed this cut for the