Event Metadata
Every event in Causet carries automatic metadata set by the runtime. This metadata is always present and cannot be overridden by the payload.
Reserved Fields
Three fields are automatically populated on every event from the event envelope:
| Field | Type | Description |
|---|---|---|
type | string | The event type name — e.g. ARTIST_FOLLOWED |
ts | bigint | Event timestamp in milliseconds since epoch (Unix ms) |
entity_id | string | The entity this event belongs to, resolved from entity_expr |
These fields are available as event.type, event.ts, and event.entity_id in projection derive expressions and rule contexts.
Note: Do not declare
type,ts, orentity_idin yourpayloaddefinition. The compiler will reject them.
Where Reserved Fields Come From
When causet-runtime emits an event, it populates the envelope:
{
"type": "ARTIST_FOLLOWED",
"ts": 1719331200000,
"entity_id": "user-123",
"payload": {
"user_id": "user-123",
"artist_id": "artist-456"
}
}typeis set from theevent_typefield of theemitoperation.tsis the deterministic timestamp of the intent (injected by the runtime — notDate.now()).entity_idis resolved from the declaredentity_exprfor the event type.
Additional Envelope Fields
Beyond the three reserved fields, the event envelope also carries:
| Field | Description |
|---|---|
fork_id | The fork this event belongs to |
ir_version | The IR version that was active when this event was emitted |
sequence_number | Monotonically increasing per (entity_id, fork_id) |
intent_id | UUID of the intent that caused this event |
These fields are stored in ledger_events columns. They are accessible via the gRPC entity browser but are not part of the event payload available in rules or derive expressions.
Intent Reference
Every event is traceable to the intent that caused it via intent_id. This links ledger_events to intent_status:
SELECT
le.event_type,
le.entity_id,
le.ts,
is_.action,
is_.status,
is_.processed_at
FROM ledger_events le
JOIN intent_status is_ ON le.intent_id = is_.id
WHERE le.entity_id = 'user-123'
ORDER BY le.sequence_number;This enables full causality tracing: given any event, you can find the original intent that caused it.
Fork ID
The fork_id on each event scopes it to a specific fork (e.g. production, staging). Events from different forks are stored in the same ledger_events table, filtered by fork_id. Projection events carry the fork_id and the derived tenant_schema for routing to the correct projection table.
Correlation in Payloads
For complex flows involving multiple services, sagas, or cross-entity events, it is good practice to include explicit correlation fields in event payloads:
events:
TICKET_PURCHASED:
state: user
entity_expr: event.user_id
payload:
user_id: string
show_id: string
artist_id: string
ticket_id: string
price: number
purchased_at: datetime
correlation_id: string # optional: trace ID from the originating request
caused_by: string # optional: intent ID or upstream event IDCarrying correlation_id through the event chain lets you trace a user action through the full distributed system:
HTTP request → intent (correlation_id: abc123)
→ TICKET_PURCHASED (correlation_id: abc123)
→ SHOW_CAPACITY_DECREMENTED (correlation_id: abc123)
→ PURCHASE_CONFIRMATION_SENT (correlation_id: abc123)Using event.ts in Projections
event.ts is the canonical timestamp for an event and should be used in projections instead of the database NOW():
projections:
user_purchases:
source_events: [TICKET_PURCHASED]
fields:
user_id: TEXT
ticket_id: TEXT
purchased_at: BIGINT
derive:
user_id: event.user_id
ticket_id: event.ticket_id
purchased_at: event.ts # use event timestamp, not wall-clockUsing event.ts ensures that a projection rebuild produces identical timestamps to the original projection materialization.
Using event.ts in Rules
In rule expressions and payload values, use intent.ts for the current timestamp (not Date.now() or system time):
core:
rules:
- name: emit_purchase
when: {}
then:
- op: emit
event_type: TICKET_PURCHASED
payload:
purchased_at: intent.ts # deterministic — safe for replayintent.ts is the deterministic timestamp injected by the runtime for the intent’s execution context.
Related Pages
- Defining Events — full event DSL syntax
- Events — how event metadata is stored
- Dispatching Events — how the
emitop populates metadata