- Refresh pnpm-lock.yaml / workspace after prior merge - Add Chain 138 info hub SPA (info-defi-oracle-138) - Token list and validation script tweaks; path_b report; Hyperledger proxmox install notes - HYBX implementation roadmap and routing graph data model Note: transaction-composer is a nested git repo — convert to submodule before tracking. Made-with: Cursor
18 KiB
HYBX Routing Graph Data Model
Purpose: Define the canonical routing graph used by the Compliance & Routing Sidecar (see hybx_compliance_routing_sidecar_technical_plan.md) for graph-based pathfinding, liquidity resolution, fee path construction, and jurisdiction-aware routing. This is the mathematical backbone for nostro pathfinding, multi-bank routing, liquidity selection, and failover as alternate paths on the same graph.
Audience: Engineers implementing the Routing Engine, orchestration adapters, and registry services.
Non-goals: Policy DSL grammar, liquidity registry service APIs, explanation-engine templates, and failover algorithms are specified elsewhere.
1. Purpose and scope
The routing graph models the operational payment network relevant to a transaction: institutions, account corridors, liquidity pools, FX venues, settlement rails, fee agents, and beneficiary anchors, connected by directed, weighted relationships.
- In scope: Vertex types, edge types, cost vectors, liquidity and fee metadata, jurisdiction overlays, serialization, versioning, and mapping from the Transaction Composer compile output.
- Out of scope: Specific shortest-path or MOCO (multi-objective) solver implementations; this document defines inputs those solvers consume.
The Transaction Composer UI produces a design-time pipeline graph (user intent). The sidecar may project that into one or more routing subgraphs and enrich them with edges and weights that are not drawn in the UI (e.g. alternate correspondents, backup rails).
2. Conceptual model
Let G = (V, E) be a directed graph with:
- V: typed vertices (routing nodes).
- E: typed directed edges with metadata and costs.
2.1 Two layers
| Layer | Meaning | Typical source of truth |
|---|---|---|
| Topology | Durable relationships: who may route to whom, which rails exist, static eligibility. | Reference data, contracts, registry |
| Snapshot | Time-bound overlays: available liquidity, current fee quotes, latency estimates, risk adjustments for this evaluation. | Pool registry, market data, sidecar snapshot builder |
At evaluation time, the engine consumes a materialized view: topology ∪ snapshot overlays. The JSON envelope below supports both.
2.2 Relationship to sidecar API
The sidecar plan describes POST /evaluate-transaction with transactionGraph and responses including routingPlan. This document defines the shape of the network graph (and fragments thereof) that backs routingPlan generation—not the full HTTP schema.
3. Vertex (node) types
Vertices are discriminated by vertexKind. All vertices share a common base; extensions are kind-specific.
3.1 Base fields (all kinds)
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | Stable unique id in this graph (UUID or registry key). |
vertexKind |
enum | yes | One of the kinds below. |
displayName |
string | no | Human label. |
jurisdiction |
string | yes | ISO-3166 alpha-2, or internal jurisdiction code (e.g. ID-JK). |
identifiers |
object | no | bic, lei, internalOrgId, etc. |
capabilities |
object | no | See below. |
riskTier |
string | number | no | Opaque tier for risk engine (e.g. T1–T3). |
metadata |
object | no | Opaque extensibility. |
Capabilities object (optional, common pattern):
{
"currenciesAllowed": ["USD", "IDR"],
"fxPermitted": true,
"settlementTypes": ["RTGS", "ACH"],
"maxSingleTransfer": { "amount": "1000000000", "currency": "USD" }
}
3.2 Vertex kind: Institution
Legal or operational entity (bank, CB, PSP). Maps from composer participants and some operational nodes.
3.3 Vertex kind: AccountCorridor
Abstract nostro/vostro or nostro-like relationship endpoint—not necessarily one ledger row. Used for pathfinding between institutions.
- Typical use: attach liquidity and correspondent edges to corridors.
3.4 Vertex kind: LiquidityPool
A source of fungible liquidity in a currency (or synthetic pool id).
- Should align with sidecar liquidity sketch: currency, amount, provider, expiry (carried in snapshot overlay on the pool or on incident edges).
3.5 Vertex kind: FxVenue
FX conversion capability (desk, LP, internal book).
3.6 Vertex kind: SettlementRail
Logical settlement channel (RTGS system, chain leg, internal settlement batch).
3.7 Vertex kind: FeeAgent
Optional dedicated vertex when fees are not folded into Institution (e.g. third-party fee collector).
3.8 Vertex kind: BeneficiaryAnchor
Terminal or near-terminal node representing beneficiary credit location (could be institution + product, or a logical “credit to PT Parak at Bank Kanaya”).
4. Edge types
Edges are directed: (sourceId, targetId). Each edge has:
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | Unique edge id. |
sourceId |
string | yes | Vertex id. |
targetId |
string | yes | Vertex id. |
edgeKind |
enum | yes | See below. |
costVector |
object | yes | Normalized costs (section 5). |
jurisdictions |
string[] | no | Tags for crossing rules; default may inherit from endpoints. |
validFrom / validTo |
string (ISO-8601) | no | Validity window for this edge. |
policyRefs |
string[] | no | Opaque ids for future Policy DSL bindings. |
liquidityRef |
string | no | Pool or line id when edge represents funding. |
feeRef |
string | no | Link to fee schedule id. |
metadata |
object | no | Extensibility. |
4.1 Edge kind: correspondent
Bank-to-bank (or institution-to-institution) routing leg; may attach to AccountCorridor vertices or directly to Institution depending on model granularity.
4.2 Edge kind: liquidity_link
Connects a pool to a corridor, venue, or institution consumption point.
4.3 Edge kind: fx_quote
Connects currency/state A to B through an FxVenue (often modeled as two edges via the venue, or one bundled edge with pair metadata in metadata).
4.4 Edge kind: fee_hop
Explicit fee accrual or pass-through segment (supports building a fee propagation tree as a subgraph).
4.5 Edge kind: settlement_path
Connects to or from a SettlementRail or BeneficiaryAnchor.
5. Weights and multi-criteria cost
The sidecar Routing Engine (see technical plan) uses a weighted directed graph with dimensions including fee cost, latency, and liquidity availability.
5.1 costVector (required on every edge)
Recommended normalized fields (all optional numbers except at least one should be present for pathfinding):
| Key | Meaning | Units / notes |
|---|---|---|
feeCost |
Expected monetary cost of traversing this edge | Normalized to a reference currency in snapshot builder, or raw with feeCurrency in metadata |
latencyMs |
Expected processing time | Milliseconds or representative score |
liquidityAvailability |
How “easy” it is to fund this hop | 0–1 score, or available notional on this hop |
regulatoryPenalty |
Additive penalty from policy | Non-negative; 0 if none |
reliability |
Historical success / health | 0–1, can be inverted by solver |
Example:
"costVector": {
"feeCost": 1250.5,
"latencyMs": 800,
"liquidityAvailability": 0.92,
"regulatoryPenalty": 0,
"reliability": 0.995
}
5.2 Aggregation strategy (normative intent, not algorithm)
Path cost is a multi-criteria problem:
- Hard constraints: e.g. minimum liquidity availability, blocked jurisdictions, expired
validTo. - Scalarization: weighted sum
w1*feeCost + w2*latencyMs + w3*(1-liquidityAvailability) + w4*regulatoryPenalty + w5*(1-reliability)with configurable weights per product lane. - Pareto / k-shortest: optional second phase to present alternates for failover UX.
Exact solver choice is implementation-defined; this document standardizes what is measured on each edge.
6. Liquidity metadata
Aligned with the sidecar liquidity model (currency, amount, provider, expiry):
On LiquidityPool vertex or liquidity_link edge (snapshot):
| Field | Description |
|---|---|
poolId |
Registry identifier |
currency |
ISO-4217 |
availableAmount |
Decimal string recommended |
providerId |
Institution or LP id |
expiresAt |
ISO-8601 |
refreshTtlSeconds |
Optional hint for cache |
7. Fee metadata
Fees may be attached to fee_hop edges or embedded in correspondent / settlement_path via feeRef.
Suggested fields (snapshot or static):
| Field | Description |
|---|---|
feeModel |
percent, flat, tiered, conditional |
bps |
Basis points if percent |
flatAmount |
If flat |
currency |
Fee currency |
conditionRef |
Opaque id for conditional rules (Policy DSL later) |
Fee propagation tree: Derived by taking the subgraph induced by fee_hop edges (and optionally fee-bearing segments), orienting edges in flow direction, and interpreting parent/child as payer → collector → onward. The routing graph remains the single source of truth; the tree is a view, not a second graph.
8. Jurisdiction overlays
- Node default: Each vertex has a primary
jurisdiction. - Edge override:
jurisdictions[]on an edge marks crossing or rule buckets (e.g.US-OFAC,EU-PII). - Transaction-level filter: Evaluation request may supply
transactionJurisdictionsordenyJurisdictionTags; the pathfinder prunes edges violating hard policy (details in Policy DSL doc).
Compliance outcomes (PASS / WARN / FAIL) are produced by the Compliance Engine; this model only supplies tags and penalties (regulatoryPenalty) consumed by routing.
9. Mapping from Transaction Composer
The Transaction Composer (transaction-composer/) compiles UI graphs into CompiledTransaction with buckets: participants, nostroAccounts, liquidity, fx, fees, settlement, plus topology.
Composer is a pipeline; routing graph is a network. The table below is a projection guide, not a 1:1 id equality (routing vertices may be created per registry lookup).
| Composer source | Compiler bucket / NodeKind |
Typical routing vertex kind(s) | Notes |
|---|---|---|---|
| Central / commercial / remittance bank | participants (centralBank, commercialBank, remittanceInstitution) |
Institution |
Map institution string to registry id; participantRole informs source vs beneficiary anchor. |
| Nostro | nostroAccounts |
AccountCorridor, Institution |
Often one corridor per nostro relationship. |
| Liquidity | liquidity (liquidityProvider) |
LiquidityPool, Institution, liquidity_link |
Pool may be resolved via Liquidity Pool Registry. |
| FX | fx (fxConversion) |
FxVenue, fx_quote edges |
May expand to venue + pair legs. |
| Fees | fees (feeRouter) |
FeeAgent or Institution, fee_hop |
Multiple fee nodes become multiple hops or a small fee subgraph. |
| Settlement | settlement |
SettlementRail, BeneficiaryAnchor |
Beneficiary text may map to anchor + rail. |
| Topology edges | topology.edges |
Mixed edgeKind |
Composer edges imply ordering; routing edges add weights and alternates. |
Important: The composer topology.orderedNodeIds defines design order. The routing engine may introduce parallel paths (e.g. two correspondent edges) not present in the UI graph.
10. Serialization and versioning
10.1 JSON envelope
{
"schemaVersion": "1.0.0",
"graphId": "rg-2026-03-29-indonesia-demo",
"effectiveAt": "2026-03-29T12:00:00Z",
"vertices": [],
"edges": [],
"overlays": {
"liquiditySnapshotId": "optional-registry-pointer",
"feeScheduleSnapshotId": "optional",
"notes": "optional"
}
}
10.2 Stability rules
- Patch (1.0.x): Add optional fields only; do not remove or rename required fields.
- Minor (1.x.0): Add new
vertexKind/edgeKindvalues; old consumers ignore unknown kinds if possible. - Major (x.0.0): Breaking renames or semantic changes.
11. Worked example (minimal)
Scenario aligned with composer demo intent: OMNL → BNI nostro/liquidity → USD/IDR FX → fees → settlement at Bank Kanaya; beneficiary PT Parak International. Includes one alternate correspondent edge as a failover hint (not expanded into full failover logic).
{
"schemaVersion": "1.0.0",
"graphId": "example-omnl-bni-kanaya",
"effectiveAt": "2026-03-29T12:00:00Z",
"vertices": [
{
"id": "v-omnl",
"vertexKind": "Institution",
"displayName": "OMNL",
"jurisdiction": "ID",
"identifiers": { "internalOrgId": "OMNL" },
"capabilities": { "currenciesAllowed": ["USD"], "fxPermitted": false }
},
{
"id": "v-bni",
"vertexKind": "Institution",
"displayName": "BNI",
"jurisdiction": "ID",
"identifiers": { "bic": "BNINIDJA" }
},
{
"id": "v-nostro-bni-usd",
"vertexKind": "AccountCorridor",
"displayName": "BNI USD Nostro corridor",
"jurisdiction": "ID",
"identifiers": { "internalOrgId": "BNI", "currency": "USD" }
},
{
"id": "v-pool-bni-usd",
"vertexKind": "LiquidityPool",
"displayName": "BNI USD pool",
"jurisdiction": "ID",
"metadata": {
"currency": "USD",
"availableAmount": "70000000000",
"providerId": "v-bni",
"expiresAt": "2026-03-29T18:00:00Z"
}
},
{
"id": "v-fx-bni",
"vertexKind": "FxVenue",
"displayName": "BNI FX",
"jurisdiction": "ID",
"capabilities": { "currenciesAllowed": ["USD", "IDR"], "fxPermitted": true }
},
{
"id": "v-rail-id",
"vertexKind": "SettlementRail",
"displayName": "ID domestic settlement",
"jurisdiction": "ID",
"capabilities": { "settlementTypes": ["RTGS"] }
},
{
"id": "v-kanaya",
"vertexKind": "Institution",
"displayName": "Bank Kanaya",
"jurisdiction": "ID"
},
{
"id": "v-parak",
"vertexKind": "BeneficiaryAnchor",
"displayName": "PT Parak International",
"jurisdiction": "ID",
"metadata": { "creditInstitutionId": "v-kanaya" }
},
{
"id": "v-alt-correspondent",
"vertexKind": "Institution",
"displayName": "Alternate correspondent (failover candidate)",
"jurisdiction": "ID"
}
],
"edges": [
{
"id": "e-omnl-nostro",
"sourceId": "v-omnl",
"targetId": "v-nostro-bni-usd",
"edgeKind": "correspondent",
"costVector": { "feeCost": 0, "latencyMs": 200, "liquidityAvailability": 1, "regulatoryPenalty": 0, "reliability": 0.999 }
},
{
"id": "e-nostro-pool",
"sourceId": "v-nostro-bni-usd",
"targetId": "v-pool-bni-usd",
"edgeKind": "liquidity_link",
"liquidityRef": "pool-bni-usd-001",
"costVector": { "feeCost": 0, "latencyMs": 50, "liquidityAvailability": 0.95, "regulatoryPenalty": 0, "reliability": 0.998 }
},
{
"id": "e-pool-fx",
"sourceId": "v-pool-bni-usd",
"targetId": "v-fx-bni",
"edgeKind": "liquidity_link",
"costVector": { "feeCost": 0, "latencyMs": 100, "liquidityAvailability": 0.93, "regulatoryPenalty": 0, "reliability": 0.997 }
},
{
"id": "e-fx-rail",
"sourceId": "v-fx-bni",
"targetId": "v-rail-id",
"edgeKind": "fx_quote",
"metadata": { "pair": "USD/IDR", "rateRef": "DEMO-15000" },
"costVector": { "feeCost": 500, "latencyMs": 400, "liquidityAvailability": 0.9, "regulatoryPenalty": 0, "reliability": 0.996 }
},
{
"id": "e-fee-compliance",
"sourceId": "v-rail-id",
"targetId": "v-kanaya",
"edgeKind": "fee_hop",
"feeRef": "fee-reg-omnl-bni",
"costVector": { "feeCost": 200, "latencyMs": 50, "liquidityAvailability": 1, "regulatoryPenalty": 0, "reliability": 0.999 }
},
{
"id": "e-settle-parak",
"sourceId": "v-kanaya",
"targetId": "v-parak",
"edgeKind": "settlement_path",
"costVector": { "feeCost": 0, "latencyMs": 300, "liquidityAvailability": 1, "regulatoryPenalty": 0, "reliability": 0.998 }
},
{
"id": "e-omnl-alt-nostro",
"sourceId": "v-omnl",
"targetId": "v-alt-correspondent",
"edgeKind": "correspondent",
"costVector": { "feeCost": 800, "latencyMs": 350, "liquidityAvailability": 0.7, "regulatoryPenalty": 0, "reliability": 0.99 },
"metadata": { "role": "failover_candidate" }
}
],
"overlays": {
"liquiditySnapshotId": "demo-snapshot-001",
"notes": "Demonstration only; ids and costs are illustrative."
}
}
12. Related work (next documents)
| Next artifact | What this routing graph doc provides |
|---|---|
| Policy DSL specification | policyRefs on edges, regulatoryPenalty, jurisdiction tags, and constraint hooks for hard rejects. |
| Liquidity pool registry model | Canonical poolId, refresh TTL, and binding to LiquidityPool vertices / liquidity_link edges. |
| Decision explanation engine | Path trace over vertices/edges with human-readable labels and cost breakdowns from costVector. |
| Failover routing strategy model | k-shortest paths, edge_disjoint alternates, and scoring on top of this graph without redefining V/E types. |
References
- hybx_compliance_routing_sidecar_technical_plan.md — sidecar architecture, Routing Engine, API sketch.
- transaction-composer/src/types/nodeTypes.ts —
NodeKind,TransactionNodeData. - transaction-composer/src/orchestration/transactionCompiler.ts —
CompiledTransactionbuckets and topology.