Themis · configuration explained

From sources
to risk decisions.

Callers provide context. Events build history. Configuration makes features available. Hydra rules turn those facts into decisions.

6feature types13event aggregation keys41prod decision points2global definitions
01

The whole flow

Source map ↗
CONFIGURATION / AUTHORS
dbt models + product-datasheets

dbt → generated event mappings.
Datasheets → copied parameter CSVs.

DEPLOYED CONFIGURATION
be-risk-configuration

Generated files + feature paths and display labels, DP settings and parameter schemas.

RUNTIME BEHAVIOR
ConfigMaps → Risk services

Argo CD deploys stage, then prod. Mappings govern ingestion and feature resolution.

LIVE DATA / EACH DECISIONValues and context flow into Risk
LIVE

Events → stored history [39 scalar features · 70 record variables ⇒ 188 features]

dbt defines extraction; live events supply values. The translator turns SQL models into ingestion config.

Pub/Sub → Data Service → RabbitMQ → Update Service → SQL Feature / Record.

BigQuery/dbt is not queried on each Decide() call.

Caller → Decide(DP, Variables) [256 input features]

Current inputs + entity keys, such as amount, userId and accountId.

CSV parameter tables [10 referenced tables · 84 features]

Use context to select a limit or other configured value.

Access lists [62 referenced lists · 71 lookup features]

Hydra / APIs / sync jobs maintain membership in the Risk DB.

C# functions [1 function: nameSimilarity]

Compare cardholder or ACH recipient names. Two feature paths across three DPs.

Risk-generated context [IsCanary · TriggeredCanaryRules]

Canary selection supplies these two shared global values.

be-risk

Resolve the facts

  1. Find the decision point by name.
  2. Load features referenced by rules and their dependencies.
  3. Resolve inputs, stored values, lists, parameters and functions.

A feature definition is a lookup recipe. Its value comes from one of these sources.

HYDRA → RISK DB

Rules for this DP

XML conditions, order, state and actions. Evaluated by CodeEffects.

Decision + actions

Result returns to the caller. Configured actions can emit events or update lists / features.

Counts use prod config: distinct feature paths across DPs, with nullable variants counted separately. Record variables support multiple windows and operations; not every ingested variable is necessarily exposed on a DP.

Stage and production use Pub/Sub. Kinesis/Both modes are legacy code paths, not the current architecture. Direct feature-update APIs and feature actions can also write the stores. Runtime evidence ↗

02

Where feature values come from

Feature config ↗
01 / REQUEST

Caller inputs

Values for this decision, sent in DecisionRequest.Variables. The caller supplies the value; be-risk-configuration defines the path as the JSON key and the Hydra label as DisplayName.

input/<DP>/amount/double?
02 / EVENT INGESTION

Event-driven features

One ingestion pipeline, two storage types. The dbt-generated event config selects which fields to extract and how to store them.

Scalar / lifetime · Feature table

Updates stored state: names, flags, dates, counters or statistics. Read first / last / count, or derive time since an event.

userId/KycSuccessfullyVerifiedEvent/*/timestamp/first/days_since/double

Record / velocity · Record table

Stores timestamped observations. At read time, select a window and aggregation such as sum, count or distinct count.

userId/ATMWithdrawal/last1d/amount/sum/double
03 / PARAMETER TABLE

CSV parameters

Input or stored features match CSV rows. The source schema maps match columns and output variables; datasheets owns the values.

parameterized/<Source>/DailyLimit/double
04 / RISK DATABASE

Access / lookup lists

Check an entity against a named list, or load a list. Lists are maintained through Hydra, APIs and jobs, including BIN sync.

merchantId/access-control/<List>/equals/bool?
05 / RISK CODE

Functions

Currently only name similarity is implemented in C#. FunctionFeatures wires its input and feature dependencies.

function/nameSimilarity/cardHolderName/double

The event source supplies two feature types: scalar and record. Global is a definition scope; the current globals are runtime-generated input features.

03

One variable, many features — 70 record variables ⇒ 188 features

Read logic ↗

Request variables

The caller’s dictionary: full feature paths provide values; short keys such as userId identify whose stored data to read.

Stored variables

Event fields extracted into named observations, such as amount or merchantTerminalId. A record is the observation; a feature is a query over it.

Parameter variables

Named CSV outputs such as DailyLimit. LinkedVelocityFeature connects a limit to usage for “used / left” responses.

Explore a real ATM feature

Only configured combinations

How many variations are possible?

Changing the variable selects different stored data. Changing the period or operation derives another feature from the same data; it does not require another ingested event. Define that path in the DP’s feature map or the global map before a rule can use it.

Record operations: sum, count, avg, min, max, stdev, first, last, values; distinct/ supports count, sum, avg, min, max, first and last; tx/count and distinct_tx/count count positive minus negative values. Distinct refers to values, not automatically transaction IDs.

Windows: lastNm minutes, lastNh hours, lastNd days, lastNM months, lastNy years, and allTime. Available history is constrained by retention. * addresses stored lifetime state. A final ? marks nullability in the feature definition; runtime normalizes it when resolving values.

Aggregation implementations ↗ · Window logic ↗

04

Aggregation points: whose history?

Event mappings ↗

An aggregation point is an entity key / grouping dimension, such as userId. The event mapping extracts its value; the caller later supplies the matching key to retrieve that entity’s history.

People
userIdsenderIdreceiverId
Accounts
accountIdbeneficiaryAccountIdbeneficiaryAccountNumberprn
Cards
cardFingerprintId
Phones / cohorts
msisdnreceiverPhoneNumbermsisdnPrefixmsisdnFirstThree
Geography
countryCode
13 configuredAll prefixes in the prod event-ingestion snapshot.
Aggregation point ≠ aggregation function ≠ storage type

Point: userId selects the subject. Function: sum selects the calculation. Storage type: record selects timestamped storage. Other generated types are bool, date, double, score, stats, string, stringcount; the prod snapshot uses all of these except score.

The namespace is configurable, not a fixed 13-value enum. Feature/list lookups also use keys such as deviceId, ipAddress, merchantId, BIN6 and composite keys such as userId_MerchantId. Their existence as a feature prefix does not establish an event-ingestion mapping.

Aliases can differ from source fields: the configured prn key reads DFIAccountNumber; beneficiaryAccountNumber reads Beneficiary.RiskBeneficiaryIdentifier. Match the configured meaning, not just the label.

05

A decision point is a contract

Wiring guide ↗
1. Configure the decision point

risk-decision-points-config.json
Name, state, actions, result types and references.

2. Define its features by path

risk-features-config.json
Path key + DisplayName under the DP. Global definitions attach automatically.

3. Use it in a Hydra rule

Risk DB holds DP / rule records. Config alone does not create rule behavior.

Hydra label / DisplayNameFeature path / JSON keyValue source

Full configured inventory: 41 prod / 43 stage DPs. Select a DP to inspect all its local and global features, actions, result types and references. This shows availability; active rule usage lives in the Risk DB.

Three distinct names: DisplayName is Hydra’s label; the JSON key is the feature path used by rules and input maps; the DP name is the caller’s Decide("…") target. Renaming a Hydra label is simply changing DisplayName, keeping the path unchanged. Existing rules and callers keep working. Stage and prod may differ.
06

Global features & canary

Runtime logic ↗

IsCanary

input/common/isCanary/bool?

True when at least one percentage-canary rule is selected for this decision. Available on all 41 prod / 43 stage DPs.

TriggeredCanaryRules

input/common/triggeredCanaryRules/string?

Comma-separated IDs of the canary rules selected to run. Available on all 41 prod / 43 stage DPs.

Available features = DP-local definitions + global definitions. Globals attach to every DP automatically. Each feature path appears once, compared without regard to case; a DP-local definition takes precedence over a global definition of the same path.

EXAMPLE / 5% CANARY

Selected before conditions run

Risk randomly selects each canary rule per decision at 1%, 5%, 10%, 25% or 50%. These fields describe selection to execute, not whether the rule matched or denied.

No stable user cohort is assigned here. Online canaries can affect the result; Shadow rules do not. OnlineCanaryRule is a separate reference-rule state.

07

The pieces a simple diagram misses

Rules, state and side effects

Hydra stores rule XML, order and Online / Shadow / Offline / Canary state in SQL. DP config also declares actions, allowed results and referenced DPs.

Asynchronous freshness

Event ingestion is asynchronous. A new event may not yet be in SQL when Decide runs; callers provide the current transaction values separately.

Conditions, transforms and transaction trace

Mappings filter events, extract keys/variables, transform values and carry transaction IDs/status. Idempotency and rollback handling affect the final velocity.

Retention, nullability and caching

Record retention bounds available history; missing data behavior depends on the feature/type. Config/rule caches also influence when a change takes effect.

Additional write paths

Feature Update APIs support bulk/override writes. Feature actions can mutate stores; access-list APIs and BIN sync supply lookup data outside event mappings.

Ownership and rollout

Caller inputs → caller repo. Event extraction → dt-dbt. CSV values → product-datasheets. Feature/DP wiring → be-risk-configuration. Functions → be-risk. Rules/lists → Hydra / Risk DB.

When adding something new

Wire the source, add the feature path and DisplayName to the intended DP’s feature map in stage/prod, then author the rule. Use the global map for a feature that belongs on every DP. Deployment creates missing DP metadata before config sync; rules and callers remain separate rollout steps. New lists still need DB setup. Run the config repo’s validator for wiring changes. The generated event file and copied CSVs are not their own sources of truth.

Yes—existing documentation exists

Existing explanations and diagrams

Verified against implementation

What the older diagrams miss: the current Pub/Sub selection, SQL storage, CSV/functions/global context and precise DP exposure. They show older Kinesis / DocumentDB architecture. This page follows the checked-in config and code.

Sources and scope

Reviewed on 20 September 2026. This guide describes configuration keyed by feature path, with automatic global availability. Links are pinned to the inspected implementation and configuration commits. Prod/stage labels refer to repository configuration; live deployments, Hydra rule usage, DB contents and event lag were not audited. The local dt-dbt checkout is older than the config reference, so event examples use the generated config.