diff --git a/Aetheria.State.Daemon/Aetheria.State.Daemon.csproj b/Aetheria.State.Daemon/Aetheria.State.Daemon.csproj
new file mode 100644
index 000000000..ca788873d
--- /dev/null
+++ b/Aetheria.State.Daemon/Aetheria.State.Daemon.csproj
@@ -0,0 +1,13 @@
+
+
+ Exe
+ net10.0
+ enable
+ latest
+ enable
+
+
+
+
+
+
diff --git a/Aetheria.State.Daemon/Program.cs b/Aetheria.State.Daemon/Program.cs
new file mode 100644
index 000000000..7185e7c4e
--- /dev/null
+++ b/Aetheria.State.Daemon/Program.cs
@@ -0,0 +1,402 @@
+using Aetheria.State;
+using Aetheria.State.Documents;
+using GameCult.Aetheria.State.Unity;
+
+var options = AetheriaDaemonHostOptions.Parse(args);
+var startedAtUtc = DateTimeOffset.UtcNow.ToString("O");
+
+Console.WriteLine($"Aetheria Verse daemon starting: {options.StatePath}");
+
+await using var node = await AetheriaStateNode.OpenAsync(
+ options.StatePath,
+ runtimeId: options.DaemonId,
+ startServer: true).ConfigureAwait(false);
+using var discoveryHost = new AetheriaVerseDiscoveryHost(node);
+
+await EnsureWorldDocumentAsync(node).ConfigureAwait(false);
+var verseHost = await EnsureVerseHostSettingsAsync(node, options, startedAtUtc).ConfigureAwait(false);
+discoveryHost.Update(verseHost);
+await PublishRuntimeSessionAsync(node, options, startedAtUtc, "starting").ConfigureAwait(false);
+await PublishStateSurfacesAsync(node, options, startedAtUtc).ConfigureAwait(false);
+var firstTick = await TickAsync(node, options).ConfigureAwait(false);
+Console.WriteLine($"Aetheria Verse daemon published frame {firstTick.Frame.FrameId}.");
+
+if (options.Once)
+{
+ await PublishRuntimeSessionAsync(node, options, startedAtUtc, "completed").ConfigureAwait(false);
+ return;
+}
+
+var stopped = new TaskCompletionSource