Help us shape the future of Go! Take the 2026 Go Developer Survey

chronicle

package module
v0.11.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 20, 2026 License: MIT Imports: 78 Imported by: 0

README ¶

Chronicle for Go

Go Reference Build Discord

The idiomatic Go client for Cratis Chronicle. Register typed events, connect securely, select a store and namespace, and append with explicit concurrency protection and complete outcomes.

Status and installation

Experimental foundation, v0.x. Connection/TLS/OAuth, compatibility, event registration, single/atomic batch appends and event history reads are implemented. Observers, projections and automatic lifecycle recovery are not yet implemented. See parity and limitations.

Requires Go 1.26 or later. After the first tagged release:

go get github.com/cratis/chronicle.go@latest

Contracts are pinned to Chronicle 19.32.3; real-kernel tests use 19.32.3-development. Generated contracts are public packages in this same module, with no .NET or sibling checkout needed.

Quick start

Start the development kernel and wait for https://localhost:35000/health to report Healthy:

docker run --rm --name chronicle-go -p 35000:35000 cratis/chronicle:19.32.3-development

This complete program is also available as go run ./examples/getting-started in a checkout:

package main

import (
    "context"
    "errors"
    "fmt"
    "log"
    "time"

    chronicle "github.com/cratis/chronicle.go"
    "github.com/cratis/chronicle.go/events"
)

type CustomerRegistered struct {
    Name string `json:"name"`
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    if err := appendCustomer(ctx); err != nil {
        log.Fatal(err)
    }
}

func appendCustomer(ctx context.Context) (err error) {
    registry := chronicle.NewRegistry()
    if _, err = chronicle.RegisterEvent[CustomerRegistered](registry,
        events.WithID("customer-registered")); err != nil {
        return err
    }
    client, err := chronicle.NewClient(chronicle.WithDevelopmentDefaults(),
        chronicle.WithRegistry(registry))
    if err != nil {
        return err
    }
    defer func() { err = errors.Join(err, client.Close()) }()
    store, err := client.EventStore(ctx, "customers")
    if err != nil {
        return err
    }
    result, err := store.EventLog().Append(ctx, "customer-42", CustomerRegistered{Name: "Ada"})
    if err != nil {
        return err
    }
    if err = result.Err(); err != nil {
        return err
    }
    fmt.Printf("Appended event at position %d\n", *result.Position)
    return nil
}

WithDevelopmentDefaults explicitly permits the kernel's self-signed certificate. Production TLS validates by default: configure real roots and credentials. An append transport error may hide a committed event; never blindly retry. Default optimistic concurrency leaves empty history unchecked; use NoMatchingEvent when first append must be protected.

Documentation and development

export GOWORK=off
go build ./...
go vet ./...
go test -race -count=1 -timeout=3m ./...
golangci-lint run
go generate ./...
python3 scripts/generate-contracts.py --check

Community, security and license

Discord (questions and help) · Documentation · Cratis · Cratis repositories · Private vulnerability reporting · MIT license

Documentation ¶

Overview ¶

Package chronicle is the Go client for Cratis Chronicle, the open-source event-sourcing database and runtime. Register event structs explicitly, construct a Client, obtain a registered EventStore, and append via its EventLog. Client, store and sequence handles are concurrency-safe.

NewClient is lazy; Dial performs TLS/OAuth and structural compatibility preflight. Close cancels and joins owned work. Use WithDevelopmentDefaults only for local self-signed kernels. Production TLS verifies server identity by default.

Append distinguishes known domain rejection from transport failures with an unknown commit outcome. Always inspect both the operation error and result.Err; never blindly retry an ambiguous write. See ExampleRegisterEvent to declare events.

Index ¶

Examples ¶

Constants ¶

View Source
const DefaultNamespace = metadata.DefaultNamespace

DefaultNamespace is selected when no WithNamespace option is supplied.

Variables ¶

View Source
var (
	// ErrNotPrepared identifies access before successful preparation.
	ErrNotPrepared = errors.New("chronicle: client is not prepared")
	// ErrPreparationInProgress identifies a concurrent or reentrant Prepare call.
	ErrPreparationInProgress = errors.New("chronicle: preparation is in progress")
	// ErrClosed identifies an operation attempted after Close.
	ErrClosed = faults.ErrClosed
	// ErrUnsupported identifies a capability not implemented by this client.
	ErrUnsupported = faults.ErrUnsupported
	// ErrNotRegistered identifies an event missing from the client's frozen catalog.
	ErrNotRegistered = faults.ErrNotRegistered
	// ErrInvalidConfiguration identifies invalid client configuration.
	ErrInvalidConfiguration = faults.ErrInvalidConfiguration
	// ErrProtocol identifies a missing or inconsistent kernel response.
	ErrProtocol = faults.ErrProtocol
)
View Source
var ErrDestructiveRegistrationUnknown = errors.New("chronicle: destructive registration outcome unknown; new definitions refused")

ErrDestructiveRegistrationUnknown refuses new definitions after a dispatched cumulative registration has an unknown outcome. Later acknowledgements do not clear it or prove remote quiescence. Ordinary registration/readiness is unchanged.

View Source
var ErrIdentityRenameRefused = errors.New("chronicle: identity rename refused")

ErrIdentityRenameRefused identifies an explicit command-envelope refusal.

View Source
var ErrIdentityRenameUnknown = errors.New("chronicle: identity rename outcome unknown")

ErrIdentityRenameUnknown identifies an uncertain mutation or confirmation.

Functions ¶

func RegisterConstraintFactory ¶

func RegisterConstraintFactory[C any](registry *Registry, names []string, factory any, define func(context.Context, C) ([]constraints.Definition, error)) error

RegisterConstraintFactory declares all output names before activation. define must return exactly one definition per name (in any order), using events from this registry. Names are copied; duplicate/conflicting declarations fail atomically. Factory/ownership semantics match RegisterProjectionFactory. Only static WithMessage templates are allowed: WithMessageProvider would retain a potentially scoped collaborator beyond preparation and is rejected in this first slice.

func RegisterEvent ¶

func RegisterEvent[T any](registry *Registry, options ...events.TypeOption) (events.Type[T], error)

RegisterEvent declares T with a stable ID and positive generation. T must be a named struct; appending either T or *T resolves the same declaration. Duplicate Go types or current persisted IDs fail without partially modifying the registry.

Example ¶
registry := chronicle.NewRegistry()
eventType, err := chronicle.RegisterEvent[CustomerRegistered](registry,
	events.WithID("customer-registered"))
if err != nil {
	fmt.Println(err)
	return
}
content, err := eventType.Descriptor().Marshal(CustomerRegistered{Name: "Ada"})
if err != nil {
	fmt.Println(err)
	return
}
fmt.Println(eventType.Ref().ID, eventType.Ref().Generation)
fmt.Println(string(content))
Output:
customer-registered 1
{"name":"Ada"}

func RegisterEventGeneration ¶

func RegisterEventGeneration[Previous, Current any](registry *Registry, current events.Type[Current], generation events.Generation, options ...events.TypeOption) (events.Type[Previous], error)

RegisterEventGeneration declares Previous as a historical shape of current. The current handle must belong to this registry. Historical values can be appended and handled, but never introduce another current event for the ID.

func RegisterEventMigration ¶

func RegisterEventMigration[Upgrade, Previous any](registry *Registry, upgrade events.Type[Upgrade], previous events.Type[Previous], migration events.Migration[Upgrade, Previous]) error

RegisterEventMigration records both directions between adjacent generations. Authoring callbacks run synchronously once; their builders and literal values are snapshotted. Duplicate direct/factory edges fail admission atomically. NewClient validates identity, adjacency, property paths and (when enabled) the complete chain, before any network I/O.

func RegisterEventMigrationFactory ¶

func RegisterEventMigrationFactory[M any](registry *Registry, upgrade, previous events.Descriptor, factory any, define func(context.Context, M) (events.MigrationDeclaration, error)) error

RegisterEventMigrationFactory declares one adjacent generation edge before activation. define uses events.DefineMigration with typed endpoint handles and returns frozen authoring metadata, never a runtime migration callback. Endpoints must be the exact registered declarations and outputs must match both endpoints. Factory/ownership semantics match RegisterProjectionFactory.

func RegisterProjectionFactory ¶

func RegisterProjectionFactory[P any](registry *Registry, id string, model readmodels.Descriptor, factory any, define func(context.Context, P) (projections.Declaration, error)) error

RegisterProjectionFactory declares a config-only projection producer. id and model are fixed before activation; define must return exactly that identity and registered model descriptor. Existing fluent/model-bound builders are unchanged. The callback runs once per captured registry per NewClient, never on reconnect. A factory returns P or (P,error), optionally taking context.Context followed by dependencies. Nil selects registered-service/default activation; a registered service takes precedence over an explicit constructor when Catalog is available. No container is needed for a plain constructor. Constructor results are owned; resolved services and the provider are borrowed. The temporary scope is closed after define, including on error. Callbacks must be synchronous and must not retain the borrowed resolver or resolve this client's store/sequence facades. CaptureClient permits *Client only through an explicit borrowed binding of its exact identity; it remains unprepared during callbacks. Client values and SDK facade artifact results are prohibited. Provider-internal graphs and hidden closure dependencies remain the application's responsibility.

Example ¶
package main

import (
	"context"
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/projections"
)

type configuredOrderOpened struct{ Name string }
type configuredOrderView struct{ ID, Name string }
type projectionSettings struct{ Label string }

func main() {
	registry := chronicle.NewRegistry()
	opened, err := chronicle.RegisterEvent[configuredOrderOpened](registry)
	if err != nil {
		panic(err)
	}
	model, err := chronicle.RegisterReadModel[configuredOrderView](registry)
	if err != nil {
		panic(err)
	}
	// definition-factory:start
	settings := projectionSettings{Label: "orders"}
	err = chronicle.RegisterProjectionFactory(registry, "orders", model.Descriptor(),
		func() projectionSettings { return settings },
		func(_ context.Context, config projectionSettings) (projections.Declaration, error) {
			return projections.ModelBound(model,
				projections.WithIdentifier("orders"),
				projections.FromEvent(opened),
				projections.WithLabels(config.Label)), nil
		})
	if err != nil {
		panic(err)
	}
	client, err := chronicle.NewClient(chronicle.WithRegistry(registry))
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := client.Close(); err != nil {
			panic(err)
		}
	}()
	// definition-factory:end
	artifacts, err := client.Artifacts("orders-store")
	if err != nil {
		panic(err)
	}
	fmt.Println(artifacts.Projections[0].KernelDefinition().Tags[0])
}
Output:
orders

func RegisterReactor ¶

func RegisterReactor[R any](registry *Registry, factory any, options ...reactors.Option) error

RegisterReactor explicitly admits R, discovering its exported event handler methods when NewClient freezes the registry. factory is a constructor returning R or (R,error), with optional context and resolved services. Plain func() R closures need no dependency injection imports. Nil requests service activation.

func RegisterReactorHandler ¶

func RegisterReactorHandler[E any](registry *Registry, id reactors.ID, handler func(context.Context, E) error, options ...reactors.Option) error

RegisterReactorHandler registers a typed closure without an artifact or DI. Additional typed handlers can be supplied using reactors.WithHandler.

func RegisterReactorHandlers ¶

func RegisterReactorHandlers(registry *Registry, id reactors.ID, handlers []reactors.Handler, options ...reactors.Option) error

RegisterReactorHandlers registers one or more typed callbacks, including a reactor composed solely of reactors.Returning callbacks. Input slices are copied.

func RegisterReactorMiddleware ¶

func RegisterReactorMiddleware(registry *Registry, factory any) error

RegisterReactorMiddleware adds a constructor for every reactor in this registry. Registry middleware runs in registration order before per-reactor middleware. Factory signatures and services are validated at NewClient, without activation, just like reactors.WithMiddleware. Duplicate registrations run independently. NewClient freezes the list; later registrations do not affect existing clients.

func RegisterReactorSideEffectHandler ¶

func RegisterReactorSideEffectHandler(registry *Registry, handler reactors.SideEffectHandler) error

RegisterReactorSideEffectHandler borrows an extension for all reactors in this registry, for example Arc's returned-command executor. It runs in registration order before per-reactor extensions. All matching handlers run, including the built-in event handler; this is not an override. Instances must be concurrency- safe and are never closed. NewClient snapshots registrations without executing effects; later registrations do not affect an existing client.

func RegisterReadModel ¶

func RegisterReadModel[T any](registry *Registry, options ...readmodels.ModelOption) (readmodels.Model[T], error)

RegisterReadModel explicitly declares a typed model schema, identity, sink and indexes. Registration does not create a producer or materialize an instance. Duplicate Go types/identifiers fail atomically. NewClient freezes declarations.

Example ¶
package main

import (
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/readmodels"
)

func main() {
	type Person struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	}
	registry := chronicle.NewRegistry()
	model, err := chronicle.RegisterReadModel[Person](registry, readmodels.WithIdentifier("Example.Person"), readmodels.WithIndexes("name"))
	if err != nil {
		panic(err)
	}
	descriptor := model.Descriptor()
	fmt.Println(model.Identifier(), descriptor.ContainerName(), descriptor.Generation(), descriptor.Sink().Type)
	// Pass WithRegistry(registry) to NewClient, then create a typed reader with
	// readmodels.For(store.ReadModels(), model). Get returns Instance[Person]:
	// check Exists before using Value. LastHandled is independent of the model.
	

func RegisterReadModelReactor ¶

func RegisterReadModelReactor[R, M any](registry *Registry, model readmodels.Model[M], factory any, options ...reactors.ReadModelOption) error

RegisterReadModelReactor registers R for M, discovering exact Added, Modified and Removed names at NewClient. factory follows RegisterReactor's constructor and service precedence rules. Every callback gets its own scope, activation, effects and cleanup. These are best-effort model changes, NOT durable observers.

func RegisterReadModelReactorHandlers ¶

func RegisterReadModelReactorHandlers[M any](registry *Registry, id reactors.ID, model readmodels.Model[M], handlers []reactors.ReadModelHandler, options ...reactors.ReadModelOption) error

RegisterReadModelReactorHandlers registers explicit callbacks without an artifact or container. Multiple callbacks for the same change all run, each with a fresh scope, in registration order. NewClient validates signatures.

func RegisterReducer ¶

func RegisterReducer[R, M any](registry *Registry, model readmodels.Model[M], factory any, options ...reducers.Option) error

RegisterReducer associates R with one model. NewClient discovers and validates exported folds. factory is a plain constructor (optionally with context and injected dependencies), or nil for service/default activation. Dependencies belong in the constructor, never in fold method parameters.

func RegisterReducerHandlers ¶

func RegisterReducerHandlers[M any](registry *Registry, model readmodels.Model[M], id reducers.ID, handlers []reducers.Handler, options ...reducers.Option) error

RegisterReducerHandlers binds explicit callbacks to one read model, without DI or an artifact type. All callbacks compile to the same fold plan as discovery.

Example (Replay) ¶
package main

import (
	"context"
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/events"
	"github.com/cratis/chronicle.go/reducers"
)

type ReplayAdjustment struct{ Amount int }
type ReplayBalance struct{ Amount int }

func main() {
	registry := chronicle.NewRegistry()
	if _, err := chronicle.RegisterEvent[ReplayAdjustment](registry); err != nil {
		panic(err)
	}
	model, err := chronicle.RegisterReadModel[ReplayBalance](registry)
	if err != nil {
		panic(err)
	}
	fold := func(_ context.Context, event ReplayAdjustment, current *ReplayBalance, _ events.Context) (*ReplayBalance, error) {
		amount := event.Amount
		if current != nil {
			amount += current.Amount
		}
		return &ReplayBalance{Amount: amount}, nil
	}
	callbacks := reducers.ReplayCallbacks{
		BeginReplay: func(ctx context.Context) error { return ctx.Err() },
		EndReplay:   func(ctx context.Context) error { return ctx.Err() },
		BeginReplayPartition: func(ctx context.Context, partition events.SourceID) error {
			// The opaque partition key is available without an event payload.
			_ = partition
			return ctx.Err()
		},
		EndReplayPartition: func(ctx context.Context, partition events.SourceID) error { return ctx.Err() },
	}
	if err := chronicle.RegisterReducerHandlers(registry, model, "balance", []reducers.Handler{reducers.On(fold)}, reducers.WithReplayCallbacks(callbacks), reducers.WithVersion("1")); err != nil {
		panic(err)
	}
	client, err := chronicle.NewClient(chronicle.WithRegistry(registry))
	if err != nil {
		panic(err)
	}
	if err := client.Close(); err != nil {
		panic(err)
	}
	// Offline startup validation, not a kernel replay or completion witness.
	fmt.Println("Replay callbacks validated")
}
Output:
Replay callbacks validated

func RegisterSeeder ¶

func RegisterSeeder(registry *Registry, seeder seeding.Seeder) error

RegisterSeeder borrows a seeder for definition preparation. Seed runs once per selected registry snapshot at NewClient, never at registration or reconnect. The caller owns the instance; Chronicle never closes it. Registration order is preserved. Duplicate concrete seeder types are rejected (use RegisterSeederFunc for independent callbacks). Adding seeders cannot mutate an existing client.

Example ¶
package main

import (
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/seeding"
)

type ReferenceItemAdded struct{ Name string }
type ReferenceSeeds struct{}

func (ReferenceSeeds) Seed(builder *seeding.Builder) error {
	seeding.For(builder, "item-1", ReferenceItemAdded{Name: "Notebook"})
	builder.ForNamespace("demo").ForEventSource("item-2", ReferenceItemAdded{Name: "Sample"})
	return nil
}

func main() {
	registry := chronicle.NewRegistry()
	if _, err := chronicle.RegisterEvent[ReferenceItemAdded](registry); err != nil {
		panic(err)
	}
	if err := chronicle.RegisterSeeder(registry, ReferenceSeeds{}); err != nil {
		panic(err)
	}
	client, err := chronicle.NewClient(chronicle.WithRegistry(registry))
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := client.Close(); err != nil {
			panic(err)
		}
	}()
	fmt.Println("Seeds prepared without connecting")
	// EventStore(ctx, "shop") registers observers, then sends the seed snapshot.
	

func RegisterSeederFactory ¶

func RegisterSeederFactory[S seeding.Seeder](registry *Registry, factory any) error

RegisterSeederFactory declares scoped construction of S during preparation. A constructor returns S or (S,error) and may accept context.Context followed by dependencies (or the operation Scope). Nil selects service/default activation. The default path uses plain Go construction, with no DI container. WithServices opts into a resolver; registered-service precedence and cleanup match observers. Each seeder gets one scope: open, construct, Seed, close before NewClient returns.

func RegisterSeederFunc ¶

func RegisterSeederFunc(registry *Registry, seed func(*seeding.Builder) error) error

RegisterSeederFunc registers a plain function. Closures are independent declarations, even when their Go function type is identical. Captures are borrowed and may be accessed concurrently by separate NewClient calls.

Types ¶

type ArtifactRegistration ¶

type ArtifactRegistration = registration.Artifact

ArtifactRegistration records a registration-stage acknowledgement or failure.

type Artifacts ¶

type Artifacts struct {
	// Events contains naming-bound event declarations.
	Events *events.Catalog
	// ReadModels contains naming- and producer-bound model declarations.
	ReadModels *readmodels.Catalog
	// Projections contains store-bound compiled projection definitions.
	Projections []projections.Definition
	// Reactors contains immutable production invocation plans.
	Reactors []*reactors.Plan
	// Reducers contains immutable production fold plans.
	Reducers []*reducers.Plan
	// Constraints contains the naming-bound append constraints.
	Constraints []constraints.Definition
}

Artifacts is a detached view of the immutable production compilation results. Plans borrow the services supplied to NewClient; no constructors run when read.

type AuthenticationError ¶

type AuthenticationError = connection.AuthenticationError

AuthenticationError identifies a rejected OAuth exchange or invalid token. Credential values and token response bodies are never included.

type Client ¶

type Client struct {
	// contains filtered or unexported fields
}

Client owns a generation supervisor and frozen registries. Construct with NewClient, Dial or CaptureClient; the zero value is not usable. Do not copy it. It is safe for concurrent use; a captured identity requires successful Prepare.

func Dial ¶

func Dial(ctx context.Context, options ...ClientOption) (*Client, error)

Dial constructs and connects a client. Startup failure closes owned resources.

func NewClient ¶

func NewClient(options ...ClientOption) (*Client, error)

NewClient validates and freezes configuration without network I/O. TLS validates by default. Omitted credentials select Chronicle's public development credentials. All selected registries are captured before application preparation. Definition factories and seeders run synchronously once per distinct registry; reconnect reuses their frozen outputs without application preparation callbacks. Use NewClientContext when preparation scopes need caller cancellation.

func NewClientContext ¶

func NewClientContext(ctx context.Context, options ...ClientOption) (*Client, error)

NewClientContext is NewClient with a context for definition preparation and optional scoped construction. The context is not retained by the client.

func (*Client) Artifacts ¶

func (c *Client) Artifacts(store StoreName) (Artifacts, error)

Artifacts returns the same frozen plans used by runtime registration and invocation. It performs no I/O and is safe after Close following successful preparation, like Catalogs. An unprepared identity returns ErrNotPrepared.

func (*Client) Catalogs ¶

func (c *Client) Catalogs(store StoreName) (*events.Catalog, *readmodels.Catalog, error)

Catalogs returns immutable event and read-model catalogs selected for a nonblank store name. WithRegistryForStore replaces the default registry, not merges it. Descriptors use the client's naming policy and store-bound producer metadata, exactly as EventStore does. This performs no I/O, creates no namespace or observers, and invokes no constructors. It is safe for concurrent use, including after Close following successful preparation; catalogs describe frozen configuration, not server registration. Before preparation it returns ErrNotPrepared (or ErrClosed). A blank prepared store name is invalid.

Example ¶
package main

import (
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/readmodels"
)

type catalogExampleModel struct{ ID string }

func main() {
	registry := chronicle.NewRegistry()
	if _, err := chronicle.RegisterEvent[CustomerRegistered](registry); err != nil {
		panic(err)
	}
	if _, err := chronicle.RegisterReadModel[catalogExampleModel](registry,
		readmodels.WithObserver(readmodels.Reducer, "customer-reducer")); err != nil {
		panic(err)
	}
	client, err := chronicle.NewClient(chronicle.WithRegistry(registry))
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := client.Close(); err != nil {
			panic(err)
		}
	}()
	// No connection or namespace is needed to classify events and model producers.
	events, models, err := client.Catalogs("customers")
	if err != nil {
		panic(err)
	}
	event, found := events.Lookup(CustomerRegistered{})
	_, producer := models.Descriptors()[0].Observer()
	fmt.Println(found, event.Ref().ID, producer)
}
Output:
true CustomerRegistered customer-reducer

func (*Client) Close ¶

func (c *Client) Close() error

Close immediately cancels client-owned work and joins it. Repeated calls return the same cleanup result. Borrowed channels and token sources are never closed. Preparation is caller-owned: Close cancels it but does not join its callbacks or cleanup. Join Prepare before closing the borrowed scope provider. Use CloseContext for a bounded wait when a callback may ignore cancellation.

func (*Client) CloseContext ¶

func (c *Client) CloseContext(ctx context.Context) error

CloseContext cancels owned work immediately and waits up to ctx's deadline for cleanup. A context error means cleanup is incomplete; it continues in the background. A later Close/CloseContext can join it. Go cannot kill callbacks. Like Close, this does not join caller-owned preparation.

func (*Client) Connect ¶

func (c *Client) Connect(ctx context.Context) error

Connect waits for authenticated protocol readiness, not artifact registration. Concurrent callers share startup. The initiating context bounds the first attempt, never the client lifetime. Transient loss reconnects automatically. A terminal auth/compatibility failure is retried only by a subsequent explicit Connect. WithSkipKeepAlive uses a protected read-only startup probe instead of a logical connection session; no application-heartbeat-driven reconnect or registration replay after kernel restarts is guaranteed in that mode.

func (*Client) EventStore ¶

func (c *Client) EventStore(ctx context.Context, name StoreName, options ...StoreOption) (*EventStore, error)

EventStore connects, ensures the store/namespace and registers its explicit events, read models and constraints before returning a cached handle. Cache keys include both coordinates. Concurrent calls share registration; failed passes can be retried on the same handle. Required registrations never report successful readiness after a failed envelope.

func (*Client) EventStores ¶

func (c *Client) EventStores(ctx context.Context) ([]StoreName, error)

EventStores lists authorized logical stores after connection preflight.

func (*Client) EvictEventStores ¶

func (c *Client) EvictEventStores() error

EvictEventStores clears the store lookup cache and subsequent automatic registration membership. It does no I/O and does not cancel or join work. Already-issued handles, watches and sequences remain usable and client-owned; their explicit use can register the current generation without recaching them. A previously captured Ready/reconnect pass may finish after eviction returns. The next lookup returns a new facade sharing the coordinate's retained resources. Eviction does not invalidate decision evidence, reclaim live resources or delete server data. Repeated eviction succeeds; unprepared/closed clients return their existing ClientStateError. No preparation callbacks are invoked.

Example ¶
package main

import (
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
)

func main() {
	client, err := chronicle.NewClient()
	if err != nil {
		fmt.Println(err)
		return
	}
	defer func() { _ = client.Close() }()
	// Eviction is local, even before connecting. Old handles (if any) remain
	// usable; subsequent EventStore calls acquire new cached facades.
	fmt.Println(client.EvictEventStores() == nil)
	fmt.Println(client.EvictEventStores() == nil)
}
Output:
true
true

func (*Client) GoString ¶

func (c *Client) GoString() string

GoString is the credential-safe representation for fmt's %#v format.

func (*Client) Ready ¶

func (c *Client) Ready(ctx context.Context) error

Ready waits for a healthy generation and registrations for cached handles captured when called. A pass captured before EvictEventStores may still finish; later passes omit detached handles, even if they remain in use. Connect alone does not imply artifact readiness. Concurrently created stores carry their own barrier. Terminal failures are not retried here.

func (*Client) Shutdown ¶

func (c *Client) Shutdown(ctx context.Context) error

Shutdown stops admission, drains admitted RPCs while ctx permits, then cancels streams/supervision and joins owned workers. Deadline expiry cancels remaining work and returns ctx.Err(), never a false claim that cleanup completed.

func (*Client) String ¶

func (c *Client) String() string

String describes the client without revealing endpoints or credentials.

type ClientOption ¶

type ClientOption func(*clientConfig)

ClientOption configures construction only. Nil options are invalid. Scalar options are last-wins; authentication conflicts are errors. TLS validation cannot be disabled by a development option when an explicit TLS configuration is present.

func WithAppendOriginResolver ¶

func WithAppendOriginResolver(resolver eventsequences.AppendOriginResolver) ClientOption

WithAppendOriginResolver selects a borrowed metadata-only callback for local immediate append attribution on every store/namespace/sequence of this client. Last wins; nil disables resolution and preserves OriginFrom(ctx). Configuration is frozen at NewClient; the callback must support synchronous concurrent calls. It runs outside SDK locks with the actual append context and cannot replace it. Errors/panics fail before RPCs or notifications with a payload-free eventsequences.AppendOriginResolutionError. SDK-owned unit commits bypass it.

Example ¶
package main

import (
	"context"
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/eventsequences"
)

type invocationOriginKey struct{}

func main() {
	// An adapter reads the current invocation's snapshot, never a captured
	// request context. Chronicle needs no dependency on the command framework.
	resolver := eventsequences.AppendOriginResolver(func(ctx context.Context) (eventsequences.Origin, bool, error) {
		origin, handled := ctx.Value(invocationOriginKey{}).(eventsequences.Origin)
		return origin, handled, nil
	})
	client, err := chronicle.NewClient(chronicle.WithNoAuthentication(), chronicle.WithAppendOriginResolver(resolver))
	if err != nil {
		fmt.Println(err)
		return
	}
	defer func() { _ = client.Close() }()
	origin := eventsequences.NewOrigin()
	ctx := context.WithValue(context.Background(), invocationOriginKey{}, origin)
	selected, handled, err := resolver(ctx)
	fmt.Println("current invocation:", selected == origin && handled && err == nil)
}
Output:
current invocation: true

func WithCausationProvider ¶

func WithCausationProvider(provider metadata.CausationProvider) ClientOption

WithCausationProvider selects the borrowed complete-chain provider. Last wins; nil disables it. A handled empty chain masks context causation.

func WithCheckFirstAppendIntoAScope ¶

func WithCheckFirstAppendIntoAScope(enabled bool) ClientOption

WithCheckFirstAppendIntoAScope protects empty resolved scopes against competing first appends. It defaults to false, like C#. Last option wins. Explicit NoCheck remains unchecked. Applies to all sequences and unit-of-work commits.

func WithConnectTimeout ¶

func WithConnectTimeout(timeout time.Duration) ClientOption

WithConnectTimeout sets the startup budget (default five seconds). It must be positive.

func WithConnectionString ¶

func WithConnectionString(value string) ClientOption

WithConnectionString selects the URI; it is validated by NewClient.

func WithCorrelationProvider ¶

func WithCorrelationProvider(provider metadata.CorrelationProvider) ClientOption

WithCorrelationProvider selects the borrowed correlation provider. Last wins; nil disables it. Explicit append correlation options always bypass it.

func WithDefaultConcurrencyStrategy ¶

func WithDefaultConcurrencyStrategy(strategy eventsequences.ConcurrencyScopeStrategy) ClientOption

WithDefaultConcurrencyStrategy replaces automatic scope selection. Nil restores the default optimistic strategy. Last option wins. Implementations are borrowed for the client's lifetime and must support concurrent calls. Explicit append scopes bypass the strategy; Resolve expectations still use the client's first-append policy. No additional tail read is made for explicit expectations.

func WithDefaultSinkType ¶

func WithDefaultSinkType(kind readmodels.SinkType) ClientOption

WithDefaultSinkType selects the kernel-owned materialized sink for models without an explicit readmodels.WithSink. The default is MongoDB; SQL and InMemory are also accepted. Last wins. NoSink and unknown types fail CaptureClient or NewClient with ErrInvalidConfiguration before preparation or connection work. Passive producers still select NoSink. The option covers every selected store registry, not individual namespaces, and borrows no database resources. Selecting a provider does not configure or verify its server-side backend.

Example ¶
package main

import (
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/readmodels"
)

type sinkExampleAccount struct{ ID, Name string }
type sinkExampleAudit struct{ ID, Message string }

func main() {
	registry := chronicle.NewRegistry()
	account, err := chronicle.RegisterReadModel[sinkExampleAccount](registry)
	if err != nil {
		panic(err)
	}
	audit, err := chronicle.RegisterReadModel[sinkExampleAudit](registry,
		readmodels.WithSink(readmodels.Sink{Type: readmodels.MongoDB}))
	if err != nil {
		panic(err)
	}
	client, err := chronicle.NewClient(chronicle.WithRegistry(registry),
		chronicle.WithDefaultSinkType(readmodels.SQL))
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := client.Close(); err != nil {
			panic(err)
		}
	}()
	_, models, err := client.Catalogs("accounts") // Offline: no database or RPC.
	if err != nil {
		panic(err)
	}
	inherited, _ := models.LookupIdentifier(account.Identifier())
	explicit, _ := models.LookupIdentifier(audit.Identifier())
	fmt.Println(inherited.Sink().Type, explicit.Sink().Type)
	// Registration alone does not create instances or configure a SQL backend.
	

func WithDevelopmentDefaults ¶

func WithDevelopmentDefaults() ClientOption

WithDevelopmentDefaults explicitly permits the kernel's self-signed development certificate. Never use this option in production. Explicit validating TLS wins.

func WithEventEnrichers ¶

func WithEventEnrichers(providers ...events.EventEnricher) ClientOption

WithEventEnrichers appends borrowed callbacks in order, including duplicates. The slice is copied at option creation. Any nil entry fails configuration. Providers are synchronous and concurrent; they run without SDK locks or work leases. They are never closed. Join calls before disposing their dependencies.

Example ¶

Requires a development kernel at localhost:35000. This example is compiled; kernel-backed behavior is exercised separately by the integration suite.

package main

import (
	"context"
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/events"
	"github.com/cratis/chronicle.go/eventsequences"
	"github.com/cratis/chronicle.go/identities"
	"github.com/cratis/chronicle.go/metadata"
	"github.com/cratis/chronicle.go/transactions"
)

type DeploymentRecorded struct {
	Version    string `json:"version"`
	RecordedBy string `json:"recordedBy"`
}

// Requires a development kernel at localhost:35000. This example is compiled;
// kernel-backed behavior is exercised separately by the integration suite.
func main() {
	ctx := metadata.WithIdentity(context.Background(), identities.Identity{Subject: "deployment-service"})
	registry := chronicle.NewRegistry()
	if _, err := chronicle.RegisterEvent[DeploymentRecorded](registry); err != nil {
		fmt.Println(err)
		return
	}
	client, err := chronicle.NewClient(chronicle.WithRegistry(registry), chronicle.WithDevelopmentDefaults(),
		chronicle.WithEventEnrichers(func(ctx context.Context, _ events.TypeRef, content *events.EventContent) error {
			return content.Set("recordedBy", metadata.Identity(ctx).Subject)
		}),
		chronicle.WithRootCausation(metadata.RootCausation{ProgramIdentifier: "deployment-recorder"}),
	)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer func() {
		if err := client.Close(); err != nil {
			fmt.Println(err)
		}
	}()
	store, err := client.EventStore(ctx, "deployments")
	if err != nil {
		fmt.Println(err)
		return
	}
	result, err := store.EventLog().Append(ctx, "service-a", DeploymentRecorded{Version: "1.0"})
	if err != nil {
		fmt.Println(err)
		return
	}
	if err := result.Err(); err != nil {
		fmt.Println(err)
		return
	}

	// Begin binds audit identity/correlation; Stage enriches once, Commit reuses it.
	unit, owner, err := transactions.Begin(ctx, store.EventLog())
	if err != nil {
		fmt.Println(err)
		return
	}
	if err := unit.Stage(ctx, []eventsequences.Entry{{Source: "service-b", Event: DeploymentRecorded{Version: "1.0"}}}); err != nil {
		fmt.Println(err)
		return
	}
	batch, err := owner.Commit(ctx)
	if err != nil {
		fmt.Println(err)
		return
	}
	if err := batch.Err(); err != nil {
		fmt.Println(err)
	}
}

func WithEventTypeGenerationValidation ¶

func WithEventTypeGenerationValidation(enabled bool) ClientOption

WithEventTypeGenerationValidation enables kernel schema and migration-chain validation. Like C#, validation defaults to disabled. When enabled, changed schemas for existing generations are rejected by the kernel. NewClient requires a complete adjacent migration chain for every current generation above one.

func WithGRPCConnection ¶

func WithGRPCConnection(conn *grpc.ClientConn) ClientOption

WithGRPCConnection borrows a connection, which Close will not close. The caller owns transport security and must explicitly select WithConnectionString (the OAuth authority), WithTokenSource, or WithNoAuthentication. Use the latter when the channel already handles authentication. Client lifecycle checks still apply.

func WithGRPCStatsHandler ¶ added in v0.5.0

func WithGRPCStatsHandler(handler stats.Handler) ClientOption

WithGRPCStatsHandler borrows a gRPC stats handler for every owned connection, including replacement generations on reconnect. The last option wins; a final nil (including a typed nil) or combination with WithGRPCConnection is invalid. Chronicle never closes the handler or its providers/exporters.

This is restricted configuration for trusted callbacks, not enforced safety for arbitrary code. Callbacks must support concurrent use, return promptly, preserve cancellation, deadlines and required context values in TagRPC/TagConn, and must not reenter blocking client lifecycle methods such as Close. A handler can still block or return an unsuitable context. It does not authorize retries or change write outcome guarantees. No arbitrary dial options or interceptors are exposed by this option.

Example ¶
package main

import (
	"context"
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
	"google.golang.org/grpc/stats"
)

type exampleStatsHandler struct{}

func (exampleStatsHandler) TagRPC(ctx context.Context, _ *stats.RPCTagInfo) context.Context {
	return ctx
}
func (exampleStatsHandler) HandleRPC(context.Context, stats.RPCStats) {}
func (exampleStatsHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context {
	return ctx
}
func (exampleStatsHandler) HandleConn(context.Context, stats.ConnStats) {}

func main() {
	// Supply an application-owned, concurrency-safe handler. Chronicle reuses it
	// across owned connection generations and never closes its resources.
	client, err := chronicle.NewClient(chronicle.WithGRPCStatsHandler(exampleStatsHandler{}))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("configured without dialing")
	if err := client.Close(); err != nil {
		fmt.Println(err)
	}
}
Output:
configured without dialing

func WithIdentityProvider ¶

func WithIdentityProvider(provider metadata.IdentityProvider) ClientOption

WithIdentityProvider selects the borrowed actor provider. Last wins; nil disables it. Handled values override context identity, including NotSet.

func WithKeepAliveTimeout ¶

func WithKeepAliveTimeout(timeout time.Duration) ClientOption

WithKeepAliveTimeout sets the maximum silence between kernel heartbeats, default five seconds. It must be positive. HTTP/2 pings additionally use 60s/30s like C#.

func WithLoadBalancer ¶ added in v0.2.0

func WithLoadBalancer(balancer LoadBalancer) ClientOption

WithLoadBalancer borrows an explicit strategy, never closing it even if it implements io.Closer. Last wins. Nil (including typed nil), a loadBalancer= URI option, or WithGRPCConnection is rejected with ErrInvalidConfiguration before I/O. Unlike C#'s explicit-option precedence, conflicting URI selection is rejected rather than silently overridden. Without this option the URI strategy applies, defaulting to least-connections.

Example ¶
package main

import (
	"context"
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
)

type preferLastServer struct{}

func (preferLastServer) Next(ctx context.Context, candidates []chronicle.ServerAddress) (chronicle.ServerAddress, error) {
	if err := ctx.Err(); err != nil {
		return chronicle.ServerAddress{}, err
	}
	return candidates[len(candidates)-1], nil
}

func main() {
	client, err := chronicle.NewClient(chronicle.WithConnectionString("chronicle://one:35000,two:35000"), chronicle.WithLoadBalancer(preferLastServer{}))
	if err != nil {
		panic(err)
	}
	defer func() { _ = client.Close() }()
	// Next runs only during Connect, not during construction.
	uri, _ := chronicle.ParseConnectionString("chronicle://one:35000,two:35000")
	selected, _ := (preferLastServer{}).Next(context.Background(), uri.Addresses())
	fmt.Println(selected)
}
Output:
two:35000

func WithLogger ¶

func WithLogger(logger *slog.Logger) ClientOption

WithLogger selects the borrowed logger for SDK lifecycle and observer diagnostics. The last option wins; a final nil logger is invalid. Without this option, CaptureClient captures slog.Default (also used immediately by NewClientContext). Explicit artifact loggers override this fallback. Only logger and handler identities are captured; their underlying state and writers remain app-owned. In particular, the pristine slog default uses log.Default's current writer, which a later slog.SetDefault(custom) bridges to that custom handler. Supply an explicit logger with a stable handler/writer for strict destination isolation. Loggers/handlers are never closed, and Chronicle never calls slog.SetDefault. Handlers must support concurrent, synchronous calls and honor cancellation without blocking shutdown or reentering client lifecycle methods. Handler panics are contained, but arbitrary blocking handlers cannot be made harmless. SDK records contain only fixed operation/stage/category fields, not arbitrary errors or metadata. Handler-added fields, mutable handler state and context inspection remain the caller's responsibility. A handler running on SDK-owned work must not call Client.Close: joining that work from its own callback can deadlock. This option does not register a *slog.Logger service for artifact constructors.

Example ¶
package main

import (
	"context"
	"fmt"
	"io"
	"log/slog"

	chronicle "github.com/cratis/chronicle.go"
)

type workerEvent struct{ ID string }
type loggingWorker struct{ logger *slog.Logger }

func (w *loggingWorker) Handle(ctx context.Context, _ workerEvent) error {
	w.logger.InfoContext(ctx, "worker handled event") // Application-owned diagnostics.
	return nil
}

func main() {
	logger := slog.New(slog.NewTextHandler(io.Discard, nil))
	registry := chronicle.NewRegistry()
	if _, err := chronicle.RegisterEvent[workerEvent](registry); err != nil {
		panic(err)
	}
	// No container: pass application dependencies in the constructor closure.
	if err := chronicle.RegisterReactor[*loggingWorker](registry, func() *loggingWorker {
		return &loggingWorker{logger: logger}
	}); err != nil {
		panic(err)
	}
	client, err := chronicle.NewClientContext(context.Background(),
		chronicle.WithRegistry(registry), chronicle.WithLogger(logger))
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := client.Close(); err != nil {
			panic(err)
		}
	}()
	catalog, _, err := client.Catalogs("worker")
	if err != nil {
		panic(err)
	}
	fmt.Println(len(catalog.Descriptors()))
	// In a running worker: client.Ready(ctx), then client.EventStore(ctx, "worker").
	// Chronicle borrows the logger/handler and never closes them.
	

func WithMaxReceiveMessageSize ¶

func WithMaxReceiveMessageSize(bytes int) ClientOption

WithMaxReceiveMessageSize sets the maximum serialized gRPC message size in bytes for every RPC. Valid values are 1..2147483647; NewClient validates the final value and the last option wins. Owned channels default to 100 MiB. Borrowed channels retain their owner's defaults unless explicitly overridden per RPC; the underlying channel is never modified.

Example ¶
package main

import (
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
)

func main() {
	// A later scalar option overrides an earlier one. Neither zero nor negative
	// values mean unlimited: NewClient validates the final bound.
	client, err := chronicle.NewClient(
		chronicle.WithMaxReceiveMessageSize(4*1024*1024),
		chronicle.WithMaxReceiveMessageSize(8*1024*1024),
	)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(client)
	if err := client.Close(); err != nil {
		fmt.Println(err)
	}
}
Output:
Chronicle client

func WithMaxSendMessageSize ¶

func WithMaxSendMessageSize(bytes int) ClientOption

WithMaxSendMessageSize sets the maximum serialized gRPC message size in bytes for every RPC. Valid values are 1..2147483647; NewClient validates the final value and the last option wins. Owned channels default to 100 MiB. Borrowed channels retain their owner's defaults unless explicitly overridden per RPC; the underlying channel is never modified.

Example ¶
package main

import (
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
)

func main() {
	client, err := chronicle.NewClient(
		chronicle.WithMaxSendMessageSize(8*1024*1024),
		chronicle.WithMaxReceiveMessageSize(16*1024*1024),
	)
	if err != nil {
		fmt.Println(err)
		return
	}
	// Construction is offline; Connect or Dial performs authenticated startup.
	fmt.Println(client)
	if err := client.Close(); err != nil {
		fmt.Println(err)
	}
}
Output:
Chronicle client

func WithNamingPolicy ¶

func WithNamingPolicy(policy serialization.NamingPolicy) ClientOption

WithNamingPolicy selects property naming for every artifact in this client. The default preserves Go spelling. Explicit json tags always win. Last wins; invalid policies fail NewClient. Registry declarations are not mutated.

func WithNoAuthentication ¶

func WithNoAuthentication() ClientOption

WithNoAuthentication suppresses OAuth and authorization metadata. Conflicting URI credentials are invalid.

func WithOnConnected ¶ added in v0.2.0

func WithOnConnected(hook ConnectionHook) ClientOption

WithOnConnected adds an application hook for authenticated protocol readiness (the same point Connect returns), not registration or attachment. Options accumulate in declaration order, without deduplication, and are frozen at CaptureClient for every owned or borrowed generation. Nil is rejected with ErrInvalidConfiguration before I/O. Execution follows ConnectionHook's contract.

Example ¶
package main

import (
	"context"
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
)

func main() {
	client, err := chronicle.NewClient(chronicle.WithOnConnected(func(_ context.Context, event chronicle.ConnectionEvent) {
		// For registration readiness, call Ready or WaitForRegistration rather
		// than treating this notification as an observer attachment receipt.
		fmt.Printf("connected generation %d\n", event.Generation)
	}))
	if err != nil {
		panic(err)
	}
	defer func() { _ = client.Close() }()
	// Construction does not connect or invoke hooks. Call Connect to start.
	fmt.Println("connected hook configured")
}
Output:
connected hook configured

func WithOnDisconnected ¶ added in v0.2.0

func WithOnDisconnected(hook ConnectionHook) ClientOption

WithOnDisconnected adds an application hook for loss of a delivered ready generation, including Close/Shutdown (ErrClosed). Options accumulate in order and are frozen at CaptureClient, inherited by owned and borrowed generations. Nil is rejected with ErrInvalidConfiguration before I/O. Execution and cancellation follow ConnectionHook's contract, including coalesced generations.

Example ¶
package main

import (
	"context"
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
)

func main() {
	client, err := chronicle.NewClient(chronicle.WithOnDisconnected(func(_ context.Context, event chronicle.ConnectionEvent) {
		// ErrClosed pairs a delivered Connected on client shutdown too.
		fmt.Printf("disconnected generation %d\n", event.Generation)
	}))
	if err != nil {
		panic(err)
	}
	defer func() { _ = client.Close() }()
	fmt.Println("disconnected hook configured")
}
Output:
disconnected hook configured

func WithRegistrationRetry ¶

func WithRegistrationRetry(policy RegistrationRetry) ClientOption

WithRegistrationRetry replaces all registration retry settings. Zero fields are invalid.

func WithRegistry ¶

func WithRegistry(registry *Registry) ClientOption

WithRegistry snapshots registered events, read models and constraints at CaptureClient time (also used by NewClient); later mutations do not affect this client. Nil means an empty registry.

func WithRegistryForStore ¶

func WithRegistryForStore(name StoreName, registry *Registry) ClientOption

WithRegistryForStore replaces the default registry for one logical store.

func WithRootCausation ¶

func WithRootCausation(config metadata.RootCausation) ClientOption

WithRootCausation opts into one Root snapshot per CaptureClient, not per option creation, append or reconnect. Last wins. Existing upstream Root links win. No option means no implicit Root. Host/process information is separately opt-in.

func WithSRVResolver ¶

func WithSRVResolver(resolver SRVResolver) ClientOption

WithSRVResolver replaces system DNS for chronicle+srv connections. Nil is invalid.

func WithServices ¶

func WithServices(factory reactors.ScopeFactory) ClientOption

WithServices borrows operation resources through a container-independent seam. For a Fundamentals.Go ScopeFactory use services.WithServices from the optional github.com/cratis/chronicle.go/services adapter. No provider is owned or closed.

func WithSkipCompatibilityCheck ¶

func WithSkipCompatibilityCheck() ClientOption

WithSkipCompatibilityCheck disables structural preflight at the caller's risk.

func WithSkipKeepAlive ¶

func WithSkipKeepAlive() ClientOption

WithSkipKeepAlive omits the application Connect session, heartbeat acknowledgments and watchdog. HTTP/2 keepalive remains 60s/30s. Startup still checks compatibility unless explicitly skipped, and always probes the protected GetConnectedClients RPC before readiness. Probe errors are returned, not ignored. No logical session is registered. Configured reactors, reducers (including passive reducers), read-model reactors and explicit WithKeepAliveTimeout are therefore invalid. grpc-go may reconnect its transport, but this does not guarantee registration replay after kernel restarts without the application session. Prefer this option for short-lived clients, not observer runtimes.

Example ¶
package main

import (
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
)

func main() {
	// A short-lived, one-shot client: do not configure reactors/reducers or an
	// explicit keepalive timeout. Compatibility and the protected startup probe
	// remain enabled. HTTP/2 keepalive is unchanged.
	client, err := chronicle.NewClient(chronicle.WithSkipKeepAlive())
	if err != nil {
		fmt.Println(err)
		return
	}
	// Use client.Connect(ctx), then ordinary store/append/read APIs. Without a
	// logical session, transport reconnect is not a registration-replay guarantee.
	fmt.Println(client)
	if err := client.Close(); err != nil {
		fmt.Println(err)
	}
}
Output:
Chronicle client

func WithTLS ¶

func WithTLS(config *tls.Config) ClientOption

WithTLS snapshots a TLS configuration. Supply RootCAs and/or Certificates for PEM material loaded with crypto/x509 and tls.LoadX509KeyPair. Nil is invalid.

func WithTokenSource ¶

func WithTokenSource(source TokenSource) ClientOption

WithTokenSource uses a caller-owned credential source; it is never closed by the client.

type ClientPreparation ¶

type ClientPreparation struct {
	// contains filtered or unexported fields
}

ClientPreparation captures declarations for one client identity. Construct with CaptureClient; the zero value is invalid. Do not copy it. Prepare is synchronous and caller-owned: Close cancels but does not join preparation or its cleanup. Join any outstanding Prepare call before closing the borrowed scope provider.

func CaptureClient ¶

func CaptureClient(options ...ClientOption) (*ClientPreparation, error)

CaptureClient applies ordinary ClientOptions, validates configuration and copies selected declaration collections without SDK I/O or schema, definition, service, or selector callbacks. Arbitrary ClientOption functions themselves still execute. Immutable descriptor plans and borrowed callbacks keep their existing ownership. Logger and handler identities are captured, not their mutable destination state; see WithLogger for the pristine slog default's standard-log bridge exception.

func (*ClientPreparation) Client ¶

func (p *ClientPreparation) Client() *Client

Client returns the same unprepared identity on every call, for an explicit borrowed binding (for example dependencyinjection.BindValue). Never transfer ownership to a provider or use Client by value. Operations require Prepare.

func (*ClientPreparation) Prepare ¶

func (p *ClientPreparation) Prepare(ctx context.Context, scopes reactors.ScopeFactory) (*Client, error)

Prepare admits at most one attempt. Nil scopes selects captured WithServices or the container-free default. A typed nil or explicit scopes plus WithServices is invalid. Before admission, a nil context or invalid scopes returns an error without consuming the attempt; an already canceled context consumes it. While running (including cleanup), all calls return ErrPreparationInProgress. After completion, arguments are ignored and the exact retained result is returned, including a historically prepared identity subsequently closed. Retry requires a new CaptureClient. The selected scopes are borrowed for preparation and runtime. Callbacks may handle ErrNotPrepared; denied operations never poison publication.

type ClientStateError ¶

type ClientStateError struct {
	// Operation is the fixed SDK operation name.
	Operation string
	// contains filtered or unexported fields
}

ClientStateError reports a denied operation with payload-free state diagnostics. It never includes configuration, coordinates, application objects or errors.

func (*ClientStateError) Error ¶

func (e *ClientStateError) Error() string

Error returns the operation and state category only.

func (*ClientStateError) Format ¶

func (e *ClientStateError) Format(s fmt.State, _ rune)

Format preserves payload-free diagnostics for every formatting verb.

func (*ClientStateError) Unwrap ¶

func (e *ClientStateError) Unwrap() error

Unwrap exposes ErrNotPrepared, ErrPreparationInProgress or ErrClosed.

type CompatibilityError ¶

type CompatibilityError struct {
	// ServerVersion identifies the kernel that rejected the descriptor.
	ServerVersion string
	// Details preserves every incompatibility returned by the kernel.
	Details []string
}

CompatibilityError reports a failed structural compatibility preflight. Details are the kernel's incompatibility descriptions; no operations are admitted.

func (*CompatibilityError) Error ¶

func (e *CompatibilityError) Error() string

type ConnectionEvent ¶ added in v0.2.0

type ConnectionEvent struct {
	// Generation is the same monotonically increasing counter as RegistrationOutcome.Generation.
	Generation uint64
	// ConnectionID is the logical session ID; empty with WithSkipKeepAlive.
	ConnectionID string
	// Address is the selected host:port; empty for WithGRPCConnection.
	Address string
	// Err is nil for Connected and the termination cause for Disconnected.
	// Close and Shutdown pair a delivered Connected with Disconnected(ErrClosed).
	Err error
}

ConnectionEvent describes authenticated protocol readiness or its loss, not registration completion or observer attachment. Use WaitForRegistration/Ready for those barriers. Borrowed channels report logical sessions, not channel state.

type ConnectionHook ¶ added in v0.2.0

type ConnectionHook func(ctx context.Context, event ConnectionEvent)

ConnectionHook observes a transition and cannot reject a connection. A single client-owned dispatcher orders events C(N), D(N), C(N+1), invoking one event's hooks concurrently and joining them before the next event. Hooks never run on the caller goroutine or under SDK locks. Supervision, reconnect, RPCs and Ready never wait for hooks; hooks may call Ready, EventStore and WaitForRegistration. If a generation ends before its Connected hooks start, both notifications are skipped and its number is never reported; gaps also come from attempts that never became ready, so a gap alone does not prove coalescing. At most two events remain pending. Every delivered Connected gets exactly one Disconnected, including on Close. Connected's context ends with the generation or client; Disconnected uses the client lifetime context, which may already be canceled. Panics are contained, values discarded, and logged through WithLogger with operation client, stage connected/disconnected and category panic. Close/Shutdown join running hooks; hooks must not call Close/Shutdown themselves. CloseContext bounds that wait. WithSkipKeepAlive reports ConnectionID "" and Disconnected only on Close or Shutdown: no session stream is watched for loss in that mode.

type ConnectionString ¶

type ConnectionString struct {
	// contains filtered or unexported fields
}

ConnectionString is an immutable parsed URI. Its zero value is not a usable endpoint. String and GoString never expose credentials. Parsing does not resolve DNS or connect.

func ParseConnectionString ¶

func ParseConnectionString(value string) (ConnectionString, error)

ParseConnectionString parses Chronicle's multi-host grammar, including encoded credentials and IPv6. Unknown options, partial credentials and conflicting auth modes fail without echoing the input. Unsupported known modes parse for diagnostics but are rejected by NewClient. Unlike C#, certificate validation defaults to enabled.

Example ¶
package main

import (
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
)

func main() {
	connection, err := chronicle.ParseConnectionString("chronicle://client:secret@localhost")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(connection)
}
Output:
chronicle://localhost:35000

func (ConnectionString) Addresses ¶

func (s ConnectionString) Addresses() []ServerAddress

Addresses returns a defensive copy of the configured endpoints.

func (ConnectionString) GoString ¶

func (s ConnectionString) GoString() string

GoString is the credential-safe representation used by fmt with %#v.

func (ConnectionString) IsSRV ¶

func (s ConnectionString) IsSRV() bool

IsSRV reports whether DNS SRV discovery was requested.

func (ConnectionString) String ¶

func (s ConnectionString) String() string

String renders only scheme and addresses; credentials and query values are omitted.

type EnvelopeError ¶

type EnvelopeError = wire.EnvelopeError

EnvelopeError preserves kernel authorization/validation/execution failure despite gRPC OK.

type EventStore ¶

type EventStore struct {
	// contains filtered or unexported fields
}

EventStore is a concurrency-safe registered store/namespace handle. It borrows its Client; Close the client to release resources. Cache eviction detaches this facade, not its resources or ability to perform explicit operations.

func (*EventStore) Captures ¶

func (s *EventStore) Captures() *captures.Service

Captures returns explicit capture validation/saving. Saving never activates a capture; unsupported source/mapping capabilities remain inspectable errors.

func (*EventStore) Compliance ¶

func (s *EventStore) Compliance() *compliance.Manager

Compliance returns the namespace-bound PII key lifecycle manager. Erasure and reauthorization reach all stores in this namespace, never other namespaces.

func (*EventStore) Constraints ¶

func (s *EventStore) Constraints() []constraints.Definition

Constraints returns the frozen definitions registered for this logical store. The slice is a copy. Enforcement and removal are kernel operations at append time.

func (*EventStore) EventLog ¶

func (s *EventStore) EventLog() *eventsequences.Sequence

EventLog returns the primary sequence, cached for this store handle.

func (*EventStore) EventSequence ¶

func (s *EventStore) EventSequence(id events.SequenceID) (*eventsequences.Sequence, error)

EventSequence returns the cached handle for a nonblank sequence ID. Concurrent calls share the handle and its append subscriptions within this store/namespace.

func (*EventStore) EventTypes ¶

func (s *EventStore) EventTypes() *events.Catalog

EventTypes returns the frozen catalog registered before this handle was published.

func (*EventStore) ExternalServices ¶

func (s *EventStore) ExternalServices() *externalservices.Service

ExternalServices returns store-wide HTTP/database declarations. The kernel, never the SDK, owns external connections and credential acquisition.

func (*EventStore) Identities ¶

func (s *EventStore) Identities() *IdentityManager

Identities returns a handle without I/O. Rename requires existing readiness; unlike ordinary store operations, it never starts or awaits registration.

func (*EventStore) Jobs ¶

func (s *EventStore) Jobs() *jobs.Service

Jobs returns namespace-scoped job administration. Job absence is not evidence of successful completion. Calls retain the store's registration/lifecycle barrier.

func (*EventStore) Name ¶

func (s *EventStore) Name() StoreName

Name returns the logical store name.

func (*EventStore) Namespace ¶

func (s *EventStore) Namespace() Namespace

Namespace returns this handle's isolated namespace.

func (*EventStore) Namespaces ¶

func (s *EventStore) Namespaces() *Namespaces

Namespaces returns administration for this logical store, not only the selected namespace.

func (*EventStore) Observers ¶

func (s *EventStore) Observers() *observation.Service

Observers returns namespace-scoped observer administration. Persistent Remove deliberately affects every namespace in this store; local Unregister methods only cancel this client's subscriptions and do not delete server records.

func (*EventStore) Patterns ¶

func (s *EventStore) Patterns() *patterns.Service

Patterns returns an immutable store/namespace query facade borrowing this client. Calls retain authentication, registration and generation cancellation barriers. Construction performs no I/O and creates no cross-store cache.

Example ¶

This example compiles but requires a running local development kernel to run. Development defaults trust the disposable server certificate and use its development credentials; use verified TLS and your TokenSource in production.

package main

import (
	"context"
	"fmt"
	"time"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/patterns"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	client, err := chronicle.NewClient(chronicle.WithConnectionString("chronicle://localhost:35000"), chronicle.WithDevelopmentDefaults())
	if err != nil {
		fmt.Println(err)
		return
	}
	defer func() {
		if err := client.Close(); err != nil {
			fmt.Println(err)
		}
	}()
	store, err := client.EventStore(ctx, "orders", chronicle.WithNamespace("tenant-a"))
	if err != nil {
		fmt.Println(err)
		return
	}
	moment := time.Date(2024, 1, 15, 9, 30, 0, 0, time.FixedZone("", 2*3600))
	facets := patterns.NewFacetSet(map[patterns.FacetName]patterns.FacetValue{patterns.AggregateType: "Order"})
	result, err := store.Patterns().GetPatternsAt(ctx, "demo-scope", &moment, facets, patterns.QueryOptions{})
	if err != nil {
		fmt.Println(err)
		return
	}
	if len(result.Data) == 0 {
		fmt.Println("No established behavior; do not claim to know.")
		return
	}
	for _, answer := range result.Data {
		fmt.Println(answer.Action(), answer.Confidence, answer.Occurrences)
	}
}

func (*EventStore) PrepareSeeds ¶

func (s *EventStore) PrepareSeeds(seeder seeding.Seeder) (*SeedBatch, error)

PrepareSeeds invokes a seeder synchronously against this store's frozen event catalog, without I/O. The returned batch retains serialized entries for retry; callbacks never run during Register. Use a new batch for corrected definitions. Like registered seeders, unscoped entries are global, not handle-local.

func (*EventStore) PreviewProjection ¶

func (s *EventStore) PreviewProjection(ctx context.Context, declaration string, sequence ...events.SequenceID) (projections.Preview, error)

PreviewProjection is the historical spelling of QueryProjection. It uses the same kernel RPC and returns the same owned result and error contracts.

func (*EventStore) Projections ¶

func (s *EventStore) Projections() []projections.Definition

Projections returns a detached list of this store's immutable compiled definitions. Use QueryProjection for kernel-executed, unregistered PDL queries.

func (*EventStore) QueryProjection ¶

func (s *EventStore) QueryProjection(ctx context.Context, declaration string, sequence ...events.SequenceID) (projections.QueryResult, error)

QueryProjection executes Projection Declaration Language in this namespace without registering it. Omitting sequence selects event-log; at most one nonblank sequence is accepted. A declaration may omit its read-model target for schema inference. Kernel 19.29.4 drops inferred properties (Chronicle#4539, fixed in 19.32.1); use an explicitly registered target schema on that kernel. The caller controls cancellation/deadlines; no retries or Go callbacks run. Invalid PDL returns *projections.QueryError.

func (*EventStore) ReadModels ¶

func (s *EventStore) ReadModels() *readmodels.Service

ReadModels returns a namespace-bound service and its immutable model catalog. Reads retain the store's registration barrier across connection generations.

func (*EventStore) RegisterProjection ¶

func (s *EventStore) RegisterProjection(ctx context.Context, declaration projections.Declaration) (result ProjectionRegistration, err error)

RegisterProjection adds one new ordinary projection and its new read model. It reuses the existing declaration/builders and exact known event generations. Replacements, variants, globals, protected or relationship/derived shapes and other runtime artifact families are unsupported. Existing readers keep their original snapshots; new ReadModels calls select the new root. Publication invalidates every previously issued/enrolled decision guard in this store.

Cancellation before publication changes nothing. After publication the desired addition survives failure and reconnect. ErrDestructiveRegistrationUnknown refuses future additions but does not withdraw ordinary acknowledged readiness. It can accompany Published=true and a successful Outcome after a cumulative attempt became uncertain and a retry acknowledged the same root.

Example ¶

This example requires a local development kernel. Tests compile it; the kernel integration suite separately exercises registration/materialization.

package main

import (
	"context"
	"fmt"
	"time"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/projections"
	"github.com/cratis/chronicle.go/readmodels"
)

type StockReceived struct {
	Quantity int `json:"quantity"`
}
type StockView struct {
	ID       string `json:"id" chronicle:"key"`
	Quantity int    `json:"quantity" chronicle:"set(StockReceived)"`
}

// This example requires a local development kernel. Tests compile it; the
// kernel integration suite separately exercises registration/materialization.
func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	registry := chronicle.NewRegistry()
	if _, err := chronicle.RegisterEvent[StockReceived](registry); err != nil {
		panic(err)
	}
	client, err := chronicle.Dial(ctx, chronicle.WithRegistry(registry), chronicle.WithDevelopmentDefaults())
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := client.Close(); err != nil {
			panic(err)
		}
	}()
	store, err := client.EventStore(ctx, "stock")
	if err != nil {
		panic(err)
	}

	model, err := readmodels.Define[StockView]()
	if err != nil {
		panic(err)
	}
	registration, err := store.RegisterProjection(ctx, projections.ModelBound(model))
	if err != nil {
		// Published=true means the addition is retained, not rolled back.
		// Retry registration with store.WaitForRegistration using a fresh context;
		// do not resubmit this declaration or blindly retry application appends.
		fmt.Println("published:", registration.Published, "registration error:", err)
		return
	}
	if !registration.Outcome.IsSuccess() {
		panic("registration not acknowledged")
	}
	result, err := store.EventLog().Append(ctx, "item-1", StockReceived{Quantity: 3})
	if err != nil {
		panic(err)
	}
	if err := result.Err(); err != nil {
		panic(err)
	}
	reader := readmodels.For(store.ReadModels(), model)
	instance, err := reader.Get(ctx, "item-1")
	if err != nil {
		panic(err)
	}
	// Projection sinks are asynchronous: one read is not an observer SLA.
	fmt.Println("materialized:", instance.Exists)
}

func (*EventStore) Subscriptions ¶

func (s *EventStore) Subscriptions() *eventstoresubscriptions.Service

Subscriptions returns a store-wide cross-store subscription service. Namespace handles share definitions; forwarding uses matching namespaces in both stores. Operations retain the store's registration barrier across connection generations.

func (*EventStore) UnregisterReactor ¶

func (s *EventStore) UnregisterReactor(ctx context.Context, id reactors.ID) error

UnregisterReactor disconnects and joins this store/namespace's reactor across current and retired generations. Unknown IDs are ignored. It retains the removal across reconnect; it does not remove persisted kernel state. Reconnect may overlap old and new generation delivery until old callbacks return, as in C#. A context error means cleanup is still joining. Do not synchronously unregister this reactor from its own callback.

func (*EventStore) UnregisterReadModelReactor ¶

func (s *EventStore) UnregisterReadModelReactor(ctx context.Context, id reactors.ID) error

UnregisterReadModelReactor cancels and joins current and retired generation callbacks for this namespace. Removal survives reconnect. Do not call it from its own callback; cancellation cannot forcibly stop uncooperative user code.

func (*EventStore) UnregisterReducer ¶

func (s *EventStore) UnregisterReducer(ctx context.Context, id reducers.ID) error

UnregisterReducer cancels and joins this store/namespace's reducer across current and retired generations. Unknown IDs are ignored. Removal survives reconnect; persisted kernel definitions are not deleted. A canceled wait does not abandon cleanup. Never call synchronously from this reducer's own fold. Passive reads remain available from the frozen plan after stream removal.

func (*EventStore) WaitForRegistration ¶

func (s *EventStore) WaitForRegistration(ctx context.Context) (RegistrationOutcome, error)

WaitForRegistration connects if necessary and joins or starts a required pass for this namespace and current generation. Failures are explicit and retryable on a later call; successful store-wide definitions are shared across namespaces. A failed reactor Open is reported with its ID while subscription retries continue; a later call observes successful readiness once Open recovers.

func (*EventStore) Webhooks ¶

func (s *EventStore) Webhooks() *webhooks.Service

Webhooks returns store-wide outgoing webhook definitions, not a local HTTP server.

type IdentityManager ¶

type IdentityManager struct {
	// contains filtered or unexported fields
}

IdentityManager borrows its namespace-bound EventStore and Client. It starts no work and needs no Close. Concurrent calls use independent invocation state. Obtain it through EventStore.Identities; the zero value is unusable.

func (*IdentityManager) Rename ¶

func (m *IdentityManager) Rename(ctx context.Context, subject string, name identities.Name, correlation ...metadata.CorrelationID) (result IdentityRenameResult, err error)

Rename requests one namespace-scoped display-name update for an opaque subject. Accepted UTF-8 nonblank strings are preserved exactly. Correlation accepts zero or one value; explicit zero selects a fresh ID without consulting its provider. Audit providers run once outside SDK locks/leases. Actor/causation are frozen in context, but the command wire persists neither; correlation is sent as a header.

The current generation/root must already be fully registered. Rename neither connects nor starts/joins readiness. Separate Ready calls are outside its privacy boundary. It uses at most two whole-namespace listings and one command, sharing the caller deadline without retry, polling or a default timeout. Listing requires permission and fits configured message bounds. Observation is not atomic and does not prove this command caused the name or historical cache convergence.

A Rename holds the per-Client, per-store definition flight only during its own command RPC; destructive definition registrations (full-set projection registration) use the same flight. Renames are not serialized across their reads. A Rename on the same Client and store (any namespace) that reaches a readiness check while anything else holds that flight (another Rename's command or a destructive definition registration) fails with reason registration_not_ready: before its own command it returns IdentityRenameNotDispatched; past its command (post_read) it returns IdentityRenameUnknown with Acknowledged true. Renames through other clients or processes are unaffected. Decide retries from the returned disposition, not from concurrency alone.

SDK processing of this invocation discards borrowed error/panic payloads without invoking unknown error hooks. Independent background operations and borrowed implementations' own logging/explicit reentrant operations are not covered.

Example ¶

This compiling example requires a running development kernel and an identity already stored by an ordinary actor append. Development defaults are not a production TLS/authentication configuration.

package main

import (
	"context"
	"errors"
	"fmt"
	"time"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/identities"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	client, err := chronicle.Dial(ctx, chronicle.WithDevelopmentDefaults())
	if err != nil {
		fmt.Println(err)
		return
	}
	defer func() { _ = client.Close() }()
	store, err := client.EventStore(ctx, "example") // Establish readiness separately.
	if err != nil {
		fmt.Println(err)
		return
	}
	result, err := store.Identities().Rename(ctx, "person-42", identities.Name("Jane Austen"))
	switch {
	case errors.Is(err, identities.ErrNotFound):
		fmt.Println("No stored identity for that exact subject")
	case errors.Is(err, chronicle.ErrIdentityRenameUnknown):
		fmt.Println("Do not retry blindly; acknowledged:", result.Acknowledged)
	case err != nil:
		fmt.Println(err)
	default:
		fmt.Println("Requested name observed")
	}
}

type IdentityRenameDisposition ¶

type IdentityRenameDisposition uint8

IdentityRenameDisposition distinguishes dispatch evidence from name observation.

const (
	// IdentityRenameNotDispatched means this invocation entered no rename RPC.
	IdentityRenameNotDispatched IdentityRenameDisposition = iota
	// IdentityRenameRefused means a valid command envelope explicitly refused execution.
	IdentityRenameRefused
	// IdentityRenameUnknown means mutation or its confirmation is uncertain.
	IdentityRenameUnknown
	// IdentityRenameObserved means exactly one subject had the requested post-read name.
	IdentityRenameObserved
)

type IdentityRenameError ¶

type IdentityRenameError struct {
	// contains filtered or unexported fields
}

IdentityRenameError reports fixed SDK categories without retaining borrowed errors or diagnostic payloads. Use Phase/Reason and errors.Is/As for inspection.

func (*IdentityRenameError) Error ¶

func (e *IdentityRenameError) Error() string

func (*IdentityRenameError) Phase ¶

func (e *IdentityRenameError) Phase() string

Phase returns prepare, pre_read, command or post_read.

func (*IdentityRenameError) Reason ¶

func (e *IdentityRenameError) Reason() string

Reason returns a fixed SDK reason, never a supplied diagnostic or identity.

func (*IdentityRenameError) Unwrap ¶

func (e *IdentityRenameError) Unwrap() []error

Unwrap exposes detached fixed categories, including context errors when known.

type IdentityRenameResult ¶

type IdentityRenameResult struct {
	// Disposition is observation/dispatch evidence, not an atomic matched-row receipt.
	Disposition IdentityRenameDisposition
	// Acknowledged means a valid successful command envelope, not that a row changed.
	// It survives subsequent cancellation or failed confirmation.
	Acknowledged bool
	// RequestCorrelationID is the once-selected operation correlation; zero before preparation.
	RequestCorrelationID metadata.CorrelationID
	// ResponseCorrelationID preserves command response presence; nil means absent.
	// It is independent of the request and owned by this result.
	ResponseCorrelationID *metadata.CorrelationID
}

IdentityRenameResult contains only SDK-owned disposition and copied correlations. It never contains identity records, names, subjects or server diagnostics.

type LoadBalancer ¶ added in v0.2.0

type LoadBalancer interface {
	// Next selects one of candidates using the attempt's cancellation budget.
	Next(ctx context.Context, candidates []ServerAddress) (ServerAddress, error)
}

LoadBalancer selects a candidate once per connection attempt, including a single-address URI and every reconnect. Calls are serialized per client, outside SDK locks. Next receives a fresh copy in URI order, or SRV priority/weight order after re-resolution. It may mutate or retain that copy, but must return an original candidate. The attempt context is bounded by WithConnectTimeout and canceled by Close; implementations must honor cancellation and must not call Close/Shutdown from Next. Errors, panics and non-candidates fail before dialing or OAuth and become LoadBalancerError; reconnect retries with normal backoff. A selection error matching a context error is treated like an attempt timeout: the first Connect keeps waiting on its own context while the client retries. The selected address is also the OAuth authority. A shared balancer must support concurrent calls from different clients.

type LoadBalancerError ¶ added in v0.2.0

type LoadBalancerError struct {
	// Err is the application failure, available through Unwrap. Panic values are
	// discarded; errors for panics and non-candidates contain no application data.
	Err error
}

LoadBalancerError reports a failed selection. It is always transient for the connection supervisor, even when its cause is an authentication status.

func (*LoadBalancerError) Error ¶ added in v0.2.0

func (*LoadBalancerError) Error() string

Error returns a fixed diagnostic without formatting the cause.

func (*LoadBalancerError) Format ¶ added in v0.2.0

func (e *LoadBalancerError) Format(s fmt.State, _ rune)

Format redacts the application cause for every fmt verb.

func (*LoadBalancerError) Unwrap ¶ added in v0.2.0

func (e *LoadBalancerError) Unwrap() error

Unwrap returns the selection failure for errors.Is and errors.As.

type Namespace ¶

type Namespace = metadata.Namespace

Namespace identifies isolated state within a store.

type Namespaces ¶

type Namespaces struct {
	// contains filtered or unexported fields
}

Namespaces administers namespaces for one logical store. Handles are concurrency-safe.

func (*Namespaces) Ensure ¶

func (n *Namespaces) Ensure(ctx context.Context, namespace Namespace) error

Ensure idempotently creates a nonblank namespace in this store.

func (*Namespaces) List ¶

func (n *Namespaces) List(ctx context.Context) ([]Namespace, error)

List returns existing namespaces; namespace selection is not authorization.

type PreparationError ¶

type PreparationError = artifacts.PreparationError

PreparationError reports a failed preparation boundary. Its text (including formatted values) never contains application error or panic payloads. Unwrap preserves ordinary causes for errors.Is/As. Recovered panic values are discarded. Failed preparation publishes no client, but cannot roll back application effects.

type ProjectionRegistration ¶

type ProjectionRegistration struct {
	Published bool                // Published means the complete local desired snapshot includes the addition.
	Outcome   RegistrationOutcome // Outcome is the real registration pass, not materialization or attachment evidence.
}

ProjectionRegistration separates retained local publication from registration acknowledgement. Published remains true after cancellation or remote failure. Use WaitForRegistration to retry; submitting the declaration again conflicts.

type RegistrationError ¶

type RegistrationError struct {
	// Outcome is the completed pass; modifying its slices does not affect the client.
	Outcome RegistrationOutcome
}

RegistrationError preserves both partial progress and the inspectable cause.

func (*RegistrationError) Error ¶

func (e *RegistrationError) Error() string

func (*RegistrationError) Unwrap ¶

func (e *RegistrationError) Unwrap() error

Unwrap preserves context, envelope and transport error inspection.

type RegistrationOutcome ¶

type RegistrationOutcome = registration.Outcome

RegistrationOutcome records a generation-scoped pass, acknowledged stages, failures, attempts and whether a delayed retry is pending. Zero is not success.

type RegistrationRetry ¶

type RegistrationRetry = registration.Policy

RegistrationRetry configures bounded, idempotent registration passes. Defaults are five attempts, 2s initial/30s maximum jittered delay and 30s per attempt. After a failed transient pass, one owned worker retries after MaximumDelay. Deterministic validation/authorization failures require a new explicit wait.

type Registry ¶

type Registry struct {
	// contains filtered or unexported fields
}

Registry is an isolated collection of explicit declarations. Its zero value is usable. Registration and snapshots are concurrency-safe. NewClient takes a frozen snapshot; adding declarations afterwards does not mutate existing clients.

func NewRegistry ¶

func NewRegistry() *Registry

NewRegistry returns an empty registry; there is no global discovery or init hook.

func (*Registry) AddConstraint ¶

func (r *Registry) AddConstraint(definition constraints.Definition) error

AddConstraint adds a built constraint after all its participating and removal event types have been registered. Duplicate names, zero definitions and foreign descriptors fail with ErrInvalidConfiguration without changing the registry. Group mutually exclusive types in one UniqueEventTypes definition rather than adding separate definitions with the same name. Definitions are immutable.

func (*Registry) AddProjection ¶

func (r *Registry) AddProjection(declaration projections.Declaration) error

AddProjection admits immutable authoring metadata. Duplicate identities/models fail immediately; forward event references and the entire graph are resolved atomically by NewClient against this registry, never a global event catalog.

func (*Registry) ConfigureDeclaredConstraint ¶

func (r *Registry) ConfigureDeclaredConstraint(name string, configure func(*constraints.Builder)) error

ConfigureDeclaredConstraint explicitly composes a model-bound constraint with the existing builder (for example IgnoreCasing, scope, On or RemovedWith). It does not replace AddConstraint's duplicate protection. The callback runs once per NewClient registry snapshot, before connection work, using declaration paths (subsequently rebound through the client's naming policy); it must not perform I/O or mutate the registry. It may run concurrently across clients. Name must identify a derived constraint and cannot be changed by the callback. Duplicate composition registrations are rejected.

Example ¶
package main

import (
	"fmt"

	chronicle "github.com/cratis/chronicle.go"
	"github.com/cratis/chronicle.go/constraints"
	"github.com/cratis/chronicle.go/events"
)

type AccountRegistered struct {
	Email string  `json:"email" chronicle:"unique(name=\"email\",message=\"Email already registered\",sequences=[\"event-log\"])"`
	Owner *string `json:"owner" chronicle:"subject"`
}
type AccountClosed struct{}

func main() {
	registry := chronicle.NewRegistry()
	_, err := chronicle.RegisterEvent[AccountRegistered](registry,
		events.WithUnique(events.Unique{Name: "account-lifecycle"}), events.WithTags("accounts"))
	if err != nil {
		fmt.Println(err)
		return
	}
	_, err = chronicle.RegisterEvent[AccountClosed](registry, events.WithRemoveConstraints("email", "account-lifecycle"))
	if err != nil {
		fmt.Println(err)
		return
	}
	// Use the existing builder to explicitly compose a tagged declaration.
	if err = registry.ConfigureDeclaredConstraint("email", func(builder *constraints.Builder) { builder.IgnoreCasing() }); err != nil {
		fmt.Println(err)
		return
	}
	client, err := chronicle.NewClient(chronicle.WithRegistry(registry))
	if err != nil {
		fmt.Println(err)
		return
	}
	defer func() {
		if err := client.Close(); err != nil {
			fmt.Println(err)
		}
	}()
	catalog, _, err := client.Catalogs("accounts")
	if err != nil {
		fmt.Println(err)
		return
	}
	owner := "customer-42"
	event := AccountRegistered{Email: "ada@example.test", Owner: &owner}
	descriptor, _ := catalog.Lookup(event)
	subject, present := descriptor.ResolveSubject(event)
	fmt.Println(subject, present, descriptor.Tags())
}
Output:
customer-42 true [accounts]

func (*Registry) WithProjection ¶

func (r *Registry) WithProjection(declaration projections.Declaration) (*Registry, error)

WithProjection returns a detached registry with a replacement producer for the declaration's model. It removes that model's prior projection/factory/reducer, retains all other registrations, and never mutates r. NewClient performs normal complete compilation/validation. This supports scenario-local inline overrides without introducing another discovery pipeline. The model must already be registered.

type SRVResolver ¶

type SRVResolver = connection.SRVResolver

SRVResolver is a cancelable DNS service-record resolver. The caller owns it.

type SeedBatch ¶

type SeedBatch struct {
	// contains filtered or unexported fields
}

SeedBatch is a manually prepared, immutable batch bound to one store. It is safe for concurrent Register calls. Failure retains the batch; success consumes pending work. The client owns the connection; a batch owns no transport resources.

func (*SeedBatch) Register ¶

func (b *SeedBatch) Register(ctx context.Context) error

Register waits for observers and sends through the kernel's idempotent seeding protocol. Calls share a single flight; after success later calls are no-ops. Transient failures use the client's bounded registration retry policy; a later call retries retained work after any failure. No append RPC is retried here.

type ServerAddress ¶

type ServerAddress struct {
	// Host is a DNS name or unbracketed IP address.
	Host string
	// Port is the TCP port.
	Port uint16
}

ServerAddress is a Chronicle endpoint. Port defaults to 35000 during parsing.

func (ServerAddress) String ¶

func (a ServerAddress) String() string

String returns host:port, with IPv6 brackets when required.

type StoreName ¶

type StoreName = metadata.StoreName

StoreName identifies a logical event store.

type StoreOption ¶

type StoreOption func(*storeConfig)

StoreOption selects a store handle. Options are last-wins; nil is invalid.

func WithNamespace ¶

func WithNamespace(namespace Namespace) StoreOption

WithNamespace selects a nonblank namespace; it does not grant authorization.

type Token ¶

type Token = connection.Token

Token is a bearer credential and optional expiration. Formatting redacts its value.

type TokenInvalidator ¶

type TokenInvalidator interface {
	Invalidate()
}

TokenInvalidator is optionally implemented by a TokenSource. Invalidate discards cached credentials after Unauthenticated; it must be concurrency-safe and nonblocking. The failed operation is not retried. The next operation requests a fresh token.

type TokenSource ¶

type TokenSource = connection.TokenSource

TokenSource supplies credentials, honors context cancellation and supports concurrent calls. Sources may also implement TokenInvalidator to discard rejected credentials.

type ValidationResult ¶

type ValidationResult = wire.ValidationResult

ValidationResult is a structured kernel validation diagnostic.

Directories ¶

Path Synopsis
Package captures authors explicit capture declarations for the kernel.
Package captures authors explicit capture declarations for the kernel.
Package chronicletest provides isolated scenarios using production Chronicle client plans.
Package chronicletest provides isolated scenarios using production Chronicle client plans.
Package compliance describes personal-data and confidentiality classifications and the kernel-owned lifecycle of erasable subject keys.
Package compliance describes personal-data and confidentiality classifications and the kernel-owned lifecycle of erasable subject keys.
Package constraints declares kernel append-time uniqueness and its violation diagnostics.
Package constraints declares kernel append-time uniqueness and its violation diagnostics.
Package contracts supplies the canonical Chronicle descriptor and public generated RPC packages.
Package contracts supplies the canonical Chronicle descriptor and public generated RPC packages.
bcl
Package declarations parses versioned Chronicle field declarations.
Package declarations parses versioned Chronicle field declarations.
Package events defines persisted event identities and immutable type catalogs.
Package events defines persisted event identities and immutable type catalogs.
Package eventsequences appends registered events and reads Chronicle sequences.
Package eventsequences appends registered events and reads Chronicle sequences.
Package eventstoresubscriptions provisions cross-store outbox to inbox forwarding.
Package eventstoresubscriptions provisions cross-store outbox to inbox forwarding.
examples
batches command
Command batches protects loaded history while appending one ordered atomic batch.
Command batches protects loaded history while appending one ordered atomic batch.
compliance command
A disposable development example.
A disposable development example.
constraints command
Command constraints reserves an address, rejects a competing claim, then releases it.
Command constraints reserves an address, rejects a competing claim, then releases it.
decisions command
Command decisions demonstrates an admitted optimistic decision and one owned atomic commit.
Command decisions demonstrates an admitted optimistic decision and one owned atomic commit.
evolution command
This offline example declares evolution; the kernel, not this program, migrates stored payloads.
This offline example declares evolution; the kernel, not this program, migrates stored payloads.
getting-started command
Command getting-started appends one event to a local development kernel.
Command getting-started appends one event to a local development kernel.
integrations command
The integrations example prepares definitions without a kernel or DI container.
The integrations example prepares definitions without a kernel or DI container.
projections command
Command projections materializes an inventory read model from an event.
Command projections materializes an inventory read model from an event.
reactors command
Run against a local development kernel: go run ./examples/reactors.
Run against a local development kernel: go run ./examples/reactors.
readmodels command
Command readmodels registers a catalog and distinguishes absent model state.
Command readmodels registers a catalog and distinguishes absent model state.
reducers command
A plain-Go passive reducer.
A plain-Go passive reducer.
seeding command
The seeding example writes reference data to a development kernel.
The seeding example writes reference data to a development kernel.
unitofwork command
Command unitofwork stages nested work in one ordered atomic transaction.
Command unitofwork stages nested work in one ordered atomic transaction.
watches command
Command watches demonstrates best-effort changes and convention-based callbacks.
Command watches demonstrates best-effort changes and convention-based callbacks.
Package externalservices declares kernel-owned HTTP and database integrations.
Package externalservices declares kernel-owned HTTP and database integrations.
Package identities describes actors responsible for events, not authentication credentials.
Package identities describes actors responsible for events, not authentication credentials.
internal
administration
Package administration shares failure semantics for explicit administration mutations.
Package administration shares failure semantics for explicit administration mutations.
appendorigin
Package appendorigin carries SDK-owned unit completion attribution across package boundaries.
Package appendorigin carries SDK-owned unit completion attribution across package boundaries.
artifacts
Package artifacts shares observer construction, resolver and ownership semantics.
Package artifacts shares observer construction, resolver and ownership semantics.
classifications
Package classifications freezes provider results by Go member identity, so later naming and producer binding cannot invoke application classifiers again.
Package classifications freezes provider results by Go member identity, so later naming and producer binding cannot invoke application classifiers again.
clientoptions
Package clientoptions provides module-private client construction options.
Package clientoptions provides module-private client construction options.
conceptfixtures
Package conceptfixtures supplies real domain codecs shared by client and kernel tests.
Package conceptfixtures supplies real domain codecs shared by client and kernel tests.
contentencoding
Package contentencoding carries the module-private outgoing encoder seam.
Package contentencoding carries the module-private outgoing encoder seam.
decision
Package decision is the SDK-only issuance and transport boundary for optimistic decision guards.
Package decision is the SDK-only issuance and transport boundary for optimistic decision guards.
derivedchildrenfixtures
Package derivedchildrenfixtures supplies the Go shapes corresponding to the captured C# derived children definition (projections/testdata/derived-children) for unit and real-kernel contract tests.
Package derivedchildrenfixtures supplies the Go shapes corresponding to the captured C# derived children definition (projections/testdata/derived-children) for unit and real-kernel contract tests.
derivedfixtures
Package derivedfixtures supplies the Go shapes corresponding to captured .NET fixtures for codec and typed-consumer contract tests.
Package derivedfixtures supplies the Go shapes corresponding to captured .NET fixtures for codec and typed-consumer contract tests.
diagnostics
Package diagnostics emits SDK-owned, bounded operational records.
Package diagnostics emits SDK-owned, bounded operational records.
discovery
Package discovery shares C# handler precedence and catalog-scoped event families.
Package discovery shares C# handler precedence and catalog-scoped event families.
enumfixtures
Package enumfixtures supplies declared Go counterparts of the immutable Chronicle 19.29.4/Fundamentals 7.19.6 packaged enum capture.
Package enumfixtures supplies declared Go counterparts of the immutable Chronicle 19.29.4/Fundamentals 7.19.6 packaged enum capture.
jsonescape
Package jsonescape matches System.Text.Json's JavaScriptEncoder.Default.
Package jsonescape matches System.Text.Json's JavaScriptEncoder.Default.
jsonstructure
Package jsonstructure validates untrusted JSON before any lossy map decoding.
Package jsonstructure validates untrusted JSON before any lossy map decoding.
kernelcapability
Package kernelcapability records which kernel defect fixes a connected server is known to contain.
Package kernelcapability records which kernel defect fixes a connected server is known to contain.
observerruntime
Package observerruntime owns ordered observer delivery and acknowledgement.
Package observerruntime owns ordered observer delivery and acknowledgement.
observersource
Package observersource shares event-origin inference across observer kinds.
Package observersource shares event-origin inference across observer kinds.
outgoing
Package outgoing is the private audit and outgoing-content preparation bridge.
Package outgoing is the private audit and outgoing-content preparation bridge.
preparation
Package preparation contains payload-free outgoing callback boundaries.
Package preparation contains payload-free outgoing callback boundaries.
reactoreffects
Package reactoreffects defines the internal scenario effect-recording boundary.
Package reactoreffects defines the internal scenario effect-recording boundary.
registration
Package registration coordinates immutable, generation-scoped registration passes.
Package registration coordinates immutable, generation-scoped registration passes.
Package jobs administers namespace-scoped kernel jobs.
Package jobs administers namespace-scoped kernel jobs.
Package metadata carries immutable correlation, causation and actor snapshots in contexts.
Package metadata carries immutable correlation, causation and actor snapshots in contexts.
Package observation administers observers and waits for processing of committed append coordinates.
Package observation administers observers and waits for processing of committed append coordinates.
Package patterns queries server-established behavior.
Package patterns queries server-established behavior.
Package projections compiles model-bound tags and fluent declarations into the same immutable kernel definition.
Package projections compiles model-bound tags and fluent declarations into the same immutable kernel definition.
internal/first
Package first supplies a catalog-ambiguity fixture.
Package first supplies a catalog-ambiguity fixture.
internal/second
Package second supplies a catalog-ambiguity fixture.
Package second supplies a catalog-ambiguity fixture.
Package reactors declares convention-discovered and explicit event handlers.
Package reactors declares convention-discovered and explicit event handlers.
Package readmodels provides explicit model declarations and namespace-bound, presence-aware reads.
Package readmodels provides explicit model declarations and namespace-bound, presence-aware reads.
Package reducers declares sequential read-model folds.
Package reducers declares sequential read-model folds.
Package seeding declares global and namespace-specific event seeds.
Package seeding declares global and namespace-specific event seeds.
Package serialization compiles one immutable field plan for JSON and JSON Schema.
Package serialization compiles one immutable field plan for JSON and JSON Schema.
Package services opts Chronicle into Fundamentals.Go operation scopes.
Package services opts Chronicle into Fundamentals.Go operation scopes.
Package transactions provides strict, ordered units of work.
Package transactions provides strict, ordered units of work.
Package webhooks declares kernel-owned outgoing HTTP event webhooks.
Package webhooks declares kernel-owned outgoing HTTP event webhooks.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL