Deployment
Relay is three ASP.NET Core apps (gateway API, control-panel host, and this docs site) plus SQL Server, and optionally ClickHouse/ClickStack and Qdrant. The .NET Aspire AppHost is a local-development orchestrator only and is not deployed.
#Build
Build in Release with --no-restore (running instances can lock Debug DLLs, and the environment restore is restricted):
dotnet build Application.sln -c Release --no-restore
Publish each app you deploy:
dotnet publish Application.Api -c Release -o out/api
dotnet publish Application -c Release -o out/panel
dotnet publish Application.Doc -c Release -o out/docs
The repo's GitHub Actions workflows (.github/workflows/deploy_api.yml, deploy_app.yml, deploy_docs.yml) do exactly this on a self-hosted runner and copy the output into IIS sites — a push to dev deploys the test environment, a push to main deploys prod, and each workflow triggers only when its projects change. The DACPAC publish is not part of CI.
#Database schema (DACPAC)
Schema is not applied by the apps at runtime — there are no EF migrations for the gateway schema. Publish the Application.Database DACPAC to the relay_gateway database (SqlPackage / Visual Studio) as part of your release, before the new build runs: the gateway selects recently added columns on every request. New tables/columns are authored as .sql there and mirrored in the DbContext model.
ClickHouse needs no schema step: relay_observation and relay_score are created, and new columns added, by the apps at startup.
#Configuration in production
Supply secrets via environment variables or Key Vault — never committed files. At minimum:
ConnectionStrings:GatewayDbon both apps;ConnectionStrings:UserManagementDband the identity-app sign-in settings (Identity:BaseUrl,Identity:ClientId,Identity:ClientSecret) on the panel host, which refuses to start without the secret.Encryption:MasterKey— the same value on the panel host and the gateway, or neither can read what the other encrypted and the Playground delegation fails.Gateway:PublicBaseUrl(andGateway:InternalBaseUrlwhen the panel reaches the gateway over a different address) so the panel's Playground, workflow approvals and resume links point at the right place;Gateway:PanelBaseUrlfor links in approval notifications.- Provider keys are entered in the panel and stored encrypted; set a
SecretRefconfig key only for providers you deliberately keep on the configuration fallback. ClickHouse:ConnectionStringon both apps if you want telemetry, dashboards and traces.Qdrant:Url(+ key) if you use knowledge bases.Connectors:RedirectUrionly behind a reverse proxy that rewrites the host.
See the full Configuration reference.
#Telemetry and HyperDX
Relay always writes its own observability tables when ClickHouse is configured. For HyperDX dashboards and alerting on the same data, run ClickStack (the all-in-one image bundles ClickHouse + an OpenTelemetry collector + HyperDX), set Gateway:Otlp:Enabled=true, Gateway:Otlp:Endpoint and Gateway:Otlp:ApiKey (the HyperDX ingestion key), and optionally Telemetry:DashboardSource=otel to read the usage dashboard from the collector's otel_traces. Telemetry:Backend is obsolete and ignored. See Telemetry & dashboards and the repo's docs/clickstack.md.
#Networking notes
- On Windows/dev, prefer
127.0.0.1overlocalhostfor containerised dependencies (IPv4 vs. IPv6). - Restrict the control-plane CORS origins with
Cors:PanelOrigins. - Serve the gateway over HTTPS: a page served over HTTPS can only open
wss://realtime sockets to a remote host. - The gateway API and the panel can scale independently, but several gateway stores are per instance: the response cache and idempotency store, the per-key rate limiter, provider health, and realtime session tokens (mint and connect must reach the same process — use sticky sessions behind a load balancer).
#This docs site
Application.Doc is self-contained (no external packages, content copied to output). Publish and host it like any static-ish ASP.NET app; it serves the Markdown under Docs/ rendered on the fly.