Runtime Overview

The causet-runtime-service is the write path of Causet. It accepts intents (commands), evaluates your declared rules, appends events to the ledger, and publishes to Kafka for async projection fan-out.


Responsibilities

ResponsibilityHow
Accept intentsHTTP POST (port 8080) or via Kafka causet.intents.v1
Validate action existsCheck against active IR for the fork
Acquire per-entity cursorCursor lock on (entity_id, stream, fork) — serial ordering
Evaluate preflight rulesRead-only validation; can reject with typed code
Evaluate core rulesMutations, relationship ops; state changes
Evaluate side_effectsEmit, submit, schedule
Persist ledger eventsAppend to ledger_events (R2DBC, sync)
Update entity snapshotsWrite current entity state to entity_snapshots
Publish to KafkaFire-and-forget after ledger commit
Expose entity browsergRPC EntityBrowserGrpcService on port 9090
Report health/actuator/health

Processing pipeline

POST /api/v1/intents/submit


 Resolve active IR for fork
 (causet-saas-cloud GetActiveRelease → S3 cached)


 Validate action exists in IR


 Acquire cursor lock
 (per entity_id + stream + fork)


 Load entity snapshot


 Execute preflight rules
   → If reject: return 400 with typed code, release cursor


 Execute core rules
   → Mutations: set/add/sub/push/filter/...
   → Relationship ops: relationship_create/remove


 Execute side_effects rules
   → emit → queued
   → submit → queued
   → schedule → queued


 Persist ledger_events + entity_snapshots (sync, R2DBC)


 Release cursor lock


 Return response to caller (sync)


 Publish causet.projection-events.v1 (async)
 Publish queued submits (async)

The runtime contract

What the runtime guarantees:

  1. An intent that returns 200 has a corresponding event in ledger_events. Ledger writes are synchronous.
  2. Per-entity ordering: intents for the same entity are serialized. No concurrent rule evaluation for the same entity.
  3. Determinism: same entity state + same intent → same events. Expressions cannot access wall-clock, randomness, or external systems.
  4. Preflight is read-only: a rejected intent makes no state changes.

What the runtime does not guarantee:

  • Exactly-once Kafka delivery: projection fan-out is at-least-once. Projection handlers must be idempotent.
  • Synchronous projections: read models lag behind writes (eventual consistency).
  • Arbitrary side effects: no HTTP, SQL, or external calls inside rules.

Active IR version

The runtime resolves the active IR version for each fork from causet-saas-cloud. IR artifacts are cached from S3/MinIO.

When a new release is deployed, the runtime picks up the new IR on the next request for that fork (TTL-based cache refresh).

The IR contains:

  • All stream definitions
  • All event schemas
  • All action (intent) definitions
  • All compiled rule bytecode

Tech stack

  • Language: Java 21
  • Framework: Spring WebFlux (reactive, non-blocking)
  • Database: R2DBC (reactive, no JDBC)
  • Serialization: JSON
  • Kafka client: reactive Kafka consumer/producer
  • gRPC: for entity browser service

Ports

PortProtocolPurpose
8080HTTPIntent submission, health check
9090gRPCEntity browser (EntityBrowserGrpcService)

Health

curl http://localhost:8080/actuator/health

Response:

{
  "status": "UP",
  "components": {
    "db": { "status": "UP" },
    "kafka": { "status": "UP" }
  }
}

Next steps