Thresh is a production-minded .NET library for integrating with the League Client API (LCU). It is a low-level, modular, DI-first infrastructure library designed for composability, operational reliability, and clear ownership boundaries.
- Async-first, DI-first API design
- Resilient HTTP primitives built on
HttpClientFactory+ Polly v8 - Real-time event streaming over WAMP WebSocket (
OnJsonApiEvent) - Flexible subscriptions by URI and Regex, with optional initial snapshot hydration
- Observability hooks via
ILogger,Meter, andActivitySource("Thresh") - Production-ready repository structure with tests, samples, benchmarks, and DocFX docs
Supported target frameworks for runtime packages:
net8.0net10.0
Officially supported operating systems:
- Windows
- macOS
Lockfile discovery follows a platform-aware, deterministic order:
LCU_LOCKFILEenvironment override- cached lockfile path (when still valid)
- process-derived lockfile candidates
- known install paths per platform
For unsupported layouts (custom launchers, Wine/Proton variants), use LCU_LOCKFILE explicitly.
Process-based discovery is best-effort and may depend on local process inspection permissions.
Thresh is intended for developers who want reliable LCU primitives without adopting a high-level application framework.
Common uses include:
- building typed clients or endpoint groups on top of
ILcuHttpClient - subscribing to LCU JSON API events through
IEventStreamwith URI- or regex-based matching - running LCU integrations inside hosted services, workers, or background agents with DI and structured logging
- composing your own retry, health, observability, and lifecycle boundaries around a small set of focused abstractions
Thresh intentionally stops at transport, discovery, and event primitives. Higher-level workflows remain in consumer code or optional packages.
| Package | Use when | Includes |
|---|---|---|
Thresh.Extensions |
You want the core runtime stack with DI registration | ILcuHttpClient, IEventStream, retries, auth handler, observability wiring |
Thresh.Endpoints |
You want typed endpoint groups on top of ILcuHttpClient |
Summoner, gameflow, champ select, and lobby typed clients |
Thresh.Hosting |
You run Thresh inside a hosted/background process | Hosting integration and lifecycle-friendly wiring |
Thresh.HealthChecks |
You expose health probes in service environments | LCU health check integration |
Thresh.Reactive |
You prefer reactive composition over callbacks | Rx-friendly wrappers around stream events |
For most applications, start with Thresh.Extensions, then add optional packages only where they fit your composition model.
Thresh.Extensions is the main entry package for runtime composition.
dotnet add package Thresh.ExtensionsThresh.Extensions wires the low-level runtime stack into DI. The example below uses one relative HTTP request to read the current gameflow state, then subscribes to the same route for subsequent updates.
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Thresh.Abstractions;
using Thresh.Extensions;
var services = new ServiceCollection()
.AddLogging(b => b.AddSimpleConsole())
.AddThresh(o =>
{
o.AcceptSelfSignedCertificates = true; // LCU HTTP (loopback only)
o.WsAcceptSelfSignedCertificates = true; // LCU WebSocket (loopback only)
});
await using var provider = services.BuildServiceProvider();
var api = provider.GetRequiredService<ILcuHttpClient>();
var ws = provider.GetRequiredService<IEventStream>();
var session = await api.GetAsync<JsonElement>("/lol-gameflow/v1/session");
var initialPhase = session.TryGetProperty("phase", out var phaseValue) ? phaseValue.GetString() : "?";
Console.WriteLine($"Current phase = {initialPhase}");
await ws.ConnectAsync();
await ws.WaitUntilConnectedAsync(TimeSpan.FromSeconds(3));
using var sub = ws.Subscribe<JsonElement>("/lol-gameflow/v1/session", data =>
{
var phase = data.TryGetProperty("phase", out var p) ? p.GetString() : "?";
Console.WriteLine($"Phase changed = {phase}");
}, withSnapshot: false);
var listenFor = TimeSpan.FromSeconds(10);
await Task.Delay(listenFor);Use relative LCU paths where possible; Thresh rebases them to the active lockfile-derived base address. The short delay keeps the sample alive long enough for subscription callbacks to fire. For longer-running examples, see samples/Thresh.Samples.Console.
Ensure the League Client is running, or set the
LCU_LOCKFILEenvironment variable.
Try the sample project to validate your local environment quickly:
dotnet run --project samples/Thresh.Samples.Console/Thresh.Samples.Console.csproj -- --helpThen run a specific demo (for example http-get or sub-typed-snapshot) once the client is running.
Thresh assumes a local LCU trust boundary and intentionally applies a strict fail-fast policy for LCU-authenticated HTTP calls.
- Prefer relative LCU paths. Thresh rebases them to the active lockfile-derived LCU base address.
- Thresh enforces credentials only for safe destinations.
- Absolute URIs are accepted only when all checks pass: HTTPS, loopback host (
127.0.0.1,::1, orlocalhost), and active lockfile port. - Any other absolute URI is rejected before credentials are attached.
- Self-signed certificate acceptance options remain loopback-scoped.
This policy is designed to prevent credential leakage to non-local destinations.
Thresh is designed to be operationally safe in long-running processes while staying focused on infrastructure concerns:
HttpClientFactory+ Polly retry/circuit-breaker policies- reconnect-aware event streaming
- structured logging, metrics, and tracing hooks
- explicit cancellation support across API surfaces
- testability through abstractions (
ILcuHttpClient,IEventStream, fakes/stubs)
Higher-level orchestration stays outside the core packages by design. See docs for production guidance on retries, observability baselines, and safe usage patterns.
src/— production librariestests/— xUnit test projectssamples/— executable usage examplesbenchmarks/— BenchmarkDotNet performance suitedocs/— DocFX documentation source
Run deterministic benchmarks (default):
dotnet run -c Release --project benchmarks/Thresh.Benchmarks/Thresh.Benchmarks.csprojWhen only deterministic benchmarks are available, the runner auto-selects and runs them directly—no interactive prompt.
Enable live-client benchmarks (optional, requires running League Client):
$env:THRESH_BENCH_LIVE = "1"
dotnet run -c Release --project benchmarks/Thresh.Benchmarks/Thresh.Benchmarks.csprojWhen multiple benchmark groups are available, BenchmarkDotNet shows an interactive menu. You can:
- Enter the benchmark number to run a specific group
- Enter
0or*to run all benchmarks - Enter a wildcard filter like
*Deterministic*to select by name pattern
The live benchmark is environment-dependent and should be interpreted separately from deterministic runs.
- Start with
docs/articles/overview.md - Build and preview docs locally:
dotnet tool update -g docfx
docfx docs/docfx.json --serveThen open http://localhost:8080.
Use the SDK pinned in global.json (currently .NET 10 feature band).
dotnet restore Thresh.slnx
dotnet build Thresh.slnx -c Release
dotnet test Thresh.slnx -c Release --no-build
dotnet pack Thresh.slnx -c Release --no-build -o ./artifactsRelease hygiene remains lightweight and GitHub-native:
release-drafterkeeps a rolling draft release from merged PRs.- Publishing runs from
.github/workflows/publish.ymlwhen a GitHub Release is published. - The publish job rebuilds, tests, and packs before pushing
.nupkgartifacts to NuGet with duplicate-safe pushes.
For one-time setup and operational details, see docs/articles/release-process.md.
Contributions are welcome. Please read CONTRIBUTING.md before opening an issue or pull request.
If you discover a potential vulnerability, please follow the process in SECURITY.md.
This project is licensed under the MIT License.
