ChronicleOptions Reference
ChronicleOptions is the root configuration class for all Chronicle .NET clients. All properties can be set in code via the configureOptions callback on AddCratisChronicle, and most can be bound directly from appsettings.json under the Cratis:Chronicle section.
There are three options classes that form an inheritance chain, each adding settings relevant to its hosting context:
| Class | Used by | Adds |
|---|---|---|
ChronicleOptions | All clients | Connection, serialization, timeouts, TLS, auth |
ChronicleClientOptions | Worker services, console apps | Event store name, namespace resolver type |
ChronicleAspNetCoreOptions | ASP.NET Core apps | HTTP header–based namespace resolution |
appsettings.json
Section titled “appsettings.json”All bindable settings live under Cratis:Chronicle:
{ "Cratis": { "Chronicle": { "ConnectionString": "chronicle://localhost:35000", "EventStore": "my-store", "EnableEventTypeGenerationValidation": false, "DefaultSinkTypeId": "SQL", "ConnectTimeout": 5, "AutoDiscoverAndRegister": true, "MaxReceiveMessageSize": 104857600, "MaxSendMessageSize": 104857600, "ProgramIdentifier": "my-app", "ClaimsBasedNamespaceResolverClaimType": "tenant_id", "Tls": { "CertificatePath": "/certs/client.pfx", "CertificatePassword": "secret" }, "Authentication": { "Authority": "https://login.example.com" } } }}ChronicleOptions properties
Section titled “ChronicleOptions properties”EnableEventTypeGenerationValidation
Section titled “EnableEventTypeGenerationValidation”Asks the Kernel to enforce strict migration chain validation when registering event types at generation 2 or higher. The value is forwarded as part of the registration request.
| Type | bool |
| Default | false |
Image restriction: This flag is only honoured by the development image of the Kernel. The production image always ignores it and validates unconditionally, regardless of what the client sends. This makes it impossible to accidentally skip migration chain validation in production even if the client has this flag set to
false.
To opt in to strict validation during development (recommended once your event schemas are stable), set the flag to true:
using Microsoft.Extensions.Hosting;
public static class ChronicleOptionsEnableValidationRegistration{ public static void Configure(string[] args) { var builder = Host.CreateApplicationBuilder(args);
builder.AddCratisChronicle(configureOptions: options => { options.EnableEventTypeGenerationValidation = true; }); }}Kotlin does not support this workflow yet.Java does not support this workflow yet.Elixir does not support this workflow yet.TypeScript does not support this workflow yet.In appsettings.json: "EnableEventTypeGenerationValidation": true.
See Generation Validation for the full ruleset.
UnitOfWorkLifecyclePolicy
Section titled “UnitOfWorkLifecyclePolicy”Controls event staging once a unit of work begins completing.
| Type | UnitOfWorkLifecyclePolicy (Compatibility or Strict) |
| Default | Compatibility |
In appsettings.json: "UnitOfWorkLifecyclePolicy": "Strict" under Cratis:Chronicle. Strict rejects late staging; Compatibility keeps legacy enrollment behavior and reports late attempts. Strict will become the default in the next major release. See Staging after completion begins for commit, rollback, and disposal behavior.
ConnectionString
Section titled “ConnectionString”The gRPC endpoint for Chronicle Server.
| Type | ChronicleConnectionString |
| Default | Development connection string (chronicle://localhost:35000 with default dev credentials) |
using Microsoft.Extensions.Hosting;
public static class ChronicleOptionsConnectionStringRegistration{ public static void Configure(string[] args) { var builder = Host.CreateApplicationBuilder(args);
builder.AddCratisChronicle(configureOptions: options => { options.ConnectionString = "chronicle://myserver:35000"; }); }}import io.cratis.chronicle.ChronicleOptionsimport io.cratis.chronicle.connection.ChronicleConnectionString
fun optionsFromConnectionString(): ChronicleOptions = ChronicleOptions(connectionString = ChronicleConnectionString.parse("chronicle://myserver:35000?skipTlsValidation=false"))import io.cratis.chronicle.ChronicleOptions;import io.cratis.chronicle.connection.ChronicleConnectionString;
class ConfigurationChronicleOptionsConnectionString { ChronicleOptions create() { return new ChronicleOptions(ChronicleConnectionString.Companion.parse("chronicle://myserver:35000?skipTlsValidation=false")); }}defmodule MyApp.ChronicleOptionsConnectionStringRegistration do @moduledoc false
def start_link do children = [ {Chronicle.Client, connection_string: "chronicle://myserver:35000"} ]
Supervisor.start_link(children, strategy: :one_for_one) endendimport { ChronicleOptions } from '@cratis/chronicle';
function createChronicleOptionsConnectionString(): ChronicleOptions { return ChronicleOptions.fromConnectionString('chronicle://myserver:35000');}In appsettings.json: "ConnectionString": "chronicle://myserver:35000".
ConnectTimeout
Section titled “ConnectTimeout”Timeout in seconds when establishing a connection to Chronicle Server.
| Type | int |
| Default | 5 |
AutoDiscoverAndRegister
Section titled “AutoDiscoverAndRegister”Controls whether Chronicle automatically discovers event types, projections, reactors, reducers, and other artifacts from loaded assemblies on startup.
| Type | bool |
| Default | true |
Set to false when using a custom IClientArtifactsProvider or when you want full manual control over what gets registered.
SkipCompatibilityCheck
Section titled “SkipCompatibilityCheck”Skips the connect-time wire-compatibility check against the server.
| Type | bool |
| Default | false |
Use this only as a deliberate, temporary escape hatch — for example, when a client that has only gained capabilities since the server’s release is incorrectly refused by a server that cannot be redeployed to pick up a wire-compatibility fix. It does not make an incompatibility disappear; it silences every incompatibility the check would have caught, including a genuine one. The connection string’s skipCompatibilityCheck=true option asks for the same thing; either input is enough. See Server support: Compatibility check for the full explanation.
DefaultSinkTypeId
Section titled “DefaultSinkTypeId”Controls which sink type is used when registering projections and reducers with the Kernel. Chronicle supports two built-in sink types:
| Value | Description |
|---|---|
WellKnownSinkTypes.MongoDB | Stores read models in MongoDB (default) |
WellKnownSinkTypes.SQL | Stores read models in a SQL database as JSON documents |
| Type | SinkTypeId |
| Default | WellKnownSinkTypes.MongoDB |
To use SQL as the storage backend for all read models:
using Microsoft.Extensions.Hosting;using Cratis.Chronicle.Sinks;
public static class ChronicleOptionsSqlSinkRegistration{ public static void Configure(string[] args) { var builder = Host.CreateApplicationBuilder(args);
builder.AddCratisChronicle(configureOptions: options => { options.DefaultSinkTypeId = WellKnownSinkTypes.SQL; }); }}import io.cratis.chronicle.ChronicleOptionsimport io.cratis.chronicle.connection.ChronicleConnectionStringimport io.cratis.chronicle.sinks.WellKnownSinkTypes
fun optionsWithSqlSink(): ChronicleOptions = ChronicleOptions( connectionString = ChronicleConnectionString.DEVELOPMENT, defaultSinkTypeId = WellKnownSinkTypes.SQL)import io.cratis.chronicle.ChronicleOptions;import io.cratis.chronicle.connection.ChronicleConnectionString;import io.cratis.chronicle.sinks.WellKnownSinkTypes;
class ConfigurationChronicleOptionsSqlSink { ChronicleOptions create() { return new ChronicleOptions(ChronicleConnectionString.Companion.getDEVELOPMENT(), "Unknown", WellKnownSinkTypes.SQL); }}defmodule MyApp.ChronicleOptionsSqlSinkRegistration do @moduledoc false
def start_link do children = [ {Chronicle.Client, default_sink_type_id: :sql} ]
Supervisor.start_link(children, strategy: :one_for_one) endendimport { ChronicleOptions, WellKnownSinks } from '@cratis/chronicle';
function createChronicleOptionsSqlSink(): ChronicleOptions { return ChronicleOptions.fromConnectionString('chronicle://localhost:35000', { defaultSinkTypeId: WellKnownSinks.SQL });}In appsettings.json:
{ "Cratis": { "Chronicle": { "DefaultSinkTypeId": "SQL" } } }See Sinks for detailed guidance on storage backends.
MaxReceiveMessageSize / MaxSendMessageSize
Section titled “MaxReceiveMessageSize / MaxSendMessageSize”Maximum gRPC message sizes in bytes. See gRPC Message Size for detailed guidance.
| Type | int? |
| Default | 104857600 (100 MB) |
ProgramIdentifier
Section titled “ProgramIdentifier”A human-readable identifier for the running application. Included in root causation metadata to identify which program appended an event.
| Type | string |
| Default | "[N/A]" |
SoftwareVersion
Section titled “SoftwareVersion”The software version of the entry assembly, extracted from AssemblyInformationalVersion or AssemblyVersion. Included in root causation to track which build produced an event, and sent with every projection registration so the kernel can ignore a registration from an older version than the one it has stored. Set it with the Cratis:Chronicle:SoftwareVersion configuration key (environment variable Cratis__Chronicle__SoftwareVersion). See the registration lifecycle.
| Type | string (semantic version) |
| Default | Resolved from the entry assembly at startup |
SoftwareCommit
Section titled “SoftwareCommit”The commit SHA of the entry assembly, extracted from the +commit part of AssemblyInformationalVersion. Included in root causation to track which commit produced an event.
| Type | string |
| Default | Resolved from the entry assembly at startup |
ClaimsBasedNamespaceResolverClaimType
Section titled “ClaimsBasedNamespaceResolverClaimType”The JWT claim type used by ClaimsBasedNamespaceResolver when namespace resolution is configured via WithClaimsBasedNamespaceResolver.
| Type | string |
| Default | "tenant_id" |
RegistrationRetry
Section titled “RegistrationRetry”How registering the client’s artifacts is retried when the Kernel does not accept them.
| Type | RegistrationRetryOptions |
| Default | Five attempts, two seconds growing to thirty |
| Nested property | Type | Default | Description |
|---|---|---|---|
MaxAttempts | int | 5 | How many times registering all artifacts is attempted before the failure surfaces. 1 disables retrying |
InitialDelay | TimeSpan | 00:00:02 | Wait after the first failed attempt. Each further attempt doubles it, jittered |
MaximumDelay | TimeSpan | 00:00:30 | Upper bound for the wait between attempts |
Registration runs on the way up, so a registration that fails takes the host with it. A Kernel that is merely busy — catching a newly added read model up over a large event log, say — answers late rather than wrongly, and a host that dies on that answer comes back and asks again, adding its whole registration to the queue it was already waiting on. Retrying in place lets the client wait out a busy Kernel instead. Registration is idempotent, so a retry costs the Kernel only the comparison it already does; a Kernel that is genuinely refusing the definitions keeps failing and the host still ends up down, just after the attempts are spent rather than on the first.
In appsettings.json:
{ "Cratis": { "Chronicle": { "RegistrationRetry": { "MaxAttempts": 10, "MaximumDelay": "00:01:00" } } } }Reactors
Section titled “Reactors”How the .NET client runs reactors.
| Nested property | Type | Default | Description |
|---|---|---|---|
MaxConcurrentPartitions | int | processor count, at most 8 | How many partitions of one reactor are handled at the same time. Batches for one partition always run in order. 1 handles one batch at a time, as before. Must be at least 1 |
Handlers for different partitions can run at the same time, so shared non-thread-safe services are at risk.
{ "Cratis": { "Chronicle": { "Reactors": { "MaxConcurrentPartitions": 4 } } } }Reducers
Section titled “Reducers”How the .NET client runs reducers.
| Nested property | Type | Default | Description |
|---|---|---|---|
MaxConcurrentPartitions | int | processor count, at most 8 | How many partitions of one reducer are handled at the same time. Batches for one partition always run in order. 1 handles one batch at a time, as before. Must be at least 1 |
{ "Cratis": { "Chronicle": { "Reducers": { "MaxConcurrentPartitions": 4 } } } }ArtifactIdentityDrift
Section titled “ArtifactIdentityDrift”Controls the .NET client’s check for likely artifact renames or moves before each foreground registration run. Background retries retain its findings without repeating snapshots or drift logs.
| Type | ArtifactIdentityDriftPolicy |
| Default | Warn |
| Values | Warn, Fail, Ignore |
Warn logs findings and continues registering. Fail throws ArtifactIdentityDriftDetected before any artifacts are registered when Warning-level reactor or event type findings exist, without retrying. Information-level reducer and projection findings never fail registration. Ignore skips the check and its kernel calls. Snapshot failures never prevent registration, even with Fail; a failed subscription confirmation omits that reactor or reducer candidate. Projections skip confirmation because the kernel keeps them subscribed until registration retires them.
In appsettings.json:
{ "Cratis": { "Chronicle": { "ArtifactIdentityDrift": "Fail" } } }Use Fail in CI or staging to investigate likely identity changes before deploying. This is a heuristic: rolling deployments can hide reactor or reducer renames, and unrelated same-named observers or same-shaped events can match. Projections are more prone to false positives because subscribed candidates are included; their findings remain informational. Event types cannot be removed to acknowledge a finding; preserve the old id or switch to Warn after reviewing an intentional new identity. See Discovery report and identity drift for findings, log levels, and limitations.
TLS certificate settings for the gRPC connection. TLS is active when both CertificatePath and CertificatePassword are set. See TLS for full details.
| Type | Tls |
| Default | TLS disabled (no certificate configured) |
| Nested property | Type | Description |
|---|---|---|
CertificatePath | string? | Path to a PFX certificate file |
CertificatePassword | string? | Password for the certificate file |
Occurrences
Section titled “Occurrences”Kernel delivery settings under Cratis:Chronicle:Occurrences. These settings do not change observer retry policy.
| Property | Type | Default | Behavior |
|---|---|---|---|
MaxInFlight | int | 1 | Maximum live leases per trigger and scope; values below one use one |
MaxAttempts | int | 10 | Quarantines exhausted work; zero permits unlimited attempts |
BackoffDelay | int | 1 | Initial failure retry delay in seconds; negative values use zero |
ExponentialBackoffDelayFactor | float | 2 | Multiplier per failed attempt; values below one use one |
MaximumBackoffDelay | int | 600 | Failure retry delay cap in seconds; negative values use zero |
Lease duration is selected by the claim or renew request, not this section: zero or a negative value uses 60 seconds, and the maximum is 600 seconds. Claims return at most 1,000 occurrences and remain subject to MaxInFlight. Kernel time controls expiry and backoff. See occurrence delivery and receipts for ordering, quarantine and recovery.
Authentication
Section titled “Authentication”OAuth/OIDC authority configuration. When Authority is not set, the Chronicle internal OAuth authority is used.
| Type | Authentication |
| Default | Internal authority |
| Nested property | Type | Description |
|---|---|---|
Authority | string? | OAuth/OIDC authority URL |
ChronicleClientOptions properties
Section titled “ChronicleClientOptions properties”ChronicleClientOptions extends ChronicleOptions and is used by worker services, console apps, and other non-web hosts via IHostApplicationBuilder.AddCratisChronicle.
EventStore
Section titled “EventStore”The name of the event store the client connects to. Required.
| Type | EventStoreName |
| Default | (none — must be set) |
{ "Cratis": { "Chronicle": { "EventStore": "my-store" } } }EventStoreNamespaceResolverType
Section titled “EventStoreNamespaceResolverType”The Type of the IEventStoreNamespaceResolver to use for resolving the event store namespace. When null, defaults to DefaultEventStoreNamespaceResolver at runtime, which always returns the default namespace.
| Type | Type? |
| Default | null → DefaultEventStoreNamespaceResolver |
This is a structural dependency — it resolves a type reference and cannot be meaningfully set from appsettings.json. Set it in code via the configureOptions callback or via IChronicleBuilder.
ChronicleAspNetCoreOptions properties
Section titled “ChronicleAspNetCoreOptions properties”ChronicleAspNetCoreOptions extends ChronicleClientOptions and is used by ASP.NET Core applications via WebApplicationBuilder.AddCratisChronicle. It defaults EventStoreNamespaceResolverType to HttpHeaderEventStoreNamespaceResolver.
NamespaceHttpHeader
Section titled “NamespaceHttpHeader”The HTTP request header used to resolve the event store namespace on a per-request basis.
| Type | string |
| Default | "x-cratis-tenant-id" |
{ "Cratis": { "Chronicle": { "NamespaceHttpHeader": "x-my-tenant" } } }Webhooks (Kernel only)
Section titled “Webhooks (Kernel only)”The Kernel’s Cratis.Chronicle.Configuration.ChronicleOptions.Webhooks is separate from the client
options above. Set these keys under webhooks in the Kernel’s chronicle.json, or under
Cratis:Chronicle:Webhooks in application configuration. Environment variable equivalents use
Cratis__Chronicle__Webhooks__ followed by the key.
| Key | Default | Purpose |
|---|---|---|
Validation | Warn | Log violations during rollout; Enforce rejects them. Enforce is planned as the next major release’s default. |
AllowInsecureHttp | false | Permit HTTP in Development. Ignored with a startup warning outside Development. Loopback HTTP is automatically permitted in Development. |
AllowedHosts | [] | Exact receiver/OAuth hosts or wildcard subdomains such as *.example.com. In Enforce mode unlisted hosts are rejected; approving a host also permits its private addresses. |
EventStoreAllowedHosts | {} | Map of event-store names to host arrays. An entry replaces the global array, including when empty. Requests without an event-store option use the global list. |
TestTimeoutSeconds | 30 | Timeout for the registration reachability probe. |
Delivery retry policy
Section titled “Delivery retry policy”Webhook delivery uses one HTTP attempt at a time. The observer owns retries, a finite
retry budget, and durable deadlines; the HTTP resilience pipeline has no retry strategy.
RetryDelaySeconds is retained for compatibility but no longer controls delivery retries.
Webhook retry policy is configured under Webhooks:Retry, independently of the global
observer retry settings. Non-webhook observers
retain their existing retry and quarantine defaults.
Webhooks:Retry key | Default | Purpose |
|---|---|---|
BackoffScheduleSeconds | [1, 5, 30, 120, 600, 1800, 3600] | Delays for successive failures; the last entry repeats. Setting it replaces the default schedule. |
MaximumBackoffDelay | 3600 | Caps the delay in seconds, including bounded ±20% jitter. |
MaxRetryAttempts | 0 | No attempt-count limit; a positive value adds a limit alongside the elapsed budget. |
RetryBudget | "1.00:00:00" | Elapsed budget from the first persisted failure since quarantine was last cleared. Zero disables this limit. |
QuarantineOnFailedPartitionCount | 5 | Pause the observer when this many partitions are parked. Zero disables this threshold. |
QuarantineOnFailedPartitionPercentage | 0.5 | Pause at or above this fraction of distinct partitions in failure/resolution history. Zero disables this threshold. |
Either exhausted retry limit parks the partition. Schedule deadlines and the first failure’s timestamp survive reactivation; jitter is not resampled. Valid receiver/circuit deferrals do not consume attempts or count toward quarantine. There is no automatic quarantined-observer probe scheduler yet; recovery requires operator action.
MaximumRetryAfterSeconds bounds receiver-provided Retry-After delays on 429 and
503 responses. Its default is 86400 seconds (24 hours). Invalid or expired headers
fall back to the retry budget, without sleeping in the HTTP pipeline. A valid deferral
and an open-circuit rejection do not consume retry attempts. See
webhook delivery policy
for configuration-error pauses, immediate 410 parking, and manual recovery.
For example, Cratis__Chronicle__Webhooks__Validation=Enforce enables blocking, and
Cratis__Chronicle__Webhooks__AllowedHosts__0=hooks.example.com approves one host. OAuth authorities
and discovered token endpoint hosts must also be approved. Cloud metadata addresses need explicit
approval even in Warn. Redirects are rejected in both modes.
See Webhook target validation for rollout, Docker/tunnel examples, DNS/address restrictions, and the distinction between warnings and blocking.