Runtime Architecture
causet-runtime-service is the write-side engine of Causet. It receives intents, evaluates rules deterministically, appends events to the ledger, and publishes to Kafka. Every write to Causet flows through this service.
Core Responsibilities
- Receive intents via HTTP (
:8080) or from the Kafka intent ingress topic - Resolve the active IR version for the fork from
causet-saas-cloud - Acquire a per-entity cursor lock for serial ordering
- Execute the rules pipeline:
preflight → core → side_effects - Append
ledger_eventsto thecausetPostgreSQL database (R2DBC) - Update
entity_snapshotsto reflect new state - Write
intent_statusrecord with outcome - Publish enriched events to
causet.projection-events.v1(async, fire-and-forget) - Expose
EntityBrowserGrpcServiceon gRPC port:9090 - Serve
/actuator/healthfor liveness and readiness probes
Intent Processing Pipeline
Step-by-Step: Processing a Single Intent
- Receive — intent arrives via HTTP
POST /intentor fromcauset.intents.v1Kafka topic. - Resolve IR — the active
irVersionfor the fork is resolved fromcauset-saas-cloud. The runtime artifactcauset.runtime.jsonis loaded from S3 (cached in Redis byirVersion). - Validate action — the named action must exist in the active IR. Unknown actions are rejected immediately.
- Acquire cursor lock — a per-entity advisory lock is acquired in the
causetdatabase. This serializes all writes to a single entity, preventing concurrent intent processing from producing conflicting state. - Load snapshot — the current
entity_snapshotfor(entity_id, fork_id)is loaded. This is the materialized current state used for rule evaluation. - Run preflight rules — read-only validation rules. Any rule may issue
op: rejectwith a code and message. If any preflight rule rejects, processing stops and the rejection is returned to the caller. No writes occur. - Run core rules — state mutation rules. These produce
ledger_events(the events to append). Allowed operations:set,unset,increment,decrement,array_push,array_remove,array_set_at,array_remove_at. - Run side-effects rules — emit, schedule, and submit operations. These produce additional ledger events or downstream intents.
- Append ledger events — all produced events are appended to
ledger_eventsin a single R2DBC transaction. - Update snapshot —
entity_snapshotsis upserted with the new entity state derived from applying the emitted events. - Write intent status —
intent_statusis written with the processing outcome (success or rejection code). - Release lock — the cursor lock is released.
- Publish to Kafka — enriched projection events are published to
causet.projection-events.v1as a fire-and-forget operation after the DB transaction completes. - Return response — intent result (intent ID + emitted events) is returned to the caller synchronously.
Per-Entity Serial Ordering via Cursor Lock
All intents targeting the same entity_id are serialized through a cursor lock held in the causet database. This guarantees:
- No two intents for the same entity execute concurrently.
- Rule evaluation always sees the latest committed snapshot.
- The event sequence for a given entity is strictly ordered.
Multiple runtime instances can run simultaneously. The cursor lock is database-level (not in-process), so horizontal scaling is safe. Throughput is bounded by per-entity lock contention — entities with very high write rates become a bottleneck.
Determinism Guarantee
The runtime rules engine is deterministic:
same entity state + same intent → same eventsRules may not:
- Read from external services or APIs
- Use wall-clock time (time is injected as a deterministic value)
- Generate random values
- Access environment variables or configuration
This guarantee makes replay safe: re-running an intent against a known snapshot always produces the same ledger events.
Reactive Stack (R2DBC)
All database access in causet-runtime is reactive using R2DBC. There is no JDBC, no blocking I/O on the write path. The reactive stack allows the runtime to handle concurrent intents (across different entities) efficiently without thread-per-request overhead.
gRPC Entity Browser
causet-runtime exposes EntityBrowserGrpcService on port :9090. This service allows:
- Fetching the full ledger event history for a specific
(entity_id, fork_id) - Fetching the current entity snapshot
- Browsing decision routes
This is used by the control plane UI and operational tooling. It is not in the hot path for intent processing.
Active IR Version Resolution
On each intent (or on startup with caching), the runtime calls causet-saas-cloud to get the active irVersion for the fork:
GetActiveRelease(forkId) → irVersionThe IR artifact is then loaded from:
s3://{bucket}/{irVersion}/causet.runtime.jsonThe artifact is cached in Redis by irVersion. On a new deployment (version promotion), the cache entry for the old version naturally expires and the new version is loaded on the next request.
Health Endpoint
GET /actuator/healthReturns liveness and readiness status. Used by Kubernetes/ECS health checks. The readiness probe checks database connectivity and Kafka producer connectivity before marking the instance ready.
Ports Summary
| Port | Protocol | Purpose |
|---|---|---|
| 8080 | HTTP | Intent ingress, health endpoint |
| 9090 | gRPC | Entity browser (EntityBrowserGrpcService) |
Related Pages
- Intents — intent structure and submission
- Rules Engine — preflight, core, and side-effects rules in detail
- The Ledger —
ledger_eventsschema and guarantees - Entity Snapshots — snapshot structure and rebuilding
- Event Flow — end-to-end lifecycle