Skip to documentation
Documentation navigation

Documentation navigation

Documentation / guides

Event history

Read durable event history without starting a live Session.

developer

Event history is a cold read that does not create a live session or acquire a writer lease. Use the public replayer for product history and the internal replayers only for restore, catalog repair, and storage maintenance.

Public history

func (s *Store) OpenEventReplayer(
	id uuid.UUID,
	req ReplayRequest,
) (journal.EventReplayer, error)

The returned EventReplayer yields Public event.Event values in ledger sequence order. It drops command intent records, lease fences, private GatePreparedRecord values, and Internal events. A zero session UUID is a concrete ledger name, not a wildcard.

Internal history

Restore and repair use:

func (s *Store) OpenInternalEventReplayer(
	id uuid.UUID, req ReplayRequest,
) (journal.EventReplayer, error)

func (s *Store) OpenInternalRecordReplayer(
	id uuid.UUID, req ReplayRequest,
) (journal.RecordReplayer, error)

The internal event view includes Internal events but still omits commands, fences, and private gate payloads. The record view includes all four journal record variants and reconstructs command/fence routing with the bound session ID. It is not a product history endpoint because command bodies and private gate payloads are not ordinary event visibility.

History and catalog

The event appender calls Catalog.UpdateOnEvent only after a durable append. That update is best-effort and returns nil even if the derived KV write fails. History remains authoritative. Catalog.RepairCatalog uses the internal event replayer to rebuild a stale or corrupt projection and writes it under KV revision CAS.

%%{init: {"theme":"dark"}}%%
flowchart LR
    J[Authoritative ledger] --> P[Public event history]
    J --> I[Internal event history]
    J --> F[Full record history]
    P --> UI[Product transcript]
    I --> RC[Catalog Repair]
    F --> RS[Restore and idempotency hydration]
    C[Catalog KV projection] --> Picker[Session picker]
    P --> C

History example

func listDurable(ctx context.Context, s *sessionstore.Store, id uuid.UUID) ([]event.Event, error) {
	r, err := s.OpenEventReplayer(id, sessionstore.ReplayRequest{FromSeq: 1})
	if err != nil {
		return nil, err
	}
	c, err := r.Open(ctx, journal.ReplayRequest{SessionID: id, From: journal.Beginning()})
	if err != nil {
		return nil, err
	}
	defer c.Close()
	var out []event.Event
	for {
		ev, _, err := c.Next(ctx)
		if errors.Is(err, io.EOF) {
			return out, nil
		}
		if err != nil {
			return nil, err
		}
		out = append(out, ev)
	}
}

Replay returns io.EOF only for a clean cold drain. A missing or corrupt offload blob returns a typed error and must be surfaced to the caller.

Source and proof

← back to documentation