Events & the System's Narrative
In EvidentSource, events are the record. Every State View your application reads is computed from them.
Most databases treat current state as the source of truth: rows are updated, old values overwritten, and history is something you reconstruct from logs if you’re lucky. EvidentSource inverts this. The event log is the record, and state is a function of it.
Why events
Section titled “Why events”- “What happened?” becomes a query. Not a ticket, and not an investigation through audit logs stitched together from three systems. A read.
- State becomes debuggable. If a State View looks wrong, the events that produced it are all still there, in order.
- History isn’t overwritten. Every earlier revision of a database stays readable, so you can see state as it stood at any point. See Bi-temporal reads.
- AI agents get context. An event records what happened, and the transaction that appended it records how it arrived. See Decision Traces & AI-Readiness.
The event shape
Section titled “The event shape”An event is a CloudEvent. A client sets:
type: what happened, such ascom.todomvc.todo.created.source: the stream the event belongs to, such astodos. There is no separate stream field. Send a bare name, not a URI; the server expands it.subject: the entity the event is about. It is optional.data: the payload.time: the event’s effective time, when the fact took effect. It may be in the past or the future. Leave it out, and the effective time is when the event was recorded.
Stream, subject and type are the three attributes every query and append condition selects on. Nothing else is selectable, including time.
What the server adds
Section titled “What the server adds”- A new
id. Every event gets a fresh id, and the reply maps each id you sent to the one assigned, so give the events in one transaction distinct ids. sequence: the event’s revision in its database.recordedtime: when the database recorded it, by the writer’s clock, and never earlier than the transaction before it.causationid: the id of the transaction that appended it.correlationid: when the caller supplies one.authtype: the kind of caller that appended it, and for an authenticated caller itsauthid.
The server sets authtype, authid and the other auth attributes itself, from the caller’s credentials or, for an asynchronous command, from the caller the command’s record names, and discards any copy a client puts on an event. They are context, not proof: they are not signed, and the server checks the token they came from only where it is configured to verify tokens, so read authid together with authtype.
Revisions
Section titled “Revisions”Each event takes the next revision of its database: 1, 2, 3, with no gaps. A transaction’s events take consecutive revisions, and a read at a revision always sees whole transactions.
Limits
Section titled “Limits”id,typeandsubjectmust be non-blank and under 256 bytes.- An event’s estimated stored size must not exceed 64 KiB. The estimate includes a fixed overhead, so the usable payload is somewhat smaller.
timecan’t be a leap second.
How events flow
Section titled “How events flow”- A client appends events directly in a transaction, or sends a command.
- For a command, a State Change component’s
decidefunction reads the database, checks the command, and returns events. - EvidentSource appends the events atomically, if every append condition still holds.
- State Views fold the new events: on the next read by default, or as they commit if the view is kept current.
- Consumers follow the database with
SubscribeDatabaseUpdates, which reports its revision after each committed transaction, and read the new events. A consumer that falls behind can miss an update, so read from the last revision you processed, not just the one reported.
The log is append-only, and every State View is computed from it.