---
title: Seeding with C Sharp
editUrl: https://github.com/Cratis/Chronicle/edit/main/Documentation/event-seeding/seeding-with-csharp.mdx
description: 'This page shows how to seed events using the Chronicle .NET client. Choose the scope before defining your seeds: global seeds reach every namespace in the…'
---

import { Tabs, TabItem } from '@astrojs/starlight/components';

This page shows how to seed events using the Chronicle .NET client. Choose the scope before defining your seeds:
global seeds reach every namespace in the event store, including tenants created later. Use `ForNamespace(...)`
for development or sample data that belongs only in a named namespace.

## Define events

Use `record` types for event definitions in examples:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Events;

[EventType]
public record EvtSeedingUserRegistered(string Email, string DisplayName);

[EventType]
public record EvtSeedingEmailVerified(string Email);

[EventType]
public record EvtSeedingProfileUpdated(string DisplayName);

[EventType]
public record EvtSeedingOrderPlaced(string UserId, decimal Amount);
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/events.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.events.EventType

@EventType
data class EvtSeedingUserRegistered(val email: String, val displayName: String)

@EventType
data class EvtSeedingEmailVerified(val email: String)

@EventType
data class EvtSeedingProfileUpdated(val displayName: String)

@EventType
data class EvtSeedingOrderPlaced(val userId: String, val amount: Double)
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/events.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.events.EventType;

@EventType
record EvtSeedingUserRegistered(String email, String displayName) {}

@EventType
record EvtSeedingEmailVerified(String email) {}

@EventType
record EvtSeedingProfileUpdated(String displayName) {}

@EventType
record EvtSeedingOrderPlaced(String userId, double amount) {}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/event-seeding/seeding-with-csharp/events.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.Events.EvtSeedingUserRegistered do
  use Chronicle.Events.EventType, id: "evt-seeding-user-registered"

  defstruct [:email, :display_name]
end

defmodule MyApp.Events.EvtSeedingEmailVerified do
  use Chronicle.Events.EventType, id: "evt-seeding-email-verified"

  defstruct [:email]
end

defmodule MyApp.Events.EvtSeedingProfileUpdated do
  use Chronicle.Events.EventType, id: "evt-seeding-profile-updated"

  defstruct [:display_name]
end

defmodule MyApp.Events.EvtSeedingOrderPlaced do
  use Chronicle.Events.EventType, id: "evt-seeding-order-placed"

  defstruct [:user_id, :amount]
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/events.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { eventType } from '@cratis/chronicle';
import { field } from '@cratis/fundamentals';

@eventType()
class EvtSeedingUserRegistered {
    @field(String) readonly email: string;
    @field(String) readonly displayName: string;

    constructor(email: string, displayName: string) {
        this.email = email;
        this.displayName = displayName;
    }
}

@eventType()
class EvtSeedingEmailVerified {
    @field(String) readonly email: string;

    constructor(email: string) {
        this.email = email;
    }
}

@eventType()
class EvtSeedingProfileUpdated {
    @field(String) readonly displayName: string;

    constructor(displayName: string) {
        this.displayName = displayName;
    }
}

@eventType()
class EvtSeedingOrderPlaced {
    @field(String) readonly userId: string;
    @field(Number) readonly amount: number;

    constructor(userId: string, amount: number) {
        this.userId = userId;
        this.amount = amount;
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/events.md)

</TabItem>
</Tabs>

## Implement a seeder

Implement `ICanSeedEvents` and use `IEventSeedingBuilder` to define events to append:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Seeding;

public sealed class EvtSeedingUserSeeding : ICanSeedEvents
{
    public void Seed(IEventSeedingBuilder builder)
    {
        builder
            .For<EvtSeedingUserRegistered>("user-123", [
                new("john@example.com", "John")
            ])
            .ForEventSource("user-456", [
                new EvtSeedingUserRegistered("jane@example.com", "Jane"),
                new EvtSeedingEmailVerified("jane@example.com")
            ]);
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seeder.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.seeding.ICanSeedEvents
import io.cratis.chronicle.seeding.IEventSeedingBuilder
import io.cratis.chronicle.seeding.Seeder

@Seeder
class EvtSeedingUserSeeding : ICanSeedEvents {
    override fun seed(builder: IEventSeedingBuilder) {
        builder
            .forEventType(
                EvtSeedingUserRegistered::class,
                "user-123",
                listOf(EvtSeedingUserRegistered("john@example.com", "John"))
            )
            .forEventSource(
                "user-456",
                listOf(
                    EvtSeedingUserRegistered("jane@example.com", "Jane"),
                    EvtSeedingEmailVerified("jane@example.com")
                )
            )
    }
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seeder.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.seeding.ICanSeedEvents;
import io.cratis.chronicle.seeding.IEventSeedingBuilder;
import io.cratis.chronicle.seeding.Seeder;

import io.cratis.chronicle.java.EventSeedingBuilderJavaBridge;

import java.util.List;

@Seeder
class EvtSeedingUserSeeding implements ICanSeedEvents {
    @Override
    public void seed(IEventSeedingBuilder builder) {
        EventSeedingBuilderJavaBridge.forEventType(
            builder,
            EvtSeedingUserRegistered.class,
            "user-123",
            List.of(new EvtSeedingUserRegistered("john@example.com", "John")));

        builder.forEventSource(
            "user-456",
            List.of(
                new EvtSeedingUserRegistered("jane@example.com", "Jane"),
                new EvtSeedingEmailVerified("jane@example.com")));
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/event-seeding/seeding-with-csharp/seeder.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.Events.EvtSeedingSeederUserRegistered do
  use Chronicle.Events.EventType, id: "evt-seeding-seeder-user-registered"

  defstruct [:email, :display_name]
end

defmodule MyApp.Events.EvtSeedingSeederEmailVerified do
  use Chronicle.Events.EventType, id: "evt-seeding-seeder-email-verified"

  defstruct [:email]
end

defmodule MyApp.Seeders.EvtSeedingUserSeeding do
  use Chronicle.Seeding.Seeder

  alias MyApp.Events.{EvtSeedingSeederEmailVerified, EvtSeedingSeederUserRegistered}

  @impl true
  def seed(builder) do
    builder
    |> Chronicle.Seeding.for(EvtSeedingSeederUserRegistered, "user-123", [
      %EvtSeedingSeederUserRegistered{email: "john@example.com", display_name: "John"}
    ])
    |> Chronicle.Seeding.for_event_source("user-456", [
      %EvtSeedingSeederUserRegistered{email: "jane@example.com", display_name: "Jane"},
      %EvtSeedingSeederEmailVerified{email: "jane@example.com"}
    ])
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seeder.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { ICanSeedEvents, IEventSeedingBuilder, seeder } from '@cratis/chronicle';

@seeder()
class EvtSeedingUserSeeding implements ICanSeedEvents {
    seed(builder: IEventSeedingBuilder): void {
        builder
            .for('user-123', [new EvtSeedingUserRegistered('john@example.com', 'John')])
            .forEventSource('user-456', [
                new EvtSeedingUserRegistered('jane@example.com', 'Jane'),
                new EvtSeedingEmailVerified('jane@example.com')
            ]);
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seeder.md)

</TabItem>
</Tabs>

## Seed multiple events of the same type

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Seeding;

public sealed class EvtSeedingMultipleSameTypeSeeding : ICanSeedEvents
{
    public void Seed(IEventSeedingBuilder builder)
    {
        builder.For<EvtSeedingUserRegistered>("user-123", [
            new("john@example.com", "John"),
            new("jane@example.com", "Jane")
        ]);
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seed-multiple-same-type.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.seeding.ICanSeedEvents
import io.cratis.chronicle.seeding.IEventSeedingBuilder
import io.cratis.chronicle.seeding.Seeder

@Seeder
class EvtSeedingMultipleSameTypeSeeding : ICanSeedEvents {
    override fun seed(builder: IEventSeedingBuilder) {
        builder.forEventType(
            EvtSeedingUserRegistered::class,
            "user-123",
            listOf(
                EvtSeedingUserRegistered("john@example.com", "John"),
                EvtSeedingUserRegistered("jane@example.com", "Jane")
            )
        )
    }
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seed-multiple-same-type.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.seeding.ICanSeedEvents;
import io.cratis.chronicle.seeding.IEventSeedingBuilder;
import io.cratis.chronicle.seeding.Seeder;

import io.cratis.chronicle.java.EventSeedingBuilderJavaBridge;

import java.util.List;

@Seeder
class EvtSeedingMultipleSameTypeSeeding implements ICanSeedEvents {
    @Override
    public void seed(IEventSeedingBuilder builder) {
        EventSeedingBuilderJavaBridge.forEventType(
            builder,
            EvtSeedingUserRegistered.class,
            "user-123",
            List.of(
                new EvtSeedingUserRegistered("john@example.com", "John"),
                new EvtSeedingUserRegistered("jane@example.com", "Jane")));
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/event-seeding/seeding-with-csharp/seed-multiple-same-type.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.Events.EvtSeedingMultiSameTypeUserRegistered do
  use Chronicle.Events.EventType, id: "evt-seeding-multi-same-type-user-registered"

  defstruct [:email, :display_name]
end

defmodule MyApp.Seeders.EvtSeedingMultipleSameTypeSeeding do
  use Chronicle.Seeding.Seeder

  alias MyApp.Events.EvtSeedingMultiSameTypeUserRegistered

  @impl true
  def seed(builder) do
    Chronicle.Seeding.for(builder, EvtSeedingMultiSameTypeUserRegistered, "user-123", [
      %EvtSeedingMultiSameTypeUserRegistered{email: "john@example.com", display_name: "John"},
      %EvtSeedingMultiSameTypeUserRegistered{email: "jane@example.com", display_name: "Jane"}
    ])
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seed-multiple-same-type.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { ICanSeedEvents, IEventSeedingBuilder, seeder } from '@cratis/chronicle';

@seeder()
class EvtSeedingMultipleSameTypeSeeding implements ICanSeedEvents {
    seed(builder: IEventSeedingBuilder): void {
        builder.for('user-123', [
            new EvtSeedingUserRegistered('john@example.com', 'John'),
            new EvtSeedingUserRegistered('jane@example.com', 'Jane')
        ]);
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seed-multiple-same-type.md)

</TabItem>
</Tabs>

## Seed mixed event types

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Seeding;

public sealed class EvtSeedingMixedTypesSeeding : ICanSeedEvents
{
    public void Seed(IEventSeedingBuilder builder)
    {
        builder.ForEventSource("user-123", [
            new EvtSeedingUserRegistered("john@example.com", "John"),
            new EvtSeedingEmailVerified("john@example.com"),
            new EvtSeedingProfileUpdated("John Doe")
        ]);
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seed-mixed-types.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.seeding.ICanSeedEvents
import io.cratis.chronicle.seeding.IEventSeedingBuilder
import io.cratis.chronicle.seeding.Seeder

@Seeder
class EvtSeedingMixedTypesSeeding : ICanSeedEvents {
    override fun seed(builder: IEventSeedingBuilder) {
        builder.forEventSource(
            "user-123",
            listOf(
                EvtSeedingUserRegistered("john@example.com", "John"),
                EvtSeedingEmailVerified("john@example.com"),
                EvtSeedingProfileUpdated("John Doe")
            )
        )
    }
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seed-mixed-types.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.seeding.ICanSeedEvents;
import io.cratis.chronicle.seeding.IEventSeedingBuilder;
import io.cratis.chronicle.seeding.Seeder;

import java.util.List;

@Seeder
class EvtSeedingMixedTypesSeeding implements ICanSeedEvents {
    @Override
    public void seed(IEventSeedingBuilder builder) {
        builder.forEventSource(
            "user-123",
            List.of(
                new EvtSeedingUserRegistered("john@example.com", "John"),
                new EvtSeedingEmailVerified("john@example.com"),
                new EvtSeedingProfileUpdated("John Doe")));
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/event-seeding/seeding-with-csharp/seed-mixed-types.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.Events.EvtSeedingMixedTypesUserRegistered do
  use Chronicle.Events.EventType, id: "evt-seeding-mixed-types-user-registered"

  defstruct [:email, :display_name]
end

defmodule MyApp.Events.EvtSeedingMixedTypesEmailVerified do
  use Chronicle.Events.EventType, id: "evt-seeding-mixed-types-email-verified"

  defstruct [:email]
end

defmodule MyApp.Events.EvtSeedingMixedTypesProfileUpdated do
  use Chronicle.Events.EventType, id: "evt-seeding-mixed-types-profile-updated"

  defstruct [:display_name]
end

defmodule MyApp.Seeders.EvtSeedingMixedTypesSeeding do
  use Chronicle.Seeding.Seeder

  alias MyApp.Events.{
    EvtSeedingMixedTypesEmailVerified,
    EvtSeedingMixedTypesProfileUpdated,
    EvtSeedingMixedTypesUserRegistered
  }

  @impl true
  def seed(builder) do
    Chronicle.Seeding.for_event_source(builder, "user-123", [
      %EvtSeedingMixedTypesUserRegistered{email: "john@example.com", display_name: "John"},
      %EvtSeedingMixedTypesEmailVerified{email: "john@example.com"},
      %EvtSeedingMixedTypesProfileUpdated{display_name: "John Doe"}
    ])
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seed-mixed-types.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { ICanSeedEvents, IEventSeedingBuilder, seeder } from '@cratis/chronicle';

@seeder()
class EvtSeedingMixedTypesSeeding implements ICanSeedEvents {
    seed(builder: IEventSeedingBuilder): void {
        builder.forEventSource('user-123', [
            new EvtSeedingUserRegistered('john@example.com', 'John'),
            new EvtSeedingEmailVerified('john@example.com'),
            new EvtSeedingProfileUpdated('John Doe')
        ]);
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/seed-mixed-types.md)

</TabItem>
</Tabs>

## Seed onto an event stream

Pass an event stream type and identifier to `For` or `ForEventSource` to route seeds onto a
specific stream. The optional event source type defaults to `Default`. These overloads also
work on the builder returned by `ForNamespace`.

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Seeding;

public sealed class EvtStreamRoutedSeeder : ICanSeedEvents
{
    public void Seed(IEventSeedingBuilder builder)
    {
        builder.For("company-123", "Offices", "office-1", [
            new EvtStreamRoutedOfficeOpened("Oslo")
        ], "Company");

        builder.ForNamespace("tenant-a").ForEventSource(
            "company-123", "Offices", "office-2", [
                new EvtStreamRoutedOfficeOpened("Bergen")
            ], "Company");
    }
}

[EventType]
public record EvtStreamRoutedOfficeOpened(string City);
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/stream-routed.md)

</TabItem>
</Tabs>

Use `ForEvents` for per-event routing. Its `EventForEventSourceId` entries can specify an
`EventSource` definition type and an `EventStream` name; Chronicle resolves their routing
from the discovered definition and rejects contradictory explicit types. Entry tags are
combined with static event-type tags. Set `EventSource` whenever you use a typed `EventStream`:
without `EventSource`, the stream name is ignored and only the explicit `EventStreamType`
and `EventStreamId` determine stream routing. `ForEvents` uses the wrapper's routing and tags,
not its append-specific occurred time, subject, causation or structured named tags.

Routing is part of seed identity: identical payloads on different streams are separate seed
entries. Omitting routing preserves the defaults used by previously stored seed definitions,
so adopting this API does not append legacy seeds again.

Non-default routing requires a kernel that confirms seeding routing support. Against an
older kernel, `Register()` throws `EventSeedingRoutingNotSupported` before sending any
seed entries, including unrouted entries in the same buffer. Upgrade the kernel before
retrying that buffer. A buffer containing only default routing still registers against
older kernels.

## Development-only seeding

If seed data should only run in development, use conditional compilation or runtime configuration:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle;
using Cratis.Chronicle.Seeding;

#if DEBUG
public sealed class EvtSeedingDevelopmentSeeding : ICanSeedEvents
{
    public void Seed(IEventSeedingBuilder builder)
    {
        builder.ForNamespace(EventStoreNamespaceName.Default).For<EvtSeedingUserRegistered>("dev-user-1", [
            new("dev@example.com", "Dev User")
        ]);
    }
}
#endif
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/development-only.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.seeding.ICanSeedEvents
import io.cratis.chronicle.seeding.IEventSeedingBuilder
import io.cratis.chronicle.seeding.Seeder

// Kotlin has no equivalent of C#'s #if DEBUG - gate seed data with a runtime check instead,
// such as an environment variable or an application profile flag.
@Seeder
class EvtSeedingDevelopmentSeeding : ICanSeedEvents {
    override fun seed(builder: IEventSeedingBuilder) {
        if (System.getenv("APP_ENVIRONMENT") != "Development") return

        builder.forEventType(
            EvtSeedingUserRegistered::class,
            "dev-user-1",
            listOf(EvtSeedingUserRegistered("dev@example.com", "Dev User"))
        )
    }
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/development-only.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.seeding.ICanSeedEvents;
import io.cratis.chronicle.seeding.IEventSeedingBuilder;
import io.cratis.chronicle.seeding.Seeder;

import io.cratis.chronicle.java.EventSeedingBuilderJavaBridge;

import java.util.List;

// Java has no equivalent of C#'s #if DEBUG - gate seed data with a runtime check instead,
// such as an environment variable or an application profile flag.
@Seeder
class EvtSeedingDevelopmentSeeding implements ICanSeedEvents {
    @Override
    public void seed(IEventSeedingBuilder builder) {
        if (!"Development".equals(System.getenv("APP_ENVIRONMENT"))) {
            return;
        }

        EventSeedingBuilderJavaBridge.forEventType(
            builder,
            EvtSeedingUserRegistered.class,
            "dev-user-1",
            List.of(new EvtSeedingUserRegistered("dev@example.com", "Dev User")));
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/event-seeding/seeding-with-csharp/development-only.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.Events.EvtSeedingDevUserRegistered do
  use Chronicle.Events.EventType, id: "evt-seeding-dev-user-registered"

  defstruct [:email, :display_name]
end

defmodule MyApp.Seeders.EvtSeedingDevelopmentSeeding do
  use Chronicle.Seeding.Seeder

  alias MyApp.Events.EvtSeedingDevUserRegistered

  @impl true
  def seed(builder) do
    Chronicle.Seeding.for(builder, EvtSeedingDevUserRegistered, "dev-user-1", [
      %EvtSeedingDevUserRegistered{email: "dev@example.com", display_name: "Dev User"}
    ])
  end
end

defmodule MyApp.EvtSeedingApplication do
  use Application

  # Chronicle doesn't distinguish between development and production seed
  # data — decide when to seed based on build configuration or runtime
  # settings, resolved once at compile time here via Mix.env/0.
  @dev_seeders (if Mix.env() == :dev do
                  [MyApp.Seeders.EvtSeedingDevelopmentSeeding]
                else
                  []
                end)

  @impl true
  def start(_type, _args) do
    children = [
      {Chronicle.Client,
       connection_string: "chronicle://localhost:35000",
       event_store: "my-store",
       seeders: @dev_seeders}
    ]

    Supervisor.start_link(children, strategy: :one_for_one)
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/development-only.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { ICanSeedEvents, IEventSeedingBuilder, seeder } from '@cratis/chronicle';

// TypeScript has no build-time equivalent of C#'s #if DEBUG: import this seeder's module only
// in development, and exclude it from ChronicleOptions.discoveryPatterns in production.
@seeder()
class EvtSeedingDevelopmentSeeding implements ICanSeedEvents {
    seed(builder: IEventSeedingBuilder): void {
        builder.for('dev-user-1', [new EvtSeedingUserRegistered('dev@example.com', 'Dev User')]);
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/development-only.md)

</TabItem>
</Tabs>

Chronicle does not distinguish between development and production seed data. Decide when to seed based on build configuration or runtime settings,
and scope development or sample data with `ForNamespace(EventStoreNamespaceName.Default)` rather than the global builder.
A build flag prevents declaring those seeds in that build. With an upgraded .NET client and kernel,
startup replaces the program's stored declaration, even when its global set is empty.

## Organize seeders by feature

For larger solutions, split seeders by domain or feature:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Seeding;

public sealed class EvtSeedingUserFeatureSeeding : ICanSeedEvents
{
    public void Seed(IEventSeedingBuilder builder)
    {
        builder.For<EvtSeedingUserRegistered>("test-user-1", [
            new("test1@example.com", "Test User 1")
        ]);
    }
}

public sealed class EvtSeedingOrderFeatureSeeding : ICanSeedEvents
{
    public void Seed(IEventSeedingBuilder builder)
    {
        builder.For<EvtSeedingOrderPlaced>("test-order-1", [
            new("test-user-1", 100.00m)
        ]);
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/organize-by-feature.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.seeding.ICanSeedEvents
import io.cratis.chronicle.seeding.IEventSeedingBuilder
import io.cratis.chronicle.seeding.Seeder

@Seeder
class EvtSeedingUserFeatureSeeding : ICanSeedEvents {
    override fun seed(builder: IEventSeedingBuilder) {
        builder.forEventType(
            EvtSeedingUserRegistered::class,
            "test-user-1",
            listOf(EvtSeedingUserRegistered("test1@example.com", "Test User 1"))
        )
    }
}

@Seeder
class EvtSeedingOrderFeatureSeeding : ICanSeedEvents {
    override fun seed(builder: IEventSeedingBuilder) {
        builder.forEventType(
            EvtSeedingOrderPlaced::class,
            "test-order-1",
            listOf(EvtSeedingOrderPlaced("test-user-1", 100.00))
        )
    }
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/organize-by-feature.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.seeding.ICanSeedEvents;
import io.cratis.chronicle.seeding.IEventSeedingBuilder;
import io.cratis.chronicle.seeding.Seeder;

import io.cratis.chronicle.java.EventSeedingBuilderJavaBridge;

import java.util.List;

@Seeder
class EvtSeedingUserFeatureSeeding implements ICanSeedEvents {
    @Override
    public void seed(IEventSeedingBuilder builder) {
        EventSeedingBuilderJavaBridge.forEventType(
            builder,
            EvtSeedingUserRegistered.class,
            "test-user-1",
            List.of(new EvtSeedingUserRegistered("test1@example.com", "Test User 1")));
    }
}

@Seeder
class EvtSeedingOrderFeatureSeeding implements ICanSeedEvents {
    @Override
    public void seed(IEventSeedingBuilder builder) {
        EventSeedingBuilderJavaBridge.forEventType(
            builder,
            EvtSeedingOrderPlaced.class,
            "test-order-1",
            List.of(new EvtSeedingOrderPlaced("test-user-1", 100.00)));
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/event-seeding/seeding-with-csharp/organize-by-feature.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.Events.EvtSeedingFeatureUserRegistered do
  use Chronicle.Events.EventType, id: "evt-seeding-feature-user-registered"

  defstruct [:email, :display_name]
end

defmodule MyApp.Events.EvtSeedingFeatureOrderPlaced do
  use Chronicle.Events.EventType, id: "evt-seeding-feature-order-placed"

  defstruct [:user_id, :amount]
end

defmodule MyApp.Seeders.EvtSeedingUserFeatureSeeding do
  use Chronicle.Seeding.Seeder

  alias MyApp.Events.EvtSeedingFeatureUserRegistered

  @impl true
  def seed(builder) do
    Chronicle.Seeding.for(builder, EvtSeedingFeatureUserRegistered, "test-user-1", [
      %EvtSeedingFeatureUserRegistered{email: "test1@example.com", display_name: "Test User 1"}
    ])
  end
end

defmodule MyApp.Seeders.EvtSeedingOrderFeatureSeeding do
  use Chronicle.Seeding.Seeder

  alias MyApp.Events.EvtSeedingFeatureOrderPlaced

  @impl true
  def seed(builder) do
    Chronicle.Seeding.for(builder, EvtSeedingFeatureOrderPlaced, "test-order-1", [
      %EvtSeedingFeatureOrderPlaced{user_id: "test-user-1", amount: 100.00}
    ])
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/organize-by-feature.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { ICanSeedEvents, IEventSeedingBuilder, seeder } from '@cratis/chronicle';

@seeder()
class EvtSeedingUserFeatureSeeding implements ICanSeedEvents {
    seed(builder: IEventSeedingBuilder): void {
        builder.for('test-user-1', [new EvtSeedingUserRegistered('test1@example.com', 'Test User 1')]);
    }
}

@seeder()
class EvtSeedingOrderFeatureSeeding implements ICanSeedEvents {
    seed(builder: IEventSeedingBuilder): void {
        builder.for('test-order-1', [new EvtSeedingOrderPlaced('test-user-1', 100.0)]);
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/organize-by-feature.md)

</TabItem>
</Tabs>

## Namespace-scoped seed data

`For` and `ForEventSource` on the top-level builder define global seeds. The server stores them per event store,
applies them to every existing namespace, and applies them once to each namespace created later.
Opening a store for a new namespace creates that namespace. The kernel then applies stored global seeds asynchronously,
usually before your application's first write, but there is no ordering guarantee. Do not rely on that timing;
put development or sample data in `ForNamespace(...)` rather than the global builder.

To target a specific namespace, use `ForNamespace` to get a scoped builder:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Seeding;

[EventType]
public record EvtSeedingProductCreated(string Name, decimal Price);

[EventType]
public record EvtSeedingOrganizationCreated(string Name);

[EventType]
public record EvtSeedingBillingSetUp(string BillingEmail);

public sealed class EvtSeedingTenantSeeding : ICanSeedEvents
{
    public void Seed(IEventSeedingBuilder builder)
    {
        // Global seed data — applied to every namespace
        builder.For<EvtSeedingProductCreated>("product-1", [
            new("Laptop", 1299.00m)
        ]);

        // Namespace-scoped seed data — applied only to the "acme" namespace
        builder.ForNamespace("acme")
            .For<EvtSeedingUserRegistered>("user-1", [
                new("admin@acme.com", "Acme Admin")
            ]);

        // A second namespace with different seed data
        builder.ForNamespace("contoso")
            .For<EvtSeedingUserRegistered>("user-1", [
                new("admin@contoso.com", "Contoso Admin")
            ])
            .ForEventSource("org-1", [
                new EvtSeedingOrganizationCreated("Contoso"),
                new EvtSeedingBillingSetUp("contoso@billing.com")
            ]);
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/namespace-scoped.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
// Requires io.cratis:chronicle 6.5.0 or later.
import io.cratis.chronicle.events.EventType
import io.cratis.chronicle.seeding.ICanSeedEvents
import io.cratis.chronicle.seeding.IEventSeedingBuilder
import io.cratis.chronicle.seeding.Seeder

@EventType
data class EvtSeedingProductCreated(val name: String, val price: Double)

@EventType
data class EvtSeedingOrganizationCreated(val name: String)

@EventType
data class EvtSeedingBillingSetUp(val billingEmail: String)

@Seeder
class EvtSeedingTenantSeeding : ICanSeedEvents {
    override fun seed(builder: IEventSeedingBuilder) {
        // Unscoped seed data is sent as global seed data and lands in the namespaces the
        // event store has. For a namespace created later, scope the data with forNamespace.
        builder.forEventType(
            EvtSeedingProductCreated::class,
            "product-1",
            listOf(EvtSeedingProductCreated("Laptop", 1299.00))
        )

        // Namespace-scoped seed data - applied only to the "acme" namespace
        builder.forNamespace("acme")
            .forEventType(
                EvtSeedingUserRegistered::class,
                "user-1",
                listOf(EvtSeedingUserRegistered("admin@acme.com", "Acme Admin"))
            )

        // A second namespace with different seed data
        builder.forNamespace("contoso")
            .forEventType(
                EvtSeedingUserRegistered::class,
                "user-1",
                listOf(EvtSeedingUserRegistered("admin@contoso.com", "Contoso Admin"))
            )
            .forEventSource(
                "org-1",
                listOf(
                    EvtSeedingOrganizationCreated("Contoso"),
                    EvtSeedingBillingSetUp("contoso@billing.com")
                )
            )
    }
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/namespace-scoped.md)

</TabItem>
<TabItem label="Java">

```java
// Requires io.cratis:chronicle 6.5.0 or later.
import io.cratis.chronicle.events.EventType;
import io.cratis.chronicle.seeding.ICanSeedEvents;
import io.cratis.chronicle.seeding.IEventSeedingBuilder;
import io.cratis.chronicle.seeding.IEventSeedingScopeBuilder;
import io.cratis.chronicle.seeding.Seeder;

import io.cratis.chronicle.java.EventSeedingBuilderJavaBridge;
import io.cratis.chronicle.java.EventSeedingScopeBuilderJavaBridge;

import java.util.List;

@EventType
record EvtSeedingProductCreated(String name, double price) {}

@EventType
record EvtSeedingOrganizationCreated(String name) {}

@EventType
record EvtSeedingBillingSetUp(String billingEmail) {}

@Seeder
class EvtSeedingTenantSeeding implements ICanSeedEvents {
    @Override
    public void seed(IEventSeedingBuilder builder) {
        // Unscoped seed data is sent as global seed data and lands in the namespaces the
        // event store has. For a namespace created later, scope the data with forNamespace.
        EventSeedingBuilderJavaBridge.forEventType(
            builder,
            EvtSeedingProductCreated.class,
            "product-1",
            List.of(new EvtSeedingProductCreated("Laptop", 1299.00)));

        // Namespace-scoped seed data - applied only to the "acme" namespace
        IEventSeedingScopeBuilder acme = builder.forNamespace("acme");
        EventSeedingScopeBuilderJavaBridge.forEventType(
            acme,
            EvtSeedingUserRegistered.class,
            "user-1",
            List.of(new EvtSeedingUserRegistered("admin@acme.com", "Acme Admin")));

        // A second namespace with different seed data
        IEventSeedingScopeBuilder contoso = builder.forNamespace("contoso");
        EventSeedingScopeBuilderJavaBridge.forEventType(
            contoso,
            EvtSeedingUserRegistered.class,
            "user-1",
            List.of(new EvtSeedingUserRegistered("admin@contoso.com", "Contoso Admin")));
        contoso.forEventSource(
            "org-1",
            List.of(
                new EvtSeedingOrganizationCreated("Contoso"),
                new EvtSeedingBillingSetUp("contoso@billing.com")));
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/event-seeding/seeding-with-csharp/namespace-scoped.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.Events.EvtSeedingNsProductCreated do
  use Chronicle.Events.EventType, id: "evt-seeding-ns-product-created"

  defstruct [:name, :price]
end

defmodule MyApp.Events.EvtSeedingNsUserRegistered do
  use Chronicle.Events.EventType, id: "evt-seeding-ns-user-registered"

  defstruct [:email, :display_name]
end

defmodule MyApp.Events.EvtSeedingNsOrganizationCreated do
  use Chronicle.Events.EventType, id: "evt-seeding-ns-organization-created"

  defstruct [:name]
end

defmodule MyApp.Events.EvtSeedingNsBillingSetUp do
  use Chronicle.Events.EventType, id: "evt-seeding-ns-billing-set-up"

  defstruct [:billing_email]
end

defmodule MyApp.Seeders.EvtSeedingTenantSeeding do
  use Chronicle.Seeding.Seeder

  alias MyApp.Events.{
    EvtSeedingNsBillingSetUp,
    EvtSeedingNsOrganizationCreated,
    EvtSeedingNsProductCreated,
    EvtSeedingNsUserRegistered
  }

  @impl true
  def seed(builder) do
    builder
    # Global seed data — applied to every namespace
    |> Chronicle.Seeding.for(EvtSeedingNsProductCreated, "product-1", [
      %EvtSeedingNsProductCreated{name: "Laptop", price: 1299.00}
    ])
    # Namespace-scoped seed data — applied only to the "acme" namespace
    |> Chronicle.Seeding.for_namespace("acme", fn scoped ->
      Chronicle.Seeding.for(scoped, EvtSeedingNsUserRegistered, "user-1", [
        %EvtSeedingNsUserRegistered{email: "admin@acme.com", display_name: "Acme Admin"}
      ])
    end)
    # A second namespace with different seed data
    |> Chronicle.Seeding.for_namespace("contoso", fn scoped ->
      scoped
      |> Chronicle.Seeding.for(EvtSeedingNsUserRegistered, "user-1", [
        %EvtSeedingNsUserRegistered{email: "admin@contoso.com", display_name: "Contoso Admin"}
      ])
      |> Chronicle.Seeding.for_event_source("org-1", [
        %EvtSeedingNsOrganizationCreated{name: "Contoso"},
        %EvtSeedingNsBillingSetUp{billing_email: "contoso@billing.com"}
      ])
    end)
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/namespace-scoped.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { eventType, ICanSeedEvents, IEventSeedingBuilder, seeder } from '@cratis/chronicle';
import { field } from '@cratis/fundamentals';

@eventType()
class EvtSeedingProductCreated {
    @field(String) readonly name: string;
    @field(Number) readonly price: number;

    constructor(name: string, price: number) {
        this.name = name;
        this.price = price;
    }
}

@eventType()
class EvtSeedingOrganizationCreated {
    @field(String) readonly name: string;

    constructor(name: string) {
        this.name = name;
    }
}

@eventType()
class EvtSeedingBillingSetUp {
    @field(String) readonly billingEmail: string;

    constructor(billingEmail: string) {
        this.billingEmail = billingEmail;
    }
}

@seeder()
class EvtSeedingTenantSeeding implements ICanSeedEvents {
    seed(builder: IEventSeedingBuilder): void {
        // Global seed data — applied to every namespace
        builder.for('product-1', [new EvtSeedingProductCreated('Laptop', 1299.0)]);

        // Namespace-scoped seed data — applied only to the "acme" namespace
        builder.forNamespace('acme')
            .for('user-1', [new EvtSeedingUserRegistered('admin@acme.com', 'Acme Admin')]);

        // A second namespace with different seed data
        builder.forNamespace('contoso')
            .for('user-1', [new EvtSeedingUserRegistered('admin@contoso.com', 'Contoso Admin')])
            .forEventSource('org-1', [
                new EvtSeedingOrganizationCreated('Contoso'),
                new EvtSeedingBillingSetUp('contoso@billing.com')
            ]);
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/namespace-scoped.md)

</TabItem>
</Tabs>

The scoped builder supports the same `For<TEvent>` and `ForEventSource` methods as the global builder. Each namespace receives only its own scoped events in addition to any global events.
`ForNamespace(EventStoreNamespaceName.Default)` never sends those entries to another namespace.

## Authoritative global seed sets

The .NET client declares the complete discovered global set for each program. A declaration replaces that
program's previous declaration, including when the new set is empty. Removing an entry from `ICanSeedEvents`
stops it reaching namespaces created afterwards; events already appended to existing namespaces are never retracted.
Explicitly queued globals (`eventStore.Seeding.For(...)` or an independent `CreateEventSeeding()` buffer)
remain append-only and are not removed by a later declaration, even if an identical seed was already declared.

If two programs declare the same seed, a namespace receives it once, not once per program.
For repeated identical seeds, Chronicle uses the largest occurrence count in any program's declaration or
explicit additions, not the sum across programs. Seed identity includes the event source, event type,
content, tags, and routing. Clearing one program's declaration does not remove a seed another program
still declares or has added explicitly. Existing namespaces receive only missing occurrences; no events
are retracted.

If any discovered seeder cannot activate or its callback fails, the client logs the failing seeder types
and sends no discovered global declaration during that registration. The previous declaration stays in
place; the partial discovered set is not stored as explicit additions either. Namespace-scoped entries
and manually queued entries still register. The next registration retries discovery, and a complete
successful discovery declares the global set as usual.

Ownership uses `ChronicleOptions.ProgramIdentifier`, falling back to the entry assembly name when the option
is still `"[N/A]"`. Set a distinct, stable `ProgramIdentifier` for each program when several programs share a
store, so one program does not replace another's declaration. The first authoritative registration after
upgrading also clears legacy stored global seeds whose owner was not recorded. Other programs' owned declarations and explicit
additions remain stored.

This requires an upgraded client and kernel. Old clients keep append-only behavior. A new client still works
against an older kernel, but that kernel ignores declarations and retains its stored globals.

### Clear stale development seeds

1. Upgrade both the .NET client and the kernel.
2. Remove the development seeder, or scope its entries with `ForNamespace(EventStoreNamespaceName.Default)`.
3. Start the application once so it declares its remaining global set, even if empty.

New namespaces no longer receive the removed declarations or legacy global entries. Existing namespaces keep
all events already appended. This does not remove explicitly queued globals.

## Register corrected seed definitions without reconnecting

When registration fails, Chronicle retains the attempted entries in the event store's default `Seeding` buffer.
That makes retrying an unchanged idempotent batch safe after a transient failure. If the definitions themselves
were wrong, create an independent empty buffer so the corrected set is not combined with the retained set:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle;
using Cratis.Chronicle.Seeding;

public static class EvtSeedingCorrection
{
    public static async Task Register(IEventStore eventStore)
    {
        var correctedSeeding = eventStore.CreateEventSeeding();
        correctedSeeding.For<EvtSeedingUserRegistered>("user-123", [
            new("john@example.com", "John Doe")
        ]);

        await correctedSeeding.Register();
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/corrected-seed-set.md)

</TabItem>
<TabItem label="Kotlin">

```text
Kotlin does not support this workflow yet.
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/corrected-seed-set.md)

</TabItem>
<TabItem label="Java">

```text
Java does not support this workflow yet.
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/event-seeding/seeding-with-csharp/corrected-seed-set.md)

</TabItem>
<TabItem label="Elixir">

```text
Elixir does not support this workflow yet.
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/corrected-seed-set.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { IEventStore } from '@cratis/chronicle';

async function registerEvtSeedingCorrection(eventStore: IEventStore): Promise<void> {
    eventStore.seeding.for('user-123', [new EvtSeedingUserRegistered('john@example.com', 'John Doe')]);

    await eventStore.seeding.register();
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/event-seeding/seeding-with-csharp/corrected-seed-set.md)

</TabItem>
</Tabs>

The new buffer uses the event store's existing connection, event types, serializer, and client configuration. A
failure from its `Register()` call still propagates normally.

## How it runs

- Seeders are automatically discovered at application startup.
- Each client sends its discovered global seed set once per event store name per connection lifetime, not once
  per namespace store it opens. After a disconnect and reconnect, it sends the global set again.
- Each namespace store still sends its `ForNamespace(...)` entries during registration.
- Every entry is applied once per namespace, and seeding is idempotent across restarts: entries that have
  already been appended are skipped on the next run.
- An entry that your seeder yields twice is two events, not one. Two events of the same type, on the same
  event source, carrying the same payload are two facts, and both are appended.
- Events are appended in batches of 100. A batch is validated as a whole before anything is written, so a
  single entry that violates a constraint means none of its batch is appended. The server reports the
  rejection at `Error` with the violations, leaves those entries unseeded, and fails registration. The remote
  client surfaces that failure through its normal transport error. Retry the unchanged buffer for a transient
  failure; after correcting the definitions, restart the process or use `CreateEventSeeding()` to offer a fresh set.

## Best practices

- Keep seed data minimal and deterministic.
- Use clear event source IDs to make debugging easier.
- Group seeders by scenario; dropping a discovered global seed stops future application, not events already appended.
- Use build flags or runtime settings to prevent seeding in production.
- Use `ForNamespace` when seed data is tenant-specific or environment-specific to avoid polluting other namespaces.
