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
| Responsibility | How |
|---|---|
| Accept intents | HTTP POST (port 8080) or via Kafka causet.intents.v1 |
| Validate action exists | Check against active IR for the fork |
| Acquire per-entity cursor | Cursor lock on (entity_id, stream, fork) — serial ordering |
| Evaluate preflight rules | Read-only validation; can reject with typed code |
| Evaluate core rules | Mutations, relationship ops; state changes |
| Evaluate side_effects | Emit, submit, schedule |
| Persist ledger events | Append to ledger_events (R2DBC, sync) |
| Update entity snapshots | Write current entity state to entity_snapshots |
| Publish to Kafka | Fire-and-forget after ledger commit |
| Expose entity browser | gRPC 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:
- An intent that returns 200 has a corresponding event in
ledger_events. Ledger writes are synchronous. - Per-entity ordering: intents for the same entity are serialized. No concurrent rule evaluation for the same entity.
- Determinism: same entity state + same intent → same events. Expressions cannot access wall-clock, randomness, or external systems.
- 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
| Port | Protocol | Purpose |
|---|---|---|
| 8080 | HTTP | Intent submission, health check |
| 9090 | gRPC | Entity browser (EntityBrowserGrpcService) |
Health
curl http://localhost:8080/actuator/healthResponse:
{
"status": "UP",
"components": {
"db": { "status": "UP" },
"kafka": { "status": "UP" }
}
}Next steps
- Intents — intent structure and submission
- Rules Engine — how rules are evaluated
- The Ledger — append-only event store
- Entity Snapshots — cached entity state
- Configuration — environment variables