# Events Store — Persistent Pub/Sub (/learn/events-store)



<EventsStoreHero className="w-full max-h-[300px]" />

Think of Events Store as a **DVR for your messages**. Just like a DVR records live TV so you can watch it later, Events Store records every event published to a channel. Subscribers can rewind to the beginning, fast-forward to a specific point, or jump in live — all from the same persistent stream.

KubeMQ Events Store implements persistent publish/subscribe with at-least-once delivery. Publishers send messages to a named channel; KubeMQ writes each event to disk with a monotonically increasing sequence number, and subscribers choose a start position to receive historical events, new events, or both. Because events are durable, a subscriber that was offline at publish time can still replay everything it missed.

## The concept it implements [#the-concept-it-implements]

<Callout type="info">
  Events Store is KubeMQ's implementation of two fundamental ideas. The interaction style is &#x2A;*[pub/sub](/learn/concepts/interaction-styles)*&#x2A; (fan-out) — one publisher, many subscribers — extended with durable storage so subscribers can &#x2A;*[replay from any position](/learn/concepts/ordering-and-replay)*&#x2A;. The delivery guarantee is &#x2A;*[at-least-once](/learn/concepts/delivery-guarantees)** — events are persisted and durable subscriptions track their position, so a message is redelivered until the subscriber has processed it. New to these terms? Start with the [Fundamentals](/learn/concepts) track.
</Callout>

## Key Properties [#key-properties]

| Property           | This pattern                                          | Learn the concept                                          |
| ------------------ | ----------------------------------------------------- | ---------------------------------------------------------- |
| Interaction style  | pub/sub (fan-out) with replay                         | [Interaction styles](/learn/concepts/interaction-styles)   |
| Delivery guarantee | at-least-once                                         | [Delivery guarantees](/learn/concepts/delivery-guarantees) |
| Persistence        | Disk-backed — survives restarts                       | [Ordering & replay](/learn/concepts/ordering-and-replay)   |
| Ordering           | Sequenced per channel (sequence number + timestamp)   | [Ordering & replay](/learn/concepts/ordering-and-replay)   |
| Scaling            | Fan-out, or load-balance with durable consumer groups | [Scaling & flow](/learn/concepts/scaling-and-flow)         |
| Addressing         | Named channels                                        | [Channels & routing](/learn/concepts/channels-and-routing) |

## Key Features [#key-features]

* **Persistent storage** — events are written to disk and survive server restarts
* **Replay from any point** — subscribe from the first message, last message, a specific sequence number, an absolute timestamp, or a relative time delta
* **Durable subscriptions** — subscribers resume from their last position after reconnecting
* **Consumer groups** — distribute event processing across multiple consumers with automatic position tracking
* **Sequenced messages** — every stored event receives a monotonically increasing sequence number and a server timestamp
* **Stream publishing** — high-throughput bidirectional streaming with per-event acknowledgment

## How It Works [#how-it-works]

<Mermaid
  chart="graph LR
  PUB[&#x22;Publisher&#x22;]
  ES{{&#x22;Events Store channel<br/>orders.events&#x22;}}
  DISK[(&#x22;Persistent log&#x22;)]
  S1[&#x22;Live subscriber<br/>(StartNewOnly)&#x22;]
  S2[&#x22;Late subscriber<br/>(replay from offset)&#x22;]

  PUB -- persist --> ES
  ES --- DISK
  ES -- &#x22;stream new&#x22; --> S1
  ES -. &#x22;replay history&#x22; .-> S2

  class ES store
  class DISK data
  class PUB,S1,S2 client"
/>

*A publisher persists events to a durable channel; a live subscriber streams new events while a late subscriber replays missed history from a chosen offset.*

1. A **publisher** sends an event to a named channel with persistence enabled
2. KubeMQ writes the event to the **store** on disk
3. Each event receives a **sequence number** and **timestamp**
4. **Subscribers** connect and specify a start position — they receive historical and/or new events based on that position
5. **Durable subscriptions** track the subscriber's position so reconnections resume automatically

<Callout type="info">
  For fire-and-forget pub/sub without persistence, use [Events](/learn/events) instead.
</Callout>

## Quick Example [#quick-example]

<Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
  <Tab value="Go">
    ```go title="publish_store.go"
    ctx := context.Background()
    client, err := kubemq.NewClient(ctx,
        kubemq.WithAddress("localhost", 50000),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    result, err := client.SendEventStore(ctx, kubemq.NewEvent().
        SetChannel("orders.events").
        SetBody([]byte(`{"action":"order.created","orderId":"ORD-1001"}`)),
    )
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("Event stored: ID=%s, Sent=%v", result.EventID, result.Sent)
    ```
  </Tab>

  <Tab value="Python">
    ```python title="publish_store.py"
    from kubemq import PubSubClient, EventStoreMessage

    with PubSubClient(address="localhost:50000") as client:
        result = client.publish_event_store(
            EventStoreMessage(
                channel="orders.events",
                body=b'{"action":"order.created","orderId":"ORD-1001"}',
            )
        )
        print(f"Event stored: ID={result.id}, Sent={result.sent}")
    ```
  </Tab>

  <Tab value="Node.js">
    ```typescript title="publish_store.ts"
    import { KubeMQClient, createEventStoreMessage } from 'kubemq-js';

    const client = await KubeMQClient.create({ address: 'localhost:50000' });

    const result = await client.sendEventStore(
      createEventStoreMessage({
        channel: 'orders.events',
        body: '{"action":"order.created","orderId":"ORD-1001"}',
      })
    );

    console.log(`Event stored: ID=${result.id}, Sent=${result.sent}`);
    ```
  </Tab>

  <Tab value="Java">
    ```java title="PublishStore.java"
    PubSubClient client = PubSubClient.builder()
        .address("localhost:50000")
        .clientId("order-publisher")
        .build();

    EventSendResult result = client.sendEventsStoreMessage(
        EventStoreMessage.builder()
            .channel("orders.events")
            .body("{\"action\":\"order.created\",\"orderId\":\"ORD-1001\"}".getBytes())
            .build());

    System.out.println("Event stored: " + result.getId());
    client.close();
    ```
  </Tab>

  <Tab value="C#">
    ```csharp title="PublishStore.cs"
    await using var client = new KubeMQClient(new KubeMQClientOptions());
    await client.ConnectAsync();

    var result = await client.SendEventStoreAsync(new EventStoreMessage
    {
        Channel = "orders.events",
        Body = Encoding.UTF8.GetBytes(
            "{\"action\":\"order.created\",\"orderId\":\"ORD-1001\"}")
    });

    Console.WriteLine($"Event stored: ID={result.Id}, Sent={result.Sent}");
    ```
  </Tab>

  <Tab value="Kotlin">
    ```kotlin title="PublishStore.kt"
    val client = KubeMQClient.pubSub {
        address = "localhost:50000"
        clientId = "order-publisher"
    }

    client.use {
        val result = client.sendEventStore(eventStoreMessage {
            channel = "orders.events"
            body = """{"action":"order.created","orderId":"ORD-1001"}""".toByteArray()
        })
        println("Event stored: ID=${result.id}, Sent=${result.sent}")
    }
    ```
  </Tab>

  <Tab value="C++">
    ```cpp title="publish_store.cc"
    kubemq::ClientOptions options;
    options.set_address("localhost", 50000);
    options.set_client_id("order-publisher");

    auto client = kubemq::Client::Create(options).value();

    kubemq::EventStoreMessage msg;
    msg.set_channel("orders.events");
    msg.set_body(R"({"action":"order.created","orderId":"ORD-1001"})");

    auto result = client->SendEventStore(msg);
    if (result.ok()) {
        std::cout << "Event stored: " << result->id() << std::endl;
    }
    ```
  </Tab>

  <Tab value="Rust">
    ```rust title="publish_store.rs"
    use kubemq::prelude::*;
    use kubemq::EventStoreBuilder;

    #[tokio::main]
    async fn main() -> kubemq::Result<()> {
        let client = KubemqClient::builder()
            .host("localhost")
            .port(50000)
            .build()
            .await?;

        let event = EventStoreBuilder::new()
            .channel("orders.events")
            .body(br#"{"action":"order.created","orderId":"ORD-1001"}"#.to_vec())
            .build();

        let result = client.send_event_store(event).await?;
        println!("Event stored: id={}, sent={}", result.id, result.sent);

        client.close().await?;
        Ok(())
    }
    ```
  </Tab>

  <Tab value="Ruby">
    ```ruby title="publish_store.rb"
    require 'kubemq'

    client = KubeMQ::PubSubClient.new(address: 'localhost:50000', client_id: 'order-publisher')

    msg = KubeMQ::PubSub::EventStoreMessage.new(
      channel: 'orders.events',
      body: '{"action":"order.created","orderId":"ORD-1001"}'
    )
    result = client.send_event_store(msg)
    puts "Event stored: id=#{result.id}, sent=#{result.sent}"

    client.close
    ```
  </Tab>

  <Tab value="Elixir">
    ```elixir title="publish_store.exs"
    {:ok, client} = KubeMQ.Client.start_link(address: "localhost:50000", client_id: "order-publisher")

    event =
      KubeMQ.EventStore.new(
        channel: "orders.events",
        body: ~s({"action":"order.created","orderId":"ORD-1001"})
      )

    {:ok, result} = KubeMQ.Client.send_event_store(client, event)
    IO.puts("Event stored: sent=#{result.sent}")

    KubeMQ.Client.close(client)
    ```
  </Tab>
</Tabs>

## Subscription Start Positions [#subscription-start-positions]

| Position           | Description                                 | Use Case                            |
| ------------------ | ------------------------------------------- | ----------------------------------- |
| `StartNewOnly`     | Only new events published after subscribing | Live monitoring, real-time alerting |
| `StartFromFirst`   | Replay all events from the beginning        | State rebuild, full audit replay    |
| `StartFromLast`    | Start from the last stored event, then new  | Resume from most recent             |
| `StartAtSequence`  | Start from a specific sequence number       | Checkpoint-based recovery           |
| `StartAtTime`      | Start from a specific timestamp             | Point-in-time recovery              |
| `StartAtTimeDelta` | Start from N seconds ago                    | Recent history replay               |

## When to Use Events Store [#when-to-use-events-store]

| Scenario                     | Events                                 | Events Store                  |
| ---------------------------- | -------------------------------------- | ----------------------------- |
| Audit trails                 | ❌ Messages can be lost                 | ✅ Best choice                 |
| Event sourcing               | ❌ No persistence                       | ✅ Best choice                 |
| Late or offline subscribers  | ❌ Miss everything published while away | ✅ Replay missed history       |
| Real-time notifications      | ✅ Best choice                          | Overkill                      |
| Live dashboards (no history) | ✅ Best choice                          | Use if historical data needed |
| Wildcard subscriptions       | ✅ Supported                            | ❌ Not supported               |

**When not to use Events Store:** if you never need replay, persistence, or guaranteed delivery — fire-and-forget [Events](/learn/events) have lower latency and support wildcard subscriptions. If a message must be processed by exactly one of several competing workers (work distribution, not fan-out), reach for [Queues](/learn/queues) instead.

### Events vs Events Store at a glance [#events-vs-events-store-at-a-glance]

| Feature                | Events          | Events Store            |
| ---------------------- | --------------- | ----------------------- |
| Persistence            | No              | Yes (disk-backed)       |
| Replay                 | No              | Yes (6 start positions) |
| Delivery guarantee     | at-most-once    | at-least-once           |
| Wildcard subscriptions | Yes             | No                      |
| Consumer groups        | Yes (ephemeral) | Yes (durable)           |
| Sequence numbers       | No              | Yes                     |
| Timestamps             | No              | Yes (server-assigned)   |
| Latency                | Lowest          | Slightly higher         |

<Callout type="info">
  Events Store is also available via the [CloudEvents protocol](/connectors/cloudevents/how-to/events-store) — use any language with a CloudEvents SDK, no KubeMQ client library needed.
</Callout>

## Learn More [#learn-more]

<Cards>
  <Card title="Getting Started" href="/learn/events-store/getting-started" description="Store and replay your first persistent event in minutes." />

  <Card title="Persistent Pub/Sub" href="/learn/events-store/tutorials/persistent-publish-subscribe" description="Publish persistent events and subscribe with replay." />

  <Card title="Replay Events" href="/learn/events-store/tutorials/replay-events" description="Replay events from a specific offset, time, or sequence." />

  <Card title="Consumer Groups" href="/learn/events-store/tutorials/consumer-groups" description="Distribute processing with durable consumer groups." />

  <Card title="Stream Publishing" href="/learn/events-store/tutorials/stream-publishing" description="High-throughput publishing with bidirectional streaming." />

  <Card title="Event Sourcing" href="/learn/events-store/tutorials/event-sourcing" description="Implement event sourcing patterns with KubeMQ." />

  <Card title="Configure Retention" href="/learn/events-store/how-to/configure-retention" description="Set time-based, size-based, or count-based retention." />

  <Card title="Events Store Reference" href="/learn/events-store/reference" description="Message structure, subscription modes, and configuration." />
</Cards>
