Canton.Ledger.Kernel
0.7.1
Prefix Reserved
dotnet add package Canton.Ledger.Kernel --version 0.7.1
NuGet\Install-Package Canton.Ledger.Kernel -Version 0.7.1
<PackageReference Include="Canton.Ledger.Kernel" Version="0.7.1" />
<PackageVersion Include="Canton.Ledger.Kernel" Version="0.7.1" />
<PackageReference Include="Canton.Ledger.Kernel" />
paket add Canton.Ledger.Kernel --version 0.7.1
#r "nuget: Canton.Ledger.Kernel, 0.7.1"
#:package Canton.Ledger.Kernel@0.7.1
#addin nuget:?package=Canton.Ledger.Kernel&version=0.7.1
#tool nuget:?package=Canton.Ledger.Kernel&version=0.7.1
Canton.Ledger.Kernel
Part of the Canton .NET SDK. The transport-neutral client kernel for Canton participant nodes: the Authentication, Telemetry, Resilience, Streams, Trees, Results, and Wire modules — the token providers that implement Canton.Ledger.Abstractions.ITokenProvider, the OpenTelemetry ActivitySource naming convention, an opt-in Polly retry pipeline, and the decisions both transports must make identically once their own wire vocabulary is decoded: rebuilding a transaction's hierarchy from its node ids, folding an exercise outcome, and naming a participant body that could not be read. Both the gRPC client and the JSON client consume this package as peers — neither depends on the other. Authentication sits at the bottom of the kernel's namespace DAG (it depends on neither of the other modules), so it can later be extracted into its own package without a breaking change.
Installation
dotnet add package Canton.Ledger.Kernel --version 0.7.1
Key Types
| Type | Purpose |
|---|---|
StaticTokenProvider |
Returns a fixed token string. Use for short-lived processes or testing. Implements Canton.Ledger.Abstractions.ITokenProvider |
ClientCredentialsProvider |
OAuth2 client-credentials flow with thread-safe TTL cache (SemaphoreSlim + Volatile reads/writes) |
ClientCredentialsOptions |
Config: Domain, ClientId, ClientSecret, Audience, TokenEndpoint, SafetyMargin, AllowInsecureTokenEndpoint, TokenAcquisitionTimeout, TokenFailureCooldown, Tls |
Telemetry.LedgerActivitySourceNames |
The well-known source names of every Canton client (All plus one constant each), so a host can register the whole set without referencing a concrete client assembly |
Telemetry.LedgerActivityTagNames |
The SDK-owned daml.* / canton.* / retry.* span attribute names the clients emit (All plus one constant each), so a dashboard query, sampling rule or redaction filter can name one without hardcoding the string |
Resilience.RetryOptions |
Config for the opt-in retry pipeline, set via LedgerClientOptions.Retry / RestLedgerClientOptions.Retry. Enabled defaults to false |
Resilience.RawCallReplay |
Allow() returns a scope inside which raw calls (the gRPC CreateCallInvoker() escape hatch, the REST raw Refit tier) are re-sent by the retry pipeline. Outside it they are sent once |
Usage
Client-credentials (OAuth2) via DI
services.AddCantonAuth(configuration.GetSection("Canton:Auth"));
{
"Canton": {
"Auth": {
"Domain": "dev-peaceful.eu.auth0.com",
"ClientId": "my-client-id",
"ClientSecret": "my-client-secret",
"Audience": "https://daml.com/jwt/aud/participant/<participant-id>"
}
}
}
Audience, when set, must equal the participant's target-audience; by default Canton accepts https://daml.com/jwt/aud/participant/<participant-id>. Leave it unset only if your identity provider stamps that audience into the token itself.
Domain accepts either a bare hostname (e.g. dev-peaceful.eu.auth0.com) or an absolute https URL (e.g. https://auth.example.com, or https://auth.example.com/tenant-a for per-tenant subpaths). If Domain is a bare hostname, it is treated as https://{hostname}. If it is an absolute URL, that URL is used as the base. In both cases, /oauth/token is appended, preserving any existing path (https://auth.example.com/tenant-a → https://auth.example.com/tenant-a/oauth/token). Userinfo, query, and fragments are rejected. ClientCredentialsProvider caches tokens until expires_in - SafetyMargin (default 30s) and concurrent callers share a single HTTP request during refresh. After a failed fetch, calls fail at once with that failure, without reaching the identity provider, for a cooldown window of TokenFailureCooldown.InitialDelay (500 ms by default) that doubles per consecutive failure up to MaxDelay (30 s) and resets on success; set TokenFailureCooldown.Enabled to false to fetch on every call that finds no cached token. It never re-sends a ledger call or a token request; it only makes calls fail fast for the window after a failed token fetch.
Plaintext http endpoints (whether from Domain or TokenEndpoint) are rejected by default, because the token request POSTs the client secret in cleartext — anyone on the network path could read it. To accept the risk against a local endpoint (e.g. http://localhost:8080 during development), set AllowInsecureTokenEndpoint to true; the provider then logs a warning at construction.
To override the token endpoint (e.g., non-standard OAuth2 servers like Keycloak's /realms/{realm}/protocol/openid-connect/token):
{
"Canton": {
"Auth": {
"TokenEndpoint": "https://custom.example.com/oauth/token",
"ClientId": "...",
"ClientSecret": "..."
}
}
}
Static token via DI
services.AddCantonStaticAuth("eyJ...");
Action-based configuration
services.AddCantonAuth(options =>
{
options.Domain = "https://auth.example.com";
options.ClientId = "my-client-id";
options.ClientSecret = "my-client-secret";
options.Audience = "https://daml.com/jwt/aud/participant/<participant-id>";
});
Registration precedence
Only unkeyed ITokenProvider registrations participate in the default selection; keyed providers remain independent. The unauthenticated gRPC and REST registration paths install the exact ITokenProvider.None singleton instance as their fallback. AddCantonAuth and AddCantonStaticAuth replace that exact unkeyed fallback with their explicit provider, so either may be called before or after those unauthenticated client registrations. Both preserve every pre-existing non-None unkeyed provider without resolving or constructing it.
Unauthenticated access
When no ITokenProvider is registered, AddLedgerClient/AddAdminClient register ITokenProvider.None as a default. Clients detect this and skip the Authorization header. Use this for local development with unauthenticated Canton nodes.
OpenTelemetry ActivitySource naming (Canton.Ledger.Kernel.Telemetry)
Every client's ActivitySource follows one naming convention, applied internally to Canton.Ledger.Kernel and its transports. LedgerActivitySourceNames and LedgerActivityTagNames expose the resulting well-known names, so a host registers listeners or writes dashboard queries without hardcoding strings:
using var listener = new ActivityListener
{
ShouldListenTo = source => LedgerActivitySourceNames.All.Contains(source.Name)
};
BCL System.Diagnostics.Activity only — no OpenTelemetry SDK dependency.
Opt-in retry pipeline (Canton.Ledger.Kernel.Resilience)
RetryOptions configures the retry pipeline each transport builds for itself, via LedgerClientOptions.Retry (gRPC) or RestLedgerClientOptions.Retry (REST):
using Canton.Ledger.Kernel.Resilience;
services.AddLedgerClient(options =>
{
options.Retry = new RetryOptions
{
Enabled = true,
MaxRetryAttempts = 3,
Delay = TimeSpan.FromMilliseconds(200),
};
});
RetryOptions.Enabled defaults to false, in which case the transport applies no retry behavior — a genuine no-op. The pipeline is transport-neutral: it references no Grpc.Core or System.Net.Http types, and knows nothing about ledger command semantics. A retried command can double-submit unless the caller reuses a stable command_id/submission_id for ledger-side dedup — the pipeline itself confers no idempotency.
Raw calls are the exception to "retried when enabled": the gRPC CreateCallInvoker() escape hatch and the REST raw Refit tier are sent exactly once, because the SDK cannot know whether a call it did not build is idempotent. A caller that knows better makes the call inside using (RawCallReplay.Allow()) { ... }, awaiting it before the scope is disposed.
Internals
Wire.ExternalPartyIdentity— recomputes an external party's public-key fingerprint the way Canton does (SHA-256 over a four-byte hash purpose of 12 and the key bytes, as a1220-prefixed multihash; an Ed25519SubjectPublicKeyInfocontributes its raw 32-byte key) and checks a generated topology against it, so both transports fail closed on a fingerprint or party namespace that is not the caller's key, and on an allocated party that is not the one the allocation's onboarding transaction is for, whichExternalPartyTopologyVerifier.ExpectedPartyOfderives as the mapping's hint in the namespace of its signing key. Internal — visible to the transports viaInternalsVisibleTo, not part of the package's public surfaceWire.PreparedSubmissionIntegrityandWire.PreparedTransactionHasher— recompute the hash of a prepared transaction the way Canton does under hashing schemes V2 and V3 and refuse a participant's hash that differs, so both transports fail closed on a prepared transaction whose hash is not the one the signer would sign; any other scheme is refused too. Internal — visible to the transports viaInternalsVisibleTo, not part of the package's public surfaceWire.ExternalPartyTopologyVerifier— decodes the topology transactions a participant generated for an external party and fails closed unless they are exactly the onboarding Canton 3.6 generates for the request: onePartyToParticipantadd-replace at serial 1 forhint::fingerprint, the requested key alone at signing threshold 1, each requested participant with its requested permission plus exactly one generating participant, Canton's confirmation threshold (every confirming participant when the request leaves it at zero), and no field the vendored protos do not describe (each transaction must re-serialize to the bytes received).Wire.TopologyMultiHashrecomputes the multi-hash the caller signs (SHA-256 with hash purpose 11 per transaction, then purpose 55 over the sorted, de-duplicated, length-prefixed hashes), so a multi-hash that covers anything else is refused too. A difference raisesMalformedResponseException. Internal, likeExternalPartyIdentityTopology/Proto/— the Canton topology, crypto and versioned-message protos (com/digitalasset/canton/...), vendored unmodified from Canton tagv3.6.1(commit2fcea1a4826fd63b6a9ea554c13cb685ae8f2ee2,community/base/src/main/protobuf/), withscalapb/scalapb.protofrom ScalaPBv0.11.14, the version Canton builds with. They compile tointernalC# message types only (no gRPC services), so the package's public surface carries no topology API. Bump them together with the Canton line the verifier targetsStreams.StreamEventClassifier— the single implementation of the no-silent-drop stream-classification policy that both the gRPC and HTTP projectors delegate to: which decoded shape becomes whichUnclassifiedKind, the missing-synchronizer rule, and the decode-failure fallback with its warning. Wire decoding stays per-transport; the classifier consumes aDecodedStreamEvent<TSynchronizerScope>and hands back the synchronizer scope it validated, so no projector can reach aSynchronizerIdwithout passing the rule. Internal — visible to the transports viaInternalsVisibleTo, not part of the package's public surfaceStreams.IStreamArms<TEvent, TMarker, TPayload>withContractArms<T>andInterfaceArms<TInterface, TView>— the union constructors for the two stream families, one static builder per arm over transport-neutral values, so each transport's projection core buildsContractStreamEvent<T>orInterfaceStreamEvent<TInterface, TView>directly. Internal, like the classifierStreams.ContractSnapshotEntryArms<T>andInterfaceSnapshotEntryArms<TInterface, TView>— convert a projected stream event into the family's active-contract snapshot entry (Created with its disclosure, Unclassified, and an Unassigned downgraded to an UnclassifiedUnassignedEvent), shared by both transports. InternalIHttpClientFactorynamed client"CantonAuth"— nousingon theHttpClient(factory manages handler lifetime)TimeProviderfor testable time (passFakeTimeProviderin tests)Volatile.Read/Volatile.Writefor cache fields — write order: token before expiry (matches read order)- Validates
expires_in > 0andaccess_tokennon-empty after deserialization - Token endpoint is resolved once at construction — unresolvable options (no
TokenEndpoint, invalidDomain, plaintexthttpwithoutAllowInsecureTokenEndpoint) throwInvalidOperationExceptionwhen the provider is constructed, not at the first token request
Related Packages
Canton.Ledger.Abstractions— transport-neutral contract layer that declaresITokenProviderCanton.Ledger.Grpc.Client— gRPC client that consumesITokenProviderCanton.Ledger.Pqs.Client— PQS query client
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Canton.Ledger.Abstractions (>= 0.7.1)
- Canton.Ledger.Grpc (>= 0.7.1)
- Daml.Runtime (>= 0.7.1)
- Google.Protobuf (>= 3.35.1)
- Microsoft.Extensions.Configuration (>= 10.0.13)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.13)
- Microsoft.Extensions.DependencyInjection (>= 10.0.13)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.13)
- Microsoft.Extensions.Http (>= 10.0.13)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.13)
- Microsoft.Extensions.Options (>= 10.0.13)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.13)
- Microsoft.Extensions.Options.DataAnnotations (>= 10.0.13)
- Polly.Core (>= 8.8.0)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on Canton.Ledger.Kernel:
| Package | Downloads |
|---|---|
|
Canton.Ledger.Grpc.Client
High-level gRPC client for the Canton Ledger API with integration to Daml.Runtime types. |
|
|
Canton.Ledger.Pqs.Client
Typed query client for the Canton Participant Query Store (PQS). Provides expression-based, SQL-injection-safe queries over active Daml contracts via PostgreSQL. |
|
|
Canton.Ledger.Rest.Client
HTTP (JSON Ledger API) client for the Canton Ledger API — a full ILedgerClient / ICantonLedgerClient implementation over the transport-neutral Daml.Ledger.Abstractions interfaces. |
|
|
Canton.Ledger.OpenTelemetry
Opt-in OpenTelemetry wiring for the Canton Ledger API clients. AddCantonLedgerInstrumentation() registers the gRPC, admin, REST, and PQS client ActivitySources plus Npgsql's own "Npgsql" ActivitySource. It reaches those names through Canton.Ledger.Kernel, so tracing one transport never drags in another's client assembly. The only assembly in this repo that depends on the OpenTelemetry SDK — the client libraries themselves emit only BCL System.Diagnostics.Activity. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.7.1 | 60 | 10/23/2026 |
| 0.7.0 | 231 | 10/14/2026 |
| 0.7.0-preview.1 | 421 | 10/13/2026 |
| 0.6.0 | 323 | 10/7/2026 |
| 0.6.0-preview.4 | 173 | 10/7/2026 |
| 0.6.0-preview.3 | 211 | 10/1/2026 |
| 0.6.0-preview.2 | 428 | 9/30/2026 |
| 0.6.0-preview.1 | 222 | 9/28/2026 |
| 0.5.0-preview.3 | 245 | 9/24/2026 |
| 0.5.0-preview.2 | 162 | 9/22/2026 |
| 0.5.0-preview.1 | 273 | 9/8/2026 |
| 0.4.1-preview.1 | 142 | 8/29/2026 |
| 0.4.0-preview.1 | 250 | 7/18/2026 |
| 0.2.0-preview.1 | 136 | 7/6/2026 |