feat: complete gRPC streaming channel — site host, docker config, docs, integration tests

Switch site host to WebApplicationBuilder with Kestrel HTTP/2 gRPC server,
add GrpcPort/keepalive config, wire SiteStreamManager as ISiteStreamSubscriber,
expose gRPC ports in docker-compose, add site seed script, update all 10
requirement docs + CLAUDE.md + README.md for the new dual-transport architecture.
This commit is contained in:
Joseph Doherty
2026-03-21 12:38:33 -04:00
parent 3fe3c4161b
commit 416a03b782
34 changed files with 728 additions and 156 deletions

View File

@@ -45,7 +45,7 @@ The Host must bind configuration sections from `appsettings.json` to strongly-ty
| Section | Options Class | Owner | Contents |
|---------|--------------|-------|----------|
| `ScadaLink:Node` | `NodeOptions` | Host | Role, NodeHostname, SiteId, RemotingPort |
| `ScadaLink:Node` | `NodeOptions` | Host | Role, NodeHostname, SiteId, RemotingPort, GrpcPort (site only, default 8083) |
| `ScadaLink:Cluster` | `ClusterOptions` | ClusterInfrastructure | SeedNodes, SplitBrainResolverStrategy, StableAfter, HeartbeatInterval, FailureDetectionThreshold, MinNrOfMembers |
| `ScadaLink:Database` | `DatabaseOptions` | Host | Central: ConfigurationDb, MachineDataDb connection strings; Site: SQLite paths |
@@ -79,6 +79,7 @@ Before the Akka.NET actor system is created, the Host must validate all required
- `NodeConfiguration.Role` must be a valid `NodeRole` value.
- `NodeConfiguration.NodeHostname` must not be null or empty.
- `NodeConfiguration.RemotingPort` must be in valid port range (165535).
- Site nodes must have `GrpcPort` in valid port range (165535) and different from `RemotingPort`.
- Site nodes must have a non-empty `SiteId`.
- Central nodes must have non-empty `ConfigurationDb` and `MachineDataDb` connection strings.
- Site nodes must have non-empty SQLite path values. Site nodes do **not** require a `ConfigurationDb` connection string — all configuration is received via artifact deployment and read from local SQLite.
@@ -112,14 +113,24 @@ The Host must configure the Akka.NET actor system using Akka.Hosting with:
On central nodes, the Host must configure the Akka.NET **ClusterClientReceptionist** and register the ManagementActor with it. This allows external processes (e.g., the CLI) to discover and communicate with the ManagementActor via ClusterClient without joining the cluster as full members. The receptionist is started as part of the Akka.NET bootstrap (REQ-HOST-6) on central nodes only.
### REQ-HOST-7: ASP.NET Web Endpoints (Central Only)
### REQ-HOST-7: ASP.NET Web Endpoints
On central nodes, the Host must use `WebApplication.CreateBuilder` to produce a full ASP.NET Core host with Kestrel, and must map web endpoints for:
- Central UI (via `MapCentralUI()` extension method).
- Inbound API (via `MapInboundAPI()` extension method).
On site nodes, the Host must use `Host.CreateDefaultBuilder` to produce a generic `IHost`**not** a `WebApplication`. This ensures no Kestrel server is started, no HTTP port is opened, and no web endpoint or middleware pipeline is configured. Site nodes are headless and must never accept inbound HTTP connections.
On site nodes, the Host must also use `WebApplication.CreateBuilder` (not `Host.CreateDefaultBuilder`) to host the **SiteStreamGrpcServer** via Kestrel HTTP/2 on the configured `GrpcPort` (default 8083). Kestrel is configured with `HttpProtocols.Http2` on the gRPC port only — no HTTP/1.1 web endpoints are exposed. The gRPC service is mapped via `MapGrpcService<SiteStreamGrpcServer>()`.
**Startup ordering (site nodes)**:
1. Actor system and SiteStreamManager must be initialized before gRPC begins accepting connections.
2. The gRPC server rejects streams with `StatusCode.Unavailable` until the actor system is ready.
**Shutdown ordering (site nodes)**:
1. On `CoordinatedShutdown`, stop accepting new gRPC streams first.
2. Cancel all active gRPC streams (triggering client-side reconnect).
3. Tear down actors.
4. Use `IHostApplicationLifetime.ApplicationStopping` to signal the gRPC server.
### REQ-HOST-8: Structured Logging