Skip to content

Annotations

Marks a data class as a Chronicle event type.

ParameterTypeDefaultDescription
idString""Stable identifier. Defaults to class name.
generationInt1Schema version. Increment when shape changes.
tombstoneBooleanfalseSignals event source retirement.
import io.cratis.chronicle.events.EventType
@EventType
data class OrderPlaced(val orderId: String, val totalAmount: Double)

Omitting id is the common case — Chronicle uses OrderPlaced as the identifier automatically.


Marks a class as a Chronicle reactor. Each public method becomes a handler for the event type of its first parameter.

ParameterTypeDefaultDescription
idString""Stable identifier. Defaults to class name.
eventSequenceStringevent logThe event sequence to observe. Overridden by @EventSequence.

A handler takes the event, and optionally an EventContext carrying the event’s metadata:

import io.cratis.chronicle.events.EventContext
import io.cratis.chronicle.observation.Reactor
@Reactor
class OrderNotifications {
fun orderPlaced(event: OrderPlaced) {
println("Order ${event.orderId} placed")
}
fun orderShipped(event: OrderShipped, context: EventContext) {
println("Order ${event.orderId} shipped at ${context.occurred}")
}
}

Supply an explicit id only when you need the identifier to survive class renames.


Excludes a reactor, or a single handler, from replay. Put it on the class and the whole reactor is registered as non-replayable, so redaction, revision, and observer rewind never replay it. Put it on one method and only that handler is skipped when an event arrives as part of a replay — the reactor’s other handlers still replay.

Use it for side effects where running again is worse than never running again.

import io.cratis.chronicle.observation.OnceOnly
import io.cratis.chronicle.observation.Reactor
@Reactor
class PaymentNotifications {
@OnceOnly
fun orderPlaced(event: OrderPlaced) {
println("Charging for ${event.orderId} - never repeated on replay")
}
}

Marks a reactor handler as the one to run while events are being replayed. When an event type has a handler marked with this, it takes over for the duration of the replay and the everyday handler does not also run. Without one, the everyday handler keeps running during replay.

Use @OnceOnly instead when the side effect should simply not happen again on replay.

import io.cratis.chronicle.observation.Reactor
import io.cratis.chronicle.observation.Replay
@Reactor
class ShippingNotifications {
fun orderPlaced(event: OrderPlaced) {
println("Emailing the customer about ${event.orderId}")
}
@Replay
fun orderPlacedDuringReplay(event: OrderPlaced) {
println("Rebuilding ${event.orderId} without emailing anyone")
}
}

Marks a class as a reducer. Each public method folds one event type into the read model.

ParameterTypeDefaultDescription
idString""Stable identifier. Defaults to class name.
eventSequenceStringevent logThe event sequence to observe. Overridden by @EventSequence.
isActiveBooleantrueWhether the kernel runs the reducer.

A handler takes the event, the state so far, and optionally an EventContext. The state is null until the first event for an event source has been folded in.

import io.cratis.chronicle.events.EventContext
import io.cratis.chronicle.observation.Reducer
@Reducer
class OrderSummaryReducer {
fun orderPlaced(event: OrderPlaced, state: OrderSummary?): OrderSummary =
(state ?: OrderSummary()).copy(orderId = event.orderId)
fun orderShipped(
event: OrderShipped,
state: OrderSummary?,
context: EventContext
): OrderSummary = (state ?: OrderSummary()).copy(status = "shipped at ${context.occurred}")
}

Marks a data class as a Chronicle read model.

ParameterTypeDefaultDescription
idString""Stable identifier. Defaults to class name.
displayNameString""Human-readable label. Defaults to name.
import io.cratis.chronicle.readModels.ReadModel
@ReadModel
data class OrderSummary(val orderId: String = "", val status: String = "pending")

Marks a model-bound read model’s projection as passive — registered with the kernel but not actively run. A passive projection’s read model is computed on demand rather than kept up to date as events arrive. Placed on the read model class.

No parameters.

import io.cratis.chronicle.events.EventType
import io.cratis.chronicle.projections.FromEvent
import io.cratis.chronicle.readModels.Passive
import io.cratis.chronicle.readModels.ReadModel
@EventType
data class SnapshotCreated(val data: String)
@Passive
@ReadModel
@FromEvent(SnapshotCreated::class)
data class HistoricalSnapshot(val data: String = "")

Marks a class as a Chronicle projection, or overrides the projection identifier on a model-bound read model. It is optional — when omitted, the class simple name is used as the identifier.

For a declarative projection the read model type is inferred from the IProjectionFor<T> type parameter. For a model-bound projection the annotated class is itself the read model.

ParameterTypeDefaultDescription
idString""Stable identifier. Defaults to class name.
eventSequenceStringevent logThe event sequence to observe. Overridden by @EventSequence.
import io.cratis.chronicle.projections.FromEvent
import io.cratis.chronicle.projections.Projection
import io.cratis.chronicle.readModels.ReadModel
@ReadModel
@Projection(eventSequence = "outbox")
@FromEvent(OrderPlaced::class)
data class OutboxOrderTracking(val orderId: String = "")

Points an observer — a reactor, a reducer or a projection — at the event sequence it observes. This is the standalone alternative to the eventSequence parameter on @Reactor, @Reducer and @Projection; reach for it when the sequence is the only thing being configured, so the observer keeps its conventional identifier.

ParameterTypeDefaultDescription
valueString(required)The event sequence to observe.
import io.cratis.chronicle.observation.EventSequence
import io.cratis.chronicle.observation.Reactor
@Reactor
@EventSequence("outbox")
class OutboxOrderNotifications {
fun orderPlaced(event: OrderPlaced) {
println("Order ${event.orderId} placed, observed from the outbox")
}
}

When both this annotation and the eventSequence parameter are present, this annotation wins.

There is no @EventLog counterpart. In the .NET client it exists to override the inbox routing that [EventStore] on an event type turns on — and the Kotlin client has no @EventStore, so an observer without an explicit sequence already reads from the event log.


Marks a class as a Chronicle constraint definition. The class must implement IConstraint.

ParameterTypeDefaultDescription
idString""Stable identifier. Defaults to class name.

Marks a property or an event type as needing to be unique - the model-bound alternative to a hand-written IConstraint. On a property, no two events of that type may carry the same value; applying it with the same id to properties on more than one event type groups them under one constraint, checked across all of them combined. On an event type, at most one instance of that type may exist per event source.

ParameterTypeDefaultDescription
idString""Constraint name, defaulting to the property/class.
messageString""Message for a constraint violation.
import io.cratis.chronicle.constraints.Unique
import io.cratis.chronicle.events.EventType
@EventType
data class ProjectCreated(@Unique val name: String, val description: String)
@EventType
@Unique
data class WorkspaceClaimed(val slug: String)

Pair it with @RemoveConstraint on a removal event to release the value for reuse.


Marks an event type as releasing a named @Unique constraint when it is appended - typically a deletion or lifecycle-ending event. Repeatable, so one event can release more than one constraint.

Only one event type may release a given constraint name with this client - if more than one declares the same name, registration keeps the first one it finds and reports the rest, rather than silently overwriting on every reconnect.

ParameterTypeDefaultDescription
valueString(required)Name of the constraint to release.
import io.cratis.chronicle.constraints.RemoveConstraint
import io.cratis.chronicle.events.EventType
@EventType
@RemoveConstraint("UniqueWorkspaceSlug")
data class WorkspaceArchived(val workspaceId: String)

Marks a class as a Chronicle event seeder. The class must implement ICanSeedEvents.


Marks a property, constructor parameter, field, or type as personally identifiable information. Chronicle encrypts annotated values at rest using a per-subject key. See PII Attribute for the full compliance model this participates in.

Applying it directly to a property works, but the declare-once pattern is to put it on a ConceptAs<T> type instead: every event or read model property that reuses that concept is PII automatically, with nothing to repeat at each call site. It can also mark a composite value object type, in which case every value the type holds is treated as PII wherever that type appears.

@Pii cannot be applied to an EventSourceId concept — Chronicle uses the event source id to look up the encryption key for every other PII value belonging to that source, so encrypting the id itself would make its own key unfindable.

ParameterTypeDefaultDescription
descriptionString""Note about what the field holds.
import io.cratis.chronicle.compliance.Pii
import io.cratis.chronicle.events.EventType
// Declared once on the concept, every event or read model property that
// reuses EmailAddress is PII automatically.
@Pii(description = "Customer email address")
data class EmailAddress(override val value: String) : io.cratis.chronicle.concepts.ConceptAs<String>
@EventType
data class CustomerRegistered(
val customerId: String,
val email: EmailAddress
)

Overrides the type a class is represented as in the generated JSON schema. Apply it to a type that brings its own serializer and writes something other than its own shape on the wire — a value object collapsed into a single string, for instance. Without it the generated schema would describe the Kotlin shape, and the value would not round-trip through the kernel.

ParameterTypeDefaultDescription
typeKClass<*>(required)Type the class is represented as.
import io.cratis.chronicle.schemas.JsonSchemaType
// Money serializes as a single string ("42.50 USD") through its own
// serializer, so the schema needs to describe a string, not an object
// with amount/currency fields.
@JsonSchemaType(String::class)
data class Money(val amount: Double, val currency: String)

Pointing the annotation at the annotated type itself throws SelfReferencingJsonSchemaType — generating that schema would recurse forever.


Marks a property as the compliance subject - the identity a release decrypts @Pii values against. IReadModelsService.release uses it to pick which property carries the subject; without it, release falls back to a property named id (case-insensitive), the convention every read model followed before this annotation existed.

Add it whenever a read model’s subject is not its id - for example a support ticket keyed by ticket id but holding a customer’s PII, where the customer, not the ticket, is who the encryption key belongs to.

No parameters.

import io.cratis.chronicle.Subject
import io.cratis.chronicle.readModels.ReadModel
@ReadModel
data class SupportTicketSummary(
val id: String = "",
@Subject val customerId: String = "",
val topic: String = ""
)

Applied to a read model class to declare that its fields are mapped from an event type. Part of the annotation-based projection style. It is repeatable — apply it once per event type the read model projects from.

ParameterTypeDefaultDescription
eventTypeKClass<*>(required)The source event class.
keyString"EventSourceId"Correlates events to instances.
import io.cratis.chronicle.events.EventType
import io.cratis.chronicle.projections.FromEvent
import io.cratis.chronicle.readModels.ReadModel
@EventType
data class OrderShipped(val orderId: String, val carrier: String)
@ReadModel
@FromEvent(OrderPlaced::class)
@FromEvent(OrderShipped::class)
data class OrderTracking(
val orderId: String = "",
val status: String = ""
)

Marks a property on an event as the key a projection correlates that event to a read model instance by. @FromEvent’s key parameter takes this today as a bare property-name string; @Key is the strongly-typed alternative for consumers that resolve the key by reflection instead of by name.

No parameters.

import io.cratis.chronicle.events.EventType
import io.cratis.chronicle.keys.Key
@EventType
data class PickTicketOpened(@Key val orderId: String, val warehouse: String)

Marks a function as deriving its key from the event context — for example the event source id, or a correlation id — rather than from a property on the event payload.

ParameterTypeDefaultDescription
propertyString(required)EventContext property to use.
import io.cratis.chronicle.keys.ContextKey
class PickTicketHandlers {
@ContextKey(property = "EventSourceId")
fun pickTicketOpened(event: PickTicketOpened) = Unit
}

IKeyBuilder/KeyBuilder build the same resolution fluently instead of declaratively — see the io.cratis.chronicle.keys package.


Applied to a read model property to override auto-mapping by name and declare which event field populates it. It is repeatable, so one property can be mapped differently per event type.

ParameterTypeDefaultDescription
propertyPathString""Path to the source property.
eventTypeKClass<*>Nothing::classEvent it applies to.

propertyPath is a dot-separated path on the event, and defaults to the annotated property’s own name. The default eventType applies the mapping to every event in the read model’s @FromEvent list that has a matching source property.


Sets a read model property to a constant value when a specific event occurs, or clears it back to no value. It is repeatable, so the same property can hold a different constant per event type.

ParameterTypeDefaultDescription
eventTypeKClass<*>The event class to trigger on.
valueString""The constant’s literal text. Ignored if clear.
clearBooleanfalseClears instead of setting value.

Kotlin annotation parameters cannot be nullable, so value is always a plain string — a numeric or boolean constant is written out as its literal text ("42", "true") rather than as a typed argument. Set clear = true to clear the property instead, which is Kotlin’s equivalent of passing null for value in the .NET client.

import io.cratis.chronicle.events.EventType
import io.cratis.chronicle.projections.FromEvent
import io.cratis.chronicle.projections.SetValue
import io.cratis.chronicle.readModels.ReadModel
@EventType
data class SubscriptionActivated(val placeholder: Boolean = true)
@EventType
data class SubscriptionCanceled(val placeholder: Boolean = true)
@ReadModel
@FromEvent(SubscriptionActivated::class)
@FromEvent(SubscriptionCanceled::class)
data class Subscription(
@SetValue(SubscriptionActivated::class, value = "active")
@SetValue(SubscriptionCanceled::class, value = "canceled")
val status: String = ""
)

Maps a read model property from a named EventContext property, for one specific event. Unlike @FromAll / @FromEvery, which map a context property across every event the projection observes, this ties the mapping to a single event type.

ParameterTypeDefaultDescription
eventTypeKClass<*>The event class this mapping applies to.
contextPropertyString""The context property to read from.

contextProperty defaults to the annotated property’s own name.

import io.cratis.chronicle.events.EventType
import io.cratis.chronicle.projections.FromEvent
import io.cratis.chronicle.projections.SetFrom
import io.cratis.chronicle.projections.SetFromContext
import io.cratis.chronicle.readModels.ReadModel
@EventType
data class OrderPlacedForAudit(val customerName: String)
@ReadModel
@FromEvent(OrderPlacedForAudit::class)
data class AuditedOrder(
@SetFrom("customerName", OrderPlacedForAudit::class)
val customerName: String = "",
@SetFromContext(OrderPlacedForAudit::class, contextProperty = "occurred")
val orderedAt: String = ""
)

Populates a read model property by joining against another event type on its event source id. Use it when the triggering event doesn’t carry the read model’s own key but instead references another entity by id.

ParameterTypeDefaultDescription
eventTypeKClass<*>The event class to join against.
onString""Read model property to join on.
eventPropertyNameString""Property on eventType to read.

on and eventPropertyName both default to the annotated property’s own name.

import io.cratis.chronicle.projections.Join
import io.cratis.chronicle.readModels.ReadModel
@ReadModel
data class OrderWithCustomerEmail(
val orderId: String = "",
val customerId: String = "",
@Join(
eventType = CustomerRegistered::class,
on = "customerId",
eventPropertyName = "email"
)
val customerEmail: String = ""
)

Declares that a collection property is populated with child read model instances created or updated by a specific event type.

ParameterTypeDefaultDescription
eventTypeKClass<*>The event class that creates children.
keyString"EventSourceId"Event property identifying the child.
identifiedByString""Child’s own identity property.
parentKeyString"EventSourceId"Event property for the parent.

identifiedBy defaults to the child type’s id/key property, falling back to EventSourceId.


Marks a single nullable property as a nested sub-object built from its own type’s @FromEvent/@SetFrom annotations.

No parameters.


Declares which event clears (nulls out) a @Nested property. Placed on the nested type itself, alongside its @FromEvent annotation.

ParameterTypeDescription
eventTypeKClass<*>The event class that clears the nested object.

Turns a property into an occurrence counter for a specific event type — every time eventType fires for the read model instance, the property is bumped by one.

ParameterTypeDefaultDescription
eventTypeKClass<*>The event class to count occurrences of.
constantKeyString""See “Constant keys” below.

Bumps a numeric property up (@Increment) or down (@Decrement) by one every time eventType fires for the read model instance.

ParameterTypeDefaultDescription
eventTypeKClass<*>The event class that bumps the property.
constantKeyString""See “Constant keys” below.
import io.cratis.chronicle.projections.Increment
import io.cratis.chronicle.readModels.ReadModel
@ReadModel
data class OrderShipmentStats(
val orderId: String = "",
@Increment(OrderShipped::class) val shipmentCount: Int = 0
)

Constant keys: when constantKey is set on @Count, @Increment, or @Decrement, every occurrence of eventType updates the same read model instance, identified by that constant value, instead of the projection’s normal per-instance key resolution.


Adds (@AddFrom) or subtracts (@SubtractFrom) the value of an event property into/from a numeric property every time eventType fires.

ParameterTypeDefaultDescription
eventTypeKClass<*>The event class carrying the value.
eventPropertyNameString""Property on eventType to read.

eventPropertyName defaults to the annotated property’s own name.


Projects a property from every event type the projection observes, rather than a single one. @FromAll and @FromEvery are equivalent aliases.

ParameterTypeDefaultDescription
propertyString""Triggering event property to read from.
contextPropertyString""Event context property to read from.

Both default to the annotated property’s own name. contextProperty (e.g. the causing identity) takes precedence over property when both are set.


Marks a projection as forward-only — it cannot be rewound and replayed from scratch. Placed on the read model class.

No parameters.


Declares which event removes a read model instance, or — when placed on a @ChildrenFrom property — which event removes a single child from that collection.

ParameterTypeDefaultDescription
eventTypeKClass<*>The event class that triggers removal.
keyString"EventSourceId"Event property for what to remove.
parentKeyString"EventSourceId"Event property for the parent.

parentKey only applies when removing a single child from a @ChildrenFrom collection.


Like @RemovedWith, but the removal event doesn’t directly carry the id — it’s resolved via a join instead.

ParameterTypeDefaultDescription
eventTypeKClass<*>The event class that triggers the removal.
keyString"EventSourceId"Property used in the join lookup.

Disables AutoMap. Placed on a read model, @ChildrenFrom element, or @Nested type, it disables AutoMap entirely for that type. Placed on a single property, it excludes just that property from AutoMap while siblings keep auto-mapping.

No parameters.


Marks a class as a discoverable Chronicle webhook definition. The class must implement IWebhookDefiner.

ParameterTypeDefaultDescription
idString""Stable identifier. Defaults to class name.
targetUrlStringThe URL to send events to.
import io.cratis.chronicle.webhooks.IWebhookDefiner
import io.cratis.chronicle.webhooks.IWebhookDefinitionBuilder
import io.cratis.chronicle.webhooks.Webhook
@Webhook(targetUrl = "https://hooks.example.com/orders")
class OrderPlacedWebhook : IWebhookDefiner {
override fun define(builder: IWebhookDefinitionBuilder) {
builder.withEventType(OrderPlaced::class)
}
}