diff --git a/Backend/LanderConfig.cs b/Backend/LanderConfig.cs
new file mode 100644
index 0000000..3dd57bb
--- /dev/null
+++ b/Backend/LanderConfig.cs
@@ -0,0 +1,34 @@
+namespace Backend;
+
+public sealed class LanderConfig
+{
+ public string CliPath { get; set; } = "publish/GameCli/GameCli";
+ public string ModelPath { get; set; } = "models/ppo_lander.onnx";
+}
+
+///
+/// Walks up from to find the repo root
+/// (marker file: GameCli.sln) and resolves the configured paths against it.
+///
+public static class PathResolver
+{
+ public static string RepoRoot()
+ {
+ var dir = AppContext.BaseDirectory;
+ while (dir is not null)
+ {
+ if (File.Exists(Path.Combine(dir, "GameCli.sln"))) return dir;
+ var parent = Directory.GetParent(dir);
+ if (parent is null) break;
+ dir = parent.FullName;
+ }
+ throw new InvalidOperationException(
+ "could not locate repo root (no GameCli.sln found in any ancestor)");
+ }
+
+ public static string Resolve(string relativeOrAbsolute)
+ {
+ if (Path.IsPathRooted(relativeOrAbsolute)) return relativeOrAbsolute;
+ return Path.GetFullPath(Path.Combine(RepoRoot(), relativeOrAbsolute));
+ }
+}
diff --git a/Backend/Program.cs b/Backend/Program.cs
index f0282f0..a965806 100644
--- a/Backend/Program.cs
+++ b/Backend/Program.cs
@@ -1,5 +1,52 @@
-// Placeholder. Rewritten in Task 6.
+using System.Net.WebSockets;
+using Backend;
+
var builder = WebApplication.CreateBuilder(args);
+
+// -------- Config --------
+builder.Services.Configure(builder.Configuration.GetSection("Lander"));
+
+// -------- Singletons --------
+builder.Services.AddSingleton(sp =>
+{
+ var cfg = sp.GetRequiredService>().Value;
+ var absolute = PathResolver.Resolve(cfg.ModelPath);
+ return new PolicyRunner(absolute);
+});
+
+builder.Services.AddLogging();
+
var app = builder.Build();
-app.MapGet("/", () => "GameCli Backend placeholder");
+app.UseWebSockets();
+
+// -------- Health --------
+app.MapGet("/", () => "GameCli Backend");
+
+// -------- WebSocket endpoint --------
+app.Map("/ws/game", async (HttpContext ctx,
+ PolicyRunner policy,
+ Microsoft.Extensions.Options.IOptions cfg,
+ ILoggerFactory loggerFactory) =>
+{
+ if (!ctx.WebSockets.IsWebSocketRequest)
+ {
+ ctx.Response.StatusCode = 400;
+ return;
+ }
+ using var socket = await ctx.WebSockets.AcceptWebSocketAsync();
+ var cliPath = PathResolver.Resolve(cfg.Value.CliPath);
+ var proc = new GameProcess(cliPath);
+ var logger = loggerFactory.CreateLogger();
+ await using var session = new GameSession(socket, proc, policy, logger);
+ try
+ {
+ await session.RunAsync(ctx.RequestAborted);
+ }
+ catch (WebSocketException) { /* client disconnected */ }
+ catch (OperationCanceledException) { /* shutdown */ }
+});
+
app.Run();
+
+// -------- Public program class so Backend.Tests can use WebApplicationFactory --------
+public partial class Program { }