# Phase 07 — Status page Stand up the read-only Kestrel-hosted admin endpoint on `Mbproxy.AdminPort`. Two routes — `GET /` (self-contained HTML, meta-refresh 5 s) and `GET /status.json` (the same data as JSON). No admin actions, no auth. **Depends on:** Phase 05 (supervisor snapshots), Phase 06 (config reload counters). **Parallel-safe with:** nothing (touches DI registration + needs counters from both 05 and 06). ## Goal A single port that an operator can open in a browser and see, at a glance: - Service uptime, version, last-reload timestamp + counts. - Every configured PLC's listener state (`bound` / `recovering` / `stopped`), last bind error, currently connected clients and their per-client PDU counts, PDU counts by function code, BCD slots rewritten, partial-overlap warnings, backend exception counts by code, last round-trip ms, bytes upstream/downstream. Same data is exposed as `/status.json` for scraping (Prometheus textfile, custom Nagios check, etc.). ## Outputs ``` src/Mbproxy/Admin/AdminEndpointHost.cs # owns the Kestrel server lifecycle src/Mbproxy/Admin/StatusSnapshotBuilder.cs # composes per-PLC + service-wide snapshots src/Mbproxy/Admin/StatusDto.cs # the wire DTOs for /status.json src/Mbproxy/Admin/StatusHtmlRenderer.cs # builds the single-page HTML src/Mbproxy/Admin/AssemblyVersionAccessor.cs # cached version string tests/Mbproxy.Tests/Admin/StatusSnapshotBuilderTests.cs tests/Mbproxy.Tests/Admin/AdminEndpointTests.cs # HTTP-level; live Kestrel + HttpClient ``` Modifications: - `src/Mbproxy/Mbproxy.csproj` — add `Microsoft.AspNetCore.App` framework reference (the Worker SDK doesn't include ASP.NET Core by default). - `src/Mbproxy/Program.cs` — register `AdminEndpointHost` as a hosted service; wire it through DI alongside the proxy worker. AdminPort comes from `IOptionsMonitor`. - `src/Mbproxy/Proxy/ProxyCounters.cs` — extend with per-client counters: `IReadOnlyList Snapshot()` includes connected clients with `Remote`, `ConnectedAtUtc`, `PdusForwarded`, `LastRoundTripMs`. - `src/Mbproxy/Proxy/PlcConnectionPair.cs` — record connect time, expose `RemoteEndpoint`, track round-trip time per request (EWMA via `LastRoundTripMs` field). - Service-wide counters introduced here: `ServiceCounters` with `UptimeStartedAtUtc`, `LastReloadUtc`, `ReloadCount`, `ReloadRejectedCount`. Wired into `ConfigReconciler` (bump on apply / reject) and the service start path (set started-at). ## Tasks 1. **`StatusDto.cs`** — record types matching the design's per-PLC + service-wide field tables verbatim. Use `System.Text.Json` source generation (`JsonSerializerContext`) to keep the response allocation-light: ```csharp [JsonSerializable(typeof(StatusResponse))] internal partial class StatusJsonContext : JsonSerializerContext; ``` 2. **`StatusSnapshotBuilder.cs`** — pulls from injected `ProxyWorker` (or a slim view of it), `ConfigReconciler`, `ServiceCounters`, and each `PlcListenerSupervisor`. Builds a `StatusResponse` record. Pure logic; no I/O. The builder is `[Sealed]` and constructed once via DI; calling `Build()` is the only operation. 3. **`StatusHtmlRenderer.cs`** — pure function `string Render(StatusResponse status)`. Produces a single HTML document with: - `` for auto-refresh. - A header line with service version + uptime + last-reload info. - A table per PLC. Columns match the per-PLC field set; `listener.state` is colour-coded inline (CSS in a `