Skip to content

Limen Architecture Doctrine

Status: Locked.
Version: 0.2
Date: 2026-08-08

This document defines the architecture of Limen. It is written to survive scrutiny from principal engineers, database experts, actuaries, and governance teams. It states what is true, what is still to be proven, and where the hard work is.


1. The stack

Layer Technology Rationale
Compute Quarkus + GraalVM native image Low memory, fast startup, stateless, reactive (Mutiny).
Business rules Kogito DMN engine Deterministic, auditable, versioned, actuary-owned.
Hot/warm state Redis Enterprise (Redis JSON + Redis Search) Sub-millisecond hydrated document access, tiered storage.
Financial ledger PostgreSQL / YugabyteDB / CockroachDB Distributed SQL, strong consistency for append-only ledger, CDC-ready.
Event streaming Kafka / Redpanda / Debezium Change data capture, read-model projection, inter-service events.
Cold storage ClickHouse + S3 / Blob Historical snapshots, actuarial projections, cheap at rest.
Read models Elasticsearch / OpenSearch, ClickHouse, PostgreSQL replicas Specialized views for operational, financial, and actuarial use.
Deployment Kubernetes + GitOps (ArgoCD) Uniform stateless code, cellular data cells.

The stack is locked for Phase 1. Individual stores may be swapped if a better equivalent emerges, but the architectural pattern (stateless compute, hydrated state, append-only ledger, DMN rules, event streaming) does not change.


2. The engines

Everything in Limen is implemented through a small set of engines. No capability owns its own core.

2.1 Graph-E (Party & hierarchy engine)

  • One party model for every person and organization.
  • Roles are edges, not separate databases.
  • Supports broker hierarchies, agent networks, commission overrides, co-insurance groups, and corporate structures.
  • Hot lookups for relationship traversal.
  • Graph-friendly access for deep hierarchy queries.

2.2 Quantum-Tree (Contract & component engine)

  • Contract is the root. Components are nested risk/cover/benefit items.
  • Fully hydrated JSON document stored in Redis JSON.
  • Supports any insurable item through metadata + type + DMN rules.
  • Temporal versions: every material change creates a new immutable version.
  • Effective time and system time are explicit.

2.3 Kinetic (Lifecycle & amortization engine)

  • Manages state transitions and time-based behavior.
  • UPP/EPP, linear or curve-based recognition.
  • Escalations, benefit expiries, renewals, cover-on-demand start/stop.
  • Calculates financial deltas when structure or terms change.
  • Schedules future events at contract creation or modification.

2.4 Fluid-GL (Append-only sub-ledger)

  • Immutable financial event store.
  • Records premiums, splits, commissions, reinsurance shares, claims, reversals, adjustments.
  • No in-place updates. Corrections are new entries.
  • Zero row locking for updates; ordering is handled by idempotency and append sequence.
  • Source of truth for all money movement.

2.5 Decisioning (DMN + Synapse)

  • Kogito DMN is the decision authority. Pricing, splits, eligibility, loadings, commission rules, amortization behavior, and claims triage are DMN rules.
  • Synapse is the AI-assisted layer. It ingests unstructured data (documents, images, telemetry, weather), normalizes it into feature vectors, and proposes decisions. The DMN rule makes the final decision and records the outcome.
  • DMN rule versions are explicit. Every ledger entry references the DMN version that produced it.

2.6 Projections (CQRS read models)

  • Operational: Elasticsearch / OpenSearch for search and 360-degree views.
  • Financial: Normalized ledger views, trial balances, ledger-to-ledger extracts.
  • Actuarial: ClickHouse for cohort cash flows, experience variances, IFRS 17 feeds.
  • All projections are fed by CDC/event streams from the write side. They are eventually consistent, not transactionally consistent.

3. The write path

This is the most important flow in the system. Every command follows it.

[Command / API request]
[Idempotency check] ──► Redis key exists? → Return cached result
[Load hydrated state] ──► Redis JSON contract document
[Run DMN rules] ──► Kogito DMN engine (if decision needed)
[Create new version] ──► Quantum-Tree (if structural change)
[Append ledger entries] ──► Fluid-GL
[Emit events] ──► Kafka / Outbox table
[Project to read models] ──► Asynchronous

3.1 Idempotency

  • Every mutating command carries an idempotency_key.
  • The gateway checks Redis for the key before processing.
  • If the key exists, the cached result is returned. If not, the command is processed and the result is stored with a TTL.
  • Keys are scoped to the cell and the operation type.
  • This guarantees exactly-once processing for commands that are retried by clients or message consumers.

3.2 Cross-store consistency

The write path touches Redis (contract state) and PostgreSQL (ledger). They do not share a transaction boundary. We use an outbox pattern:

  • The command handler writes the new contract version and the ledger entry in a single PostgreSQL transaction within the same service boundary where possible, or it writes to a local outbox table.
  • The outbox table is read by a CDC process and published to Kafka.
  • Consumers read the event and update Redis, projections, and other stores.
  • Sagas handle multi-step processes (e.g., collection → split → commission → reinsurance cession) where a failure requires compensating entries.

This is not two-phase commit. It is eventual consistency with strong ordering and idempotency. The ledger is the source of truth; Redis is a performance-optimized materialized view of the latest state.


4. The contract model

A contract is the root. Everything else hangs from it or is linked to it.

{
  "contract_id": "LMN-2024-0087431",
  "version": "v5",
  "version_info": {
    "effective_date": "2026-04-01T00:00:00Z",
    "system_date": "2026-04-01T00:00:01Z",
    "reason": "ANNUAL_ESCALATION",
    "previous_version": "v4",
    "dmn_version": "pricing-term-life-4.2"
  },
  "status": "INFORCE",
  "product_ref": "TERM-LIFE-PREFERRED",
  "currency": "ZAR",
  "parties": [
    {
      "party_id": "PTY-10023",
      "role": "POLICYHOLDER",
      "effective_date": "2019-04-01",
      "termination_date": null
    },
    {
      "party_id": "PTY-10023",
      "role": "LIFE_ASSURED",
      "effective_date": "2019-04-01"
    },
    {
      "party_id": "BRK-4402",
      "role": "BROKER",
      "effective_date": "2019-04-01"
    }
  ],
  "components": [
    {
      "component_id": "COMP-LIFE",
      "type": "RISK_LIFE",
      "sum_assured": 4000000,
      "base_premium": 2640,
      "dynamic_modifiers": { "gym_score": 1.0, "weather_risk": 1.0 },
      "status": "ACTIVE"
    },
    {
      "component_id": "COMP-CRIT",
      "type": "RIDER_CRITICAL_ILLNESS",
      "sum_assured": 400000,
      "base_premium": 468.5,
      "status": "ACTIVE"
    }
  ],
  "scheduled_events": [
    { "type": "PRE_ALERT_RENEWAL", "due_date": "2027-03-01" },
    { "type": "ESCALATION", "due_date": "2027-04-01" }
  ]
}

4.1 Key design decisions

  • Parties are referenced, not embedded. Party details live in Graph-E. The contract stores only party IDs, roles, and effective dates. This keeps the contract document small and avoids duplicating PII across every version.
  • Components are nested. A commercial policy can have a building component, a vehicle component, and a life component in the same tree.
  • Every version is complete. Reconstructing a historical state is a single document lookup, not a join across version tables.
  • Time is explicit. Every relationship, component, and event has an effective date and a termination date.

5. The ledger model

The ledger is a table of immutable financial events.

Field Purpose
entry_id Unique identifier.
contract_id The contract the entry belongs to.
version_id The contract version active at the effective date.
effective_date When the event took effect.
system_date When the event was recorded.
entry_type Premium receipt, split, commission, claim, reversal, adjustment, etc.
account_type Risk, expense, UPP, EPP, rewards, reinsurance, etc.
amount Decimal.
currency ISO currency code.
period Accounting period (YYYYMM).
reference External reference (debit order ID, claim ID, etc.).
idempotency_key Exactly-once identifier.
dmn_version Rule version that produced the entry.
parent_entry_id For reversals/adjustments, links to the original entry.

5.1 Ledger principles

  • No UPDATE. No DELETE. Every correction is a new entry.
  • Running balance is computed. It is not stored as a mutable column. Read models may pre-compute it, but the ledger itself is the event sequence.
  • Currency is explicit. Multi-currency is supported from the start.
  • Tax metadata is structured. Different jurisdictions require different tax treatments. Tax is part of the entry metadata, not a separate table.

6. Temporal versioning and bi-temporal fork

Every material change creates a new contract version. The old version is preserved.

6.1 Backdated correction

When a correction must be applied from an earlier date, Limen creates a fork:

  1. The original timeline remains untouched.
  2. A new version is created from the historical version at the correction date.
  3. The correction is applied.
  4. Kinetic re-evaluates all scheduled events from that point forward.
  5. The DMN engine re-runs the rules that depend on the changed state.
  6. The financial delta is computed and appended to Fluid-GL as a correcting entry with a modern system date.

This preserves the audit trail, avoids rewriting the past, and keeps the system correct.

6.2 What is a "material change"

Material changes create a new version:

  • Structural: adding/removing components, changing sum assured, changing parties/roles, changing premium terms.
  • Financial: premium adjustments, reversals, claims, commission recalculations.
  • Lifecycle: inforce, lapse, reinstatement, renewal, escalation.

Non-material changes may not create a version:

  • Contact detail updates (address, phone, email) that do not affect premium or risk.
  • Administrative annotations.
  • Document attachments.

The rule: if the change affects money, risk, or lifecycle, it is a new version.


7. Scheduling and cron-less automation

There are no month-end batches. Future events are scheduled at contract creation or modification.

7.1 Event scheduling

When a contract is created or modified, Kinetic calculates all future milestones:

  • Pre-alert communications (e.g., 60 days before renewal).
  • Escalations.
  • Benefit expiries.
  • Cover-on-demand start/stop.
  • Renewal dates.

These are stored in a Redis sorted set or Kafka delayed topic, ordered by due date. Stateless workers process them as they mature.

7.2 Collection scheduling

Premium collections are not generated as a batch file. Each due collection is an event requirement derived from the contract. The collection adapter polls the requirement store or receives real-time bank webhooks. A cleared payment appends a ledger entry. A rejection appends a reversal and triggers the next DMN rule.


8. Multi-tenancy and deployment

8.1 Cellular model

Each major client or jurisdiction runs in an isolated data cell:

  • Local PostgreSQL instance for the ledger.
  • Local Redis instance for hot state.
  • Local Kafka/Redpanda for events.
  • Stateless Quarkus pods deployed uniformly via GitOps.

8.2 Why cells

  • Sovereignty: PII does not cross national borders unless explicitly allowed.
  • Isolation: Sanlam data and AVBOB data never share the same database.
  • Blast radius: A problem in one cell does not affect another.
  • Uniformity: Code and DMN rules are deployed identically across all cells.

8.3 GitOps

The control plane manages stateless code and DMN rules. Data stays in the cell. Updates roll out as canary deployments across cells. Rollbacks are fast because the code is stateless.


9. Latency budgets (SLA by operation class)

We do not claim one latency for everything. We publish budgets.

Operation class Target p99 Conditions
Hot contract read < 2 ms State in Redis hot/warm tier, same region.
Simple DMN calculation < 5 ms In-memory rule set, no external calls.
Premium collection append < 50 ms Idempotency check + ledger append + Redis update.
Contract modification (version) < 100 ms Load + DMN + version + ledger + emit.
Historical snapshot from cold < 500 ms Fetch from ClickHouse/S3.
Actuarial projection query < 5 s Columnar read model, bounded query.
AI document ingestion < 30 s Async pipeline, not a synchronous API.
Cross-cell query < 500 ms Only where explicitly allowed by governance.

These are targets. They must be validated by load tests before any external claim is made.


10. Migration strategy: a spectrum

We do not promise a single migration timeline. We offer a spectrum.

Book type Pattern Timeline
Greenfield / new product Direct launch on Limen Weeks
Simple short-term / credit life Shadow ingest + shadow execution + cutover 60-90 days
Medium complexity Parallel run with reconciliation 3-6 months
Complex long-term life Staged migration, product by product, actuarial sign-off 12-24 months
Legacy with unknown data Discovery + remediation before migration 6+ months before cutover

10.1 Migration pattern

  1. Shadow ingest: Replicate active transactions into Fluid-GL and Quantum-Tree without touching the legacy system.
  2. Shadow execution: Run Limen calculations alongside the legacy system. Compare outputs. Reconcile discrepancies.
  3. Cutover: Route new business through Limen. Legacy records migrate lazily or in batches as activity occurs.

The pattern is safe because it never requires a big-bang weekend. It is not magic because the legacy data must still be understood.


11. AI and DMN: the boundary

AI is powerful and dangerous. Its role is strictly bounded.

11.1 What AI does

  • Ingest unstructured data: documents, images, telematics, weather, satellite imagery.
  • Propose features: risk scores, document classifications, fraud flags.
  • Triage: route claims to auto-settle, manual review, or investigation.

11.2 What AI does not do

  • Make a final decision that affects money or coverage without DMN or human gate.
  • Change a DMN rule.
  • Append a ledger entry.

11.3 The DMN guard

Every AI proposal is passed to a DMN rule. The rule either:

  • Accepts the proposal and records the decision.
  • Rejects the proposal and routes to a human.
  • Requests more information.

The audit trail stores both the AI proposal and the DMN outcome. This is how we combine adaptability with compliance.


12. Failure modes and mitigations

This is where the architecture earns its credibility. See Failure modes for the full catalog. Key mitigations here:

Failure Mitigation
Duplicate command Idempotency keys at the gateway.
Redis loss Rebuild from ledger + event log. Ledger is the source of truth.
Ledger out of order Sequence numbers and monotonic effective dates. Out-of-order entries are detected and held.
DMN rule error Rule versions are immutable. A bad rule is replaced by a new version; old decisions stand.
Cross-cell data request Denied by default. Allowed only through explicit governance API.
Bank rejection after 4 days Event-driven saga. Reversal entry + DMN rule + next action.
Backdated child added Bi-temporal fork. Replay forward. Append delta.
AI hallucination DMN gate + human triage for high-risk decisions.
Read model lag Bounded eventual consistency. Critical operations do not depend on read models.

13. What is still unknown

These are the risks we must validate in Phase 1.

  1. DMN performance at scale. A complex rule set with many tables may exceed 5 ms. We need benchmarks.
  2. Redis JSON document size and query patterns. Real policies with 30-year histories may exceed the 5 KB assumption.
  3. Ledger throughput. 40 million policies with monthly collections = 480 million ledger entries per year. Can PostgreSQL/YugabyteDB handle this with the chosen schema and indexing?
  4. Cold snapshot reconstruction. How long does it take to rebuild a contract from cold storage after a Redis failure?
  5. Cross-cell governance API. What is the actual implementation for allowed cross-cell queries without violating POPIA?
  6. IFRS 17 data shapes. The platform is a data provider, but the exact CSM/VFA/PAA projections need actuarial validation.
  7. Reinsurance treaty modeling. The component participation model needs proof for proportional and non-proportional treaties.

These are not reasons to stop. They are the validation experiments for Phase 1.


14. Decision log

Decision Rationale Date
One core model for all lines Prevents architectural forks and duplicated systems. 2026-08
Append-only ledger is mandatory Financial integrity, audit, no locking. 2026-08
DMN owns business logic Speed of change, actuary ownership, auditability. 2026-08
Cellular multi-tenancy Data residency and sovereignty. 2026-08
Redis hot state + PostgreSQL ledger CQRS separation: fast reads, strong writes. 2026-08
Start with Contract + Ledger core Highest leverage, hardest problems first. 2026-08
Retire AIR-OS name Limen is the platform; AIR-OS is a category claim. 2026-08

See Decision log for the full log.


15. The first vertical slice

Do not build anything else until this slice is proven:

  1. Create a party (Graph-E).
  2. Create a contract with components (Quantum-Tree).
  3. Create a version (temporal versioning).
  4. Record a premium (Fluid-GL).
  5. Reverse the premium (Fluid-GL).
  6. View the timeline and ledger.
  7. Explain the rule trace (DMN version, inputs, outputs).

Only after this slice passes functional, performance, and audit tests do we expand.