Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,34 @@ Also not on `SparkOptions`:
- **HttpClient settings** (the SSP and the block explorer). Drive these via
`IHttpClientFactory` named clients in your DI container.

## Custom operator TLS

Operators are reached through Grpc.Net.Client with the platform's default
handler and trust store. To talk to operators whose certificates the trust
store doesn't know — a private CA, or a self-hosted regtest network with
self-signed certificates — pass your own handler:

```csharp
var handler = new SocketsHttpHandler
{
EnableMultipleHttp2Connections = true,
SslOptions = new SslClientAuthenticationOptions
{
CertificateChainPolicy = new X509ChainPolicy
{
TrustMode = X509ChainTrustMode.CustomRootTrust,
CustomTrustStore = { X509Certificate2.CreateFromPemFile("operators-ca.pem") },
},
},
};

await using var spark = new SparkConnection(options, http, loggerFactory, handler);
```

The handler is shared by every operator channel and is not disposed with the
connection. Prefer a custom root over a raw `RemoteCertificateValidationCallback`;
if you pin self-signed leaf certificates, compare the exact certificate bytes.

## See also

- [`getting-started.md`](getting-started.md) — minimum setup.
Expand Down
1 change: 1 addition & 0 deletions src/NSpark/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ NSpark.SparkConnection.Dispose() -> void
NSpark.SparkConnection.DisposeAsync() -> System.Threading.Tasks.ValueTask
NSpark.SparkConnection.Options.get -> NSpark.SparkOptions!
NSpark.SparkConnection.SparkConnection(Microsoft.Extensions.Options.IOptions<NSpark.SparkOptions!>! options, System.Net.Http.HttpClient! httpClient) -> void
NSpark.SparkConnection.SparkConnection(Microsoft.Extensions.Options.IOptions<NSpark.SparkOptions!>! options, System.Net.Http.HttpClient! httpClient, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory, System.Net.Http.HttpMessageHandler? operatorHandler) -> void
NSpark.SparkConnection.SparkConnection(Microsoft.Extensions.Options.IOptions<NSpark.SparkOptions!>! options, System.Net.Http.HttpClient! httpClient, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory) -> void
NSpark.SparkNetwork
NSpark.SparkNetwork.Mainnet = 0 -> NSpark.SparkNetwork
Expand Down
13 changes: 10 additions & 3 deletions src/NSpark/SparkConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,17 @@ public SparkConnection(
}

/// <summary>
/// Construct a client whose operator channels send over <paramref name="operatorHandler"/>
/// instead of the network, when it is set — the seam unit tests use to stand in for operators.
/// Construct a client whose operator gRPC channels send over <paramref name="operatorHandler"/>
/// when it is set, for example a <see cref="SocketsHttpHandler"/> that trusts a private CA or
/// pins the certificates of self-hosted (regtest) operators. <c>null</c> uses the default handler.
/// </summary>
internal SparkConnection(
/// <remarks>
/// The handler is shared by every operator channel, so it must support HTTP/2
/// (<see cref="SocketsHttpHandler.EnableMultipleHttp2Connections"/> is recommended). It is not
/// disposed with the connection: the caller owns it. Mainnet still requires <c>https://</c>
/// operator addresses.
/// </remarks>
public SparkConnection(
IOptions<SparkOptions> options,
HttpClient httpClient,
ILoggerFactory loggerFactory,
Expand Down
Loading