diff --git a/.gitignore b/.gitignore
index 643b33d..6cebb1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,11 @@ Thumbs.db
.obsidian/workspace.json
.obsidian/workspace-mobile.json
.trash/
+
+# C# and Godot runtime state
+/artifacts/
+**/bin/
+**/obj/
+client/.godot/
+*.user
+*.suo
diff --git a/Delvehold.slnx b/Delvehold.slnx
new file mode 100644
index 0000000..f8386c3
--- /dev/null
+++ b/Delvehold.slnx
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/Delvehold/Documentation/Implementation Map.md b/Delvehold/Documentation/Implementation Map.md
new file mode 100644
index 0000000..17a96c2
--- /dev/null
+++ b/Delvehold/Documentation/Implementation Map.md
@@ -0,0 +1,36 @@
+---
+title: Implementation Map
+description: Current runtime body, authority boundaries, and focused verification targets.
+aliases:
+ - /documentation/implementation-map
+---
+
+# Implementation Map
+
+This page describes the body that exists in source. The [[Game Design/index|Game Design Document]] describes the intended game and remains the authority for design.
+
+## Runtime body
+
+- `Delvehold.WorldHost` is the sole writer of canonical local world state. It owns the persistent CultCache, the explicit first-world seed, and admission of typed commands.
+- `Delvehold.Client` is one Godot 4.7.2 C# application. DELVE and HOLD are views inside that application, not separate clients or authorities.
+- `Delvehold.Protocol` contains the typed CultMesh operation payloads shared by the client and host. It owns no state and performs no simulation.
+- CultLib revision `334e60f1928b4212a29dd8b0d19b2c099fe6365e` supplies CultCache, CultNet, and CultMesh. Builds reject another revision or a dirty CultLib worktree. The host targets the installed .NET 10 runtime; Godot's C# client targets its supported .NET 8 surface.
+
+## First live path
+
+The client discovers the host through its CultMesh rendezvous endpoint and sends `delvehold.world/enter`. The host returns and persists an idempotent typed world-entry command receipt. Entering the world does not advance simulation or grant the client write authority.
+
+The seed state names one workshop, one commons, one dungeon core, one contract, and one material. These identifiers make the connection observable; they are not a claim that the first playable slice is complete.
+
+## Forbidden writers
+
+- The Godot scene tree, UI controls, and client session cannot write canonical world state.
+- The protocol assembly cannot persist, simulate, or invent defaults on behalf of the host.
+- Reconnect and replay cannot manufacture a second receipt for the same idempotency key.
+- JSON is not a load-bearing state format. Canonical state and receipts are typed CultCache documents.
+
+## Verification layer
+
+`scripts/verify-runtime.ps1` builds the three projects, starts the host, connects the headless Godot client, rejects an idempotency collision and an invalid projection, restarts the host against the same CultCache, and replays the original enter intent. The smoke compares the complete accepted receipt summary, including its original effective time, across both accepted calls, requires the CultCache state file to remain present, and rejects known canonical-write APIs in client source.
+
+Dungeon generation, CultMath randomness, CultGeometry isosurface extraction, gameplay mutation, and the Ghostlight boundary remain subsequent cuts.
diff --git a/Delvehold/Documentation/index.md b/Delvehold/Documentation/index.md
index 45bbdba..a02992d 100644
--- a/Delvehold/Documentation/index.md
+++ b/Delvehold/Documentation/index.md
@@ -6,3 +6,4 @@ description: Technical contracts, authority boundaries, and implementation maps.
The machinery beneath DELVE/HOLD: typed world state, ownership boundaries, client projections, and the contracts joining them.
- [[Authority Map|Authority map]]
+- [[Implementation Map|Implementation map]]
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..e0b9b3f
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,13 @@
+
+
+ net8.0
+ enable
+ enable
+ latest
+ $(CULTLIB_ROOT)
+ $(MSBuildThisFileDirectory)..\CultLib
+ 334e60f1928b4212a29dd8b0d19b2c099fe6365e
+ C:\Program Files\Git\cmd\git.exe
+ git
+
+
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 0000000..948be29
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index 00fd0f7..6cd6596 100644
--- a/README.md
+++ b/README.md
@@ -21,3 +21,20 @@ The launcher expects `GameCult-Quartz` beside this repository or at `GAMECULT_QU
```
Generated output is written to `quartz-site/public/`.
+
+## Local runtime
+
+The first runtime foundation expects the admitted CultLib worktree at
+`F:\Projects\CultLib-aetheria-authority` and Godot 4.7.2 Mono at
+`F:\Applications\Godot_v4.7.2-stable_mono_win64`. Build and exercise the
+Godot-to-host CultMesh path, including a host restart, with:
+
+```powershell
+.\scripts\verify-runtime.ps1
+```
+
+Open the project in the Godot editor with:
+
+```powershell
+.\scripts\open-godot.ps1
+```
diff --git a/client/Delvehold.Client.csproj b/client/Delvehold.Client.csproj
new file mode 100644
index 0000000..d6a251c
--- /dev/null
+++ b/client/Delvehold.Client.csproj
@@ -0,0 +1,12 @@
+
+
+ net8.0
+ true
+ Delvehold.Client
+ Delvehold.Client
+
+
+
+
+
+
diff --git a/client/Main.cs b/client/Main.cs
new file mode 100644
index 0000000..ef58765
--- /dev/null
+++ b/client/Main.cs
@@ -0,0 +1,160 @@
+using Delvehold.Protocol;
+using GameCult.Mesh;
+using Godot;
+
+namespace Delvehold.Client;
+
+public partial class Main : Control
+{
+ private Label _mode = null!;
+ private Label _status = null!;
+ private Button _delve = null!;
+ private Button _hold = null!;
+ private CultMeshClient? _mesh;
+ private string _projection = "hold";
+
+ public override void _Ready()
+ {
+ BuildInterface();
+ _ = EnterWorldAsync();
+ }
+
+ public override void _ExitTree()
+ {
+ _mesh?.Dispose();
+ }
+
+ private void BuildInterface()
+ {
+ var margin = new MarginContainer();
+ margin.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);
+ margin.AddThemeConstantOverride("margin_left", 48);
+ margin.AddThemeConstantOverride("margin_top", 42);
+ margin.AddThemeConstantOverride("margin_right", 48);
+ margin.AddThemeConstantOverride("margin_bottom", 42);
+ AddChild(margin);
+
+ var column = new VBoxContainer();
+ column.AddThemeConstantOverride("separation", 18);
+ margin.AddChild(column);
+
+ var title = new Label { Text = "DELVE / HOLD" };
+ title.AddThemeFontSizeOverride("font_size", 42);
+ column.AddChild(title);
+
+ _mode = new Label { Text = "HOLD projection" };
+ _mode.AddThemeFontSizeOverride("font_size", 24);
+ column.AddChild(_mode);
+
+ var modes = new HBoxContainer();
+ modes.AddThemeConstantOverride("separation", 12);
+ column.AddChild(modes);
+ _delve = new Button { Text = "DELVE", CustomMinimumSize = new Vector2(160, 48) };
+ _hold = new Button { Text = "HOLD", CustomMinimumSize = new Vector2(160, 48), Disabled = true };
+ _delve.Pressed += () => SelectProjection("delve");
+ _hold.Pressed += () => SelectProjection("hold");
+ modes.AddChild(_delve);
+ modes.AddChild(_hold);
+
+ _status = new Label { Text = "Finding the Greathold world host…", AutowrapMode = TextServer.AutowrapMode.WordSmart };
+ column.AddChild(_status);
+ }
+
+ private void SelectProjection(string projection)
+ {
+ _projection = projection;
+ _mode.Text = projection.ToUpperInvariant() + " projection";
+ _delve.Disabled = projection == "delve";
+ _hold.Disabled = projection == "hold";
+ }
+
+ private async Task EnterWorldAsync()
+ {
+ var options = ClientArguments.Parse(OS.GetCmdlineUserArgs());
+ SelectProjection(options.Projection);
+ try
+ {
+ _mesh = new CultMeshClient(new CultMeshClientOptions
+ {
+ RendezvousEndpoints = [options.Endpoint],
+ Discovery = new CultMeshVerseDiscoveryClientOptions { TransportVersion = "cultmesh.v0" },
+ Sessions = new CultMeshSessionManagerOptions
+ {
+ Trust = new CultMeshAuthorityTrustPolicy(CultMeshAuthorityTrustMode.LocalDevelopment)
+ },
+ OperationResponseTimeout = TimeSpan.FromSeconds(8)
+ });
+ var receipt = await _mesh.InvokeAsync(
+ new CultMeshSessionTarget(
+ DelveholdWorldContract.VerseId,
+ DelveholdWorldContract.AuthorityRuntimeId),
+ DelveholdWorldContract.ServiceId,
+ DelveholdWorldContract.EnterOperation,
+ DelveholdWorldContract.EnterIntentSchema,
+ DelveholdWorldContract.EnterReceiptSchema,
+ new WorldEnterIntent
+ {
+ Projection = _projection
+ },
+ options.ClientRuntimeId,
+ options.SmokeId);
+
+ var line = $"{receipt.Status} {receipt.Value.IdempotencyKey} world={receipt.Value.WorldId} revision={receipt.Value.WorldRevision} effective={receipt.Value.EffectiveAtUtc:O}";
+ var admittedAsExpected = receipt.Status == options.ExpectedStatus;
+ CallDeferred(MethodName.ShowResult, line, admittedAsExpected, options.HeadlessSmoke);
+ }
+ catch (Exception error)
+ {
+ CallDeferred(MethodName.ShowResult, error.Message, false, options.HeadlessSmoke);
+ }
+ }
+
+ private void ShowResult(string message, bool succeeded, bool quit)
+ {
+ _status.Text = succeeded ? message : "Connection failed: " + message;
+ GD.Print($"DELVEHOLD_SMOKE {(succeeded ? "PASS" : "FAIL")} {message}");
+ if (quit) GetTree().Quit(succeeded ? 0 : 1);
+ }
+
+ private sealed record ClientArguments(
+ string Endpoint,
+ string ClientRuntimeId,
+ string SmokeId,
+ string Projection,
+ string ExpectedStatus,
+ bool HeadlessSmoke)
+ {
+ public static ClientArguments Parse(string[] args)
+ {
+ var endpoint = "cultnet+tcp://127.0.0.1:4075";
+ var clientId = "delvehold.godot.local";
+ var smokeId = Guid.NewGuid().ToString("N");
+ var projection = "hold";
+ var expectedStatus = "accepted";
+ var headless = false;
+ for (var index = 0; index < args.Length; index++)
+ {
+ switch (args[index])
+ {
+ case "--endpoint" when index + 1 < args.Length:
+ endpoint = args[++index];
+ break;
+ case "--client-id" when index + 1 < args.Length:
+ clientId = args[++index];
+ break;
+ case "--smoke-id" when index + 1 < args.Length:
+ smokeId = args[++index];
+ headless = true;
+ break;
+ case "--projection" when index + 1 < args.Length:
+ projection = args[++index].ToLowerInvariant();
+ break;
+ case "--expect-status" when index + 1 < args.Length:
+ expectedStatus = args[++index].ToLowerInvariant();
+ break;
+ }
+ }
+ return new ClientArguments(endpoint, clientId, smokeId, projection, expectedStatus, headless);
+ }
+ }
+}
diff --git a/client/Main.cs.uid b/client/Main.cs.uid
new file mode 100644
index 0000000..ab64960
--- /dev/null
+++ b/client/Main.cs.uid
@@ -0,0 +1 @@
+uid://dgm3vnnuux4iy
diff --git a/client/Main.tscn b/client/Main.tscn
new file mode 100644
index 0000000..07a45f6
--- /dev/null
+++ b/client/Main.tscn
@@ -0,0 +1,12 @@
+[gd_scene load_steps=2 format=3]
+
+[ext_resource path="res://Main.cs" type="Script" id="1"]
+
+[node name="Delvehold" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1")
diff --git a/client/project.godot b/client/project.godot
new file mode 100644
index 0000000..38199e2
--- /dev/null
+++ b/client/project.godot
@@ -0,0 +1,33 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+; [section] ; section goes between []
+; param=value ; assign values to parameters
+
+config_version=5
+
+[application]
+
+config/name="DELVE/HOLD"
+run/main_scene="res://Main.tscn"
+config/features=PackedStringArray("4.7", "C#")
+
+[display]
+
+window/size/viewport_width=1280
+window/size/viewport_height=720
+window/size/window_width_override=1280
+window/size/window_height_override=720
+window/stretch/mode="canvas_items"
+
+[dotnet]
+
+project/assembly_name="Delvehold.Client"
+
+[rendering]
+
+renderer/rendering_method="gl_compatibility"
+renderer/rendering_method.mobile="gl_compatibility"
+environment/defaults/default_clear_color=Color(0.035, 0.027, 0.024, 1)
diff --git a/scripts/open-godot.ps1 b/scripts/open-godot.ps1
new file mode 100644
index 0000000..cbf8b1c
--- /dev/null
+++ b/scripts/open-godot.ps1
@@ -0,0 +1,13 @@
+[CmdletBinding()]
+param(
+ [string]$GodotRoot = 'F:\Applications\Godot_v4.7.2-stable_mono_win64',
+ [string]$CultLibRoot = 'F:\Projects\CultLib-aetheria-authority'
+)
+
+$godot = Join-Path $GodotRoot 'Godot_v4.7.2-stable_mono_win64.exe'
+if (-not (Test-Path -LiteralPath $godot)) {
+ throw "Godot 4.7.2 Mono was not found at '$godot'."
+}
+
+$env:CULTLIB_ROOT = $CultLibRoot
+Start-Process -FilePath $godot -ArgumentList @('--editor', '--path', (Join-Path $PSScriptRoot '..\client'))
diff --git a/scripts/verify-runtime.ps1 b/scripts/verify-runtime.ps1
new file mode 100644
index 0000000..1a246d3
--- /dev/null
+++ b/scripts/verify-runtime.ps1
@@ -0,0 +1,90 @@
+[CmdletBinding()]
+param(
+ [string]$CultLibRoot = 'F:\Projects\CultLib-aetheria-authority',
+ [string]$GodotRoot = 'F:\Applications\Godot_v4.7.2-stable_mono_win64'
+)
+
+$ErrorActionPreference = 'Stop'
+$repoRoot = Split-Path $PSScriptRoot -Parent
+$godot = Join-Path $GodotRoot 'Godot_v4.7.2-stable_mono_win64_console.exe'
+if (-not (Test-Path -LiteralPath $godot)) {
+ throw "Godot 4.7.2 Mono console was not found at '$godot'."
+}
+
+dotnet build (Join-Path $repoRoot 'Delvehold.slnx') -p:CultLibRoot=$CultLibRoot
+if ($LASTEXITCODE -ne 0) { throw 'The focused .NET/Godot build failed.' }
+
+$smokeRoot = Join-Path $repoRoot ('artifacts\runtime-smoke\' + [guid]::NewGuid().ToString('N'))
+New-Item -ItemType Directory -Path $smokeRoot -Force | Out-Null
+$statePath = Join-Path $smokeRoot 'world.cc'
+$port = Get-Random -Minimum 41000 -Maximum 49000
+$endpoint = "cultnet+tcp://127.0.0.1:$port"
+$smokeId = 'runtime-smoke-enter-v0'
+$clientId = 'delvehold.godot.smoke'
+$hostProject = Join-Path $repoRoot 'src\Delvehold.WorldHost\Delvehold.WorldHost.csproj'
+$clientProject = Join-Path $repoRoot 'client'
+
+function Start-WorldHost([string]$suffix) {
+ $stdout = Join-Path $smokeRoot "host-$suffix.out.log"
+ $stderr = Join-Path $smokeRoot "host-$suffix.err.log"
+ $process = Start-Process -FilePath 'dotnet' -PassThru -WindowStyle Hidden `
+ -RedirectStandardOutput $stdout -RedirectStandardError $stderr `
+ -ArgumentList @('run', '--no-build', '--project', $hostProject, '-p:CultLibRoot=' + $CultLibRoot, '--', '--state', $statePath, '--port', $port)
+ $deadline = [DateTime]::UtcNow.AddSeconds(20)
+ while ([DateTime]::UtcNow -lt $deadline) {
+ if ($process.HasExited) {
+ throw "World host exited before readiness. See '$stderr'."
+ }
+ if ((Test-Path -LiteralPath $stdout) -and (Select-String -Path $stdout -Pattern 'DELVEHOLD_HOST_READY' -Quiet)) {
+ return $process
+ }
+ Start-Sleep -Milliseconds 100
+ }
+ Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
+ throw "World host did not become ready. See '$stdout' and '$stderr'."
+}
+
+function Invoke-GodotSmoke([string]$suffix, [string]$projection, [string]$expectedStatus, [string]$intentId = $smokeId) {
+ $log = Join-Path $smokeRoot "godot-$suffix.log"
+ & $godot --headless --path $clientProject --editor --quit-after 1 2>&1 | Set-Content -Path $log
+ if ($LASTEXITCODE -ne 0) { throw "Godot import/build failed. See '$log'." }
+ $runtimeOutput = & $godot --headless --path $clientProject -- --endpoint $endpoint --client-id $clientId --smoke-id $intentId --projection $projection --expect-status $expectedStatus 2>&1
+ $runtimeOutput | Tee-Object -FilePath $log -Append | Write-Host
+ if ($LASTEXITCODE -ne 0) { throw "Godot runtime smoke failed. See '$log'." }
+ if (-not (Select-String -Path $log -Pattern "DELVEHOLD_SMOKE PASS $expectedStatus $intentId" -Quiet)) {
+ throw "Godot did not report the expected '$expectedStatus' typed receipt. See '$log'."
+ }
+ return (Select-String -Path $log -Pattern "DELVEHOLD_SMOKE PASS $expectedStatus $intentId" | Select-Object -Last 1).Line
+}
+
+$forbiddenClientWriters = Select-String -Path (Join-Path $repoRoot 'client\*.cs') `
+ -Pattern 'CultMesh\.CreateNodeAsync|\.Database\.PutAsync|\.Cache\.UpsertAsync'
+if ($forbiddenClientWriters) {
+ throw 'The Godot client contains a canonical CultCache/CultMesh write path.'
+}
+
+$hostProcess = $null
+try {
+ $hostProcess = Start-WorldHost 'first'
+ $firstReceipt = Invoke-GodotSmoke 'first' 'hold' 'accepted'
+ $null = Invoke-GodotSmoke 'collision' 'delve' 'rejected'
+ $null = Invoke-GodotSmoke 'invalid-projection' 'banana' 'rejected' 'runtime-smoke-invalid-v0'
+ Stop-Process -Id $hostProcess.Id -Force
+ $hostProcess.WaitForExit()
+ $hostProcess = Start-WorldHost 'restart'
+ $restartReceipt = Invoke-GodotSmoke 'restart' 'hold' 'accepted'
+}
+finally {
+ if ($null -ne $hostProcess -and -not $hostProcess.HasExited) {
+ Stop-Process -Id $hostProcess.Id -Force -ErrorAction SilentlyContinue
+ }
+}
+
+if (-not (Test-Path -LiteralPath $statePath)) {
+ throw 'The host did not persist its CultCache state.'
+}
+if ($firstReceipt -ne $restartReceipt) {
+ throw "The restarted host did not return the original persisted receipt.`nFirst: $firstReceipt`nRestart: $restartReceipt"
+}
+
+Write-Host "DELVEHOLD_RUNTIME_VERIFIED state=$statePath receipt=$smokeId"
diff --git a/src/Delvehold.Protocol/Delvehold.Protocol.csproj b/src/Delvehold.Protocol/Delvehold.Protocol.csproj
new file mode 100644
index 0000000..41a29e6
--- /dev/null
+++ b/src/Delvehold.Protocol/Delvehold.Protocol.csproj
@@ -0,0 +1,15 @@
+
+
+ Delvehold.Protocol
+ Delvehold.Protocol
+
+
+
+
+
+
+
+
+
diff --git a/src/Delvehold.Protocol/WorldContracts.cs b/src/Delvehold.Protocol/WorldContracts.cs
new file mode 100644
index 0000000..e22018a
--- /dev/null
+++ b/src/Delvehold.Protocol/WorldContracts.cs
@@ -0,0 +1,33 @@
+using GameCult.Caching;
+using MessagePack;
+
+namespace Delvehold.Protocol;
+
+public static class DelveholdWorldContract
+{
+ public const string VerseId = "delvehold.shared-world";
+ public const string AuthorityRuntimeId = "delvehold.world-host";
+ public const string ServiceId = "delvehold.world";
+ public const string EnterOperation = "enter";
+ public const string EnterIntentSchema = "delvehold.world_enter_intent.v0";
+ public const string EnterReceiptSchema = "delvehold.world_enter_receipt.v0";
+}
+
+[MessagePackObject]
+public sealed class WorldEnterIntent
+{
+ [Key(0)] public string Projection { get; set; } = "hold";
+}
+
+[MessagePackObject]
+[CultDocument("delvehold.world_enter_receipt", DelveholdWorldContract.EnterReceiptSchema)]
+public sealed class WorldEnterReceipt
+{
+ [Key(0), CultName] public string IdempotencyKey { get; set; } = "";
+ [Key(1)] public string ClientRuntimeId { get; set; } = "";
+ [Key(2)] public string Projection { get; set; } = "hold";
+ [Key(3)] public long WorldRevision { get; set; }
+ [Key(4)] public string WorldId { get; set; } = "";
+ [Key(5)] public string Message { get; set; } = "";
+ [Key(6)] public DateTime EffectiveAtUtc { get; set; }
+}
diff --git a/src/Delvehold.WorldHost/Delvehold.WorldHost.csproj b/src/Delvehold.WorldHost/Delvehold.WorldHost.csproj
new file mode 100644
index 0000000..6449b4b
--- /dev/null
+++ b/src/Delvehold.WorldHost/Delvehold.WorldHost.csproj
@@ -0,0 +1,17 @@
+
+
+ Exe
+ net10.0
+ Delvehold.WorldHost
+ Delvehold.WorldHost
+
+
+
+
+
+
+
+
+
diff --git a/src/Delvehold.WorldHost/Program.cs b/src/Delvehold.WorldHost/Program.cs
new file mode 100644
index 0000000..46c0117
--- /dev/null
+++ b/src/Delvehold.WorldHost/Program.cs
@@ -0,0 +1,170 @@
+using System.Net;
+using System.Net.Sockets;
+using Delvehold.Protocol;
+using Delvehold.WorldHost;
+using GameCult.Caching;
+using GameCult.Caching.MessagePack;
+using GameCult.Mesh;
+using GameCult.Networking;
+
+const string routeGeneration = "delvehold-local-v1";
+var settings = HostSettings.Parse(args);
+Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(settings.StatePath))!);
+
+var documentTypes = new[] { typeof(DelveholdWorldState), typeof(WorldEnterReceipt) };
+var cacheRegistry = CultMesh.CreateCultCacheDocumentRegistry(documentTypes);
+var networkRegistry = CultMesh.CreateCultNetDocumentRegistry(documentTypes, cacheRegistry);
+using var node = await CultMesh.CreateNodeAsync(settings.StatePath, new CultMeshNodeOptions
+{
+ StartServer = false,
+ CacheOptions = new CultCacheOpenOptions
+ {
+ Registry = cacheRegistry,
+ FlushOnDispose = true,
+ StoreFlushOnDispose = true
+ },
+ DatabaseOptions = new CultNetDatabaseOptions
+ {
+ RuntimeId = DelveholdWorldContract.AuthorityRuntimeId,
+ DocumentRegistry = networkRegistry
+ }
+});
+
+var worldKey = new CultRecordKey(WorldStateContract.RecordKey);
+if (node.Cache.Get(worldKey) is null)
+ await node.Database.PutAsync(worldKey, DelveholdWorldState.FirstWorld());
+await node.FlushAsync();
+
+using var server = new TcpFramedCultNetSchemaServer(
+ new TcpListener(IPAddress.Loopback, settings.Port));
+var endpoint = $"cultnet+tcp://127.0.0.1:{server.LocalEndPoint.Port}";
+using var identity = new CultMeshSessionIdentityServer(
+ server,
+ DelveholdWorldContract.AuthorityRuntimeId,
+ [DelveholdWorldContract.VerseId],
+ [CultMeshProtocols.Documents.Value],
+ [routeGeneration]);
+
+using var catalog = new CultMeshVerseCatalog();
+catalog.Upsert(new CultMeshVerseDescriptor(
+ DelveholdWorldContract.VerseId,
+ "DELVE/HOLD shared world",
+ CultMeshVerseAuthorityModel.OperatorCluster,
+ new CultMeshVerseCompatibility("cultmesh.v0", "delvehold-local-rules-v0"),
+ authorityRoutes:
+ [
+ new CultMeshAuthorityRoute(
+ DelveholdWorldContract.AuthorityRuntimeId,
+ endpoint,
+ [CultMeshProtocols.Documents.Value],
+ priority: 10,
+ generation: routeGeneration)
+ ],
+ description: "The authoritative DELVE/HOLD world host."));
+server.OnCultNet((request, peer) =>
+{
+ peer.SendCultNet(CultMeshVerseMessages.CreateCatalogResponse(catalog, request));
+ return Task.CompletedTask;
+});
+
+using var operationGate = new SemaphoreSlim(1, 1);
+using var operations = new CultNetOperationServer(server, DelveholdWorldContract.AuthorityRuntimeId)
+ .Register(
+ DelveholdWorldContract.ServiceId,
+ DelveholdWorldContract.EnterOperation,
+ DelveholdWorldContract.EnterIntentSchema,
+ DelveholdWorldContract.EnterReceiptSchema,
+ async context =>
+ {
+ await operationGate.WaitAsync();
+ try
+ {
+ var sourceRuntimeId = context.SourceRuntimeId?.Trim() ?? "";
+ var projection = context.Value.Projection?.Trim().ToLowerInvariant() ?? "";
+ if (sourceRuntimeId.Length == 0 || (projection != "delve" && projection != "hold"))
+ {
+ var invalidWorld = node.Cache.Get(worldKey)
+ ?? throw new InvalidOperationException("Canonical world state is unavailable.");
+ return CultNetOperationReply.Rejected(
+ new WorldEnterReceipt
+ {
+ IdempotencyKey = context.IdempotencyKey,
+ ClientRuntimeId = sourceRuntimeId,
+ Projection = projection,
+ WorldRevision = invalidWorld.Revision,
+ WorldId = invalidWorld.WorldId,
+ Message = "World entry requires a caller identity and the DELVE or HOLD projection.",
+ EffectiveAtUtc = DateTime.UtcNow
+ },
+ "invalid-world-entry");
+ }
+
+ var receiptKey = new CultRecordKey("delvehold:receipt:" + context.IdempotencyKey);
+ var existing = node.Cache.Get(receiptKey);
+ if (existing is not null)
+ {
+ var matches = existing.ClientRuntimeId == sourceRuntimeId &&
+ existing.Projection == projection;
+ return matches
+ ? CultNetOperationReply.Accepted(existing)
+ : CultNetOperationReply.Rejected(
+ existing,
+ "idempotency-key-collision");
+ }
+
+ var world = node.Cache.Get(worldKey)
+ ?? throw new InvalidOperationException("Canonical world state is unavailable.");
+ var receipt = new WorldEnterReceipt
+ {
+ IdempotencyKey = context.IdempotencyKey,
+ ClientRuntimeId = sourceRuntimeId,
+ Projection = projection,
+ WorldRevision = world.Revision,
+ WorldId = world.WorldId,
+ Message = $"Entered {world.WorldId} through the {projection.ToUpperInvariant()} projection.",
+ EffectiveAtUtc = DateTime.UtcNow
+ };
+ await node.Database.PutAsync(receiptKey, receipt);
+ await node.FlushAsync();
+ return CultNetOperationReply.Accepted(receipt);
+ }
+ finally
+ {
+ operationGate.Release();
+ }
+ });
+
+server.PeerFailed += (peer, error) => Console.Error.WriteLine($"DELVEHOLD_PEER_FAILED {peer} {error.Message}");
+Console.WriteLine($"DELVEHOLD_HOST_READY {endpoint}");
+
+var stopped = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+Console.CancelKeyPress += (_, eventArgs) =>
+{
+ eventArgs.Cancel = true;
+ stopped.TrySetResult();
+};
+await Task.WhenAny(stopped.Task, server.BackgroundFailure);
+if (server.BackgroundFailure.IsCompleted)
+ throw await server.BackgroundFailure;
+
+internal sealed record HostSettings(string StatePath, int Port)
+{
+ public static HostSettings Parse(string[] args)
+ {
+ var state = Path.Combine("artifacts", "host", "delvehold.cc");
+ var port = 4075;
+ for (var index = 0; index < args.Length; index++)
+ {
+ switch (args[index])
+ {
+ case "--state" when index + 1 < args.Length:
+ state = args[++index];
+ break;
+ case "--port" when index + 1 < args.Length:
+ port = int.Parse(args[++index]);
+ break;
+ }
+ }
+ return new HostSettings(state, port);
+ }
+}
diff --git a/src/Delvehold.WorldHost/WorldState.cs b/src/Delvehold.WorldHost/WorldState.cs
new file mode 100644
index 0000000..8034954
--- /dev/null
+++ b/src/Delvehold.WorldHost/WorldState.cs
@@ -0,0 +1,34 @@
+using GameCult.Caching;
+using MessagePack;
+
+namespace Delvehold.WorldHost;
+
+internal static class WorldStateContract
+{
+ public const string Schema = "delvehold.world_state.v0";
+ public const string RecordKey = "delvehold:world:canonical";
+}
+
+[MessagePackObject]
+[CultDocument("delvehold.world_state", WorldStateContract.Schema)]
+public sealed class DelveholdWorldState
+{
+ [Key(0), CultName] public string WorldId { get; set; } = "";
+ [Key(1)] public long Revision { get; set; }
+ [Key(2)] public string WorkshopId { get; set; } = "";
+ [Key(3)] public string CommonsId { get; set; } = "";
+ [Key(4)] public string DungeonCoreId { get; set; } = "";
+ [Key(5)] public string ContractId { get; set; } = "";
+ [Key(6)] public string MaterialId { get; set; } = "";
+
+ public static DelveholdWorldState FirstWorld() => new()
+ {
+ WorldId = "greathold-first-world",
+ Revision = 1,
+ WorkshopId = "workshop-emberbench",
+ CommonsId = "commons-deepmarket",
+ DungeonCoreId = "core-cinderwake",
+ ContractId = "contract-first-light",
+ MaterialId = "material-wardstone"
+ };
+}