Link and Network
Definition: the same cardinality question as Cardinality, but relational: multiple entities converging on one shared attribute. The risk is about the connection, not about one entity’s own behaviour.
Construction: an Abstraction Rule with a configuration identical to Cardinality, with the Search Key inverted to the shared trait (a device, an address, a beneficiary account) instead of the primary entity, and the Function Key set to the primary entity. See the Rule Taxonomy for how this type differs from its neighbours.
| Question an analyst asks | Rule Taxonomy type that answers it |
|---|---|
| How many different customers have used this device? | Link and Network: Distinct Count of CustomerId, Search Key the device |
| How many different accounts pay this beneficiary? | Link and Network: Distinct Count of AccountId, Search Key the beneficiary |
| How many different devices has this customer used? | Not this type: Cardinality, the Search Key is the customer |
| Is this device on a blacklist? | Not this type: Static and Match Screening |
Link and Network: Inverting the Search Key
Cardinality asks “how many distinct values of X has this entity produced”. Link and Network asks “how many distinct entities have produced this value”. The mechanism is the same Distinct Count, and only the two roles are swapped.
| Abstraction Rule field | Cardinality | Link and Network |
|---|---|---|
| Search Key | The entity (CustomerId) |
The shared trait (DeviceId) |
| Function Key | The attribute (DeviceId) |
The entity (CustomerId) |
| Function Type | Distinct Count, or Same Count | Distinct Count, or Same Count |
| Signal | One entity showing many values | Many entities converging on one value |
Because the recall is keyed on the shared trait, it returns the history of every entity that has presented that trait, including entities unrelated to the customer being processed. That is the point, and it is also why the trait must be a Search Key and the fetch limit in the model definition matters: a popular shared trait, such as a public Wi-Fi address, returns a great many records.
Link and Network: Construction Steps
Step 1: Make the Shared Trait a Search Key
In Request XPath (Models » References » Request XPath), make the shared attribute a Search Key. The same field may also be a Search Key for a Cardinality rule where it is the entity, as the two are simply different rules on different keys. Each Search Key is one cache query per invocation.
Step 2: Define the Abstraction Rule
Set Search on, the Search Key to the shared trait, the Search Interval to the window in which convergence is meaningful
(for example 30 Days), the Function Type to Distinct Count, and the Function Key to the entity. Use the rule fragment to
restrict which events are eligible, for example Matched = Payload.TxnType.MatchEqual("Login"), or Matched = True for
all. See
Abstraction Rules.
Step 3: Threshold in an Activation Rule
A device shared by several customers, read as Abstraction.<Name>:
Matched = Abstraction.DistinctCustomersPerDevice30d.MatchGreater(3)
Combined with a payload condition, to raise the signal only when value is moving, using Require and Against per the
Rule Authoring Methodology:
Matched = Abstraction.DistinctAccountsPerBeneficiary7d.RequireGreater(5).Against(Payload.CurrencyAmount).MatchGreater(500)
Exempting known good shared traits, such as an office network, using a List, per Reference Data:
Matched = Abstraction.DistinctCustomersPerDevice30d.RequireGreater(3).Against(Payload.DeviceId).MatchNotInList(List.KnownSharedDevices)
Where the shared trait is known bad in advance, this is not a network signal but a membership check, per Static and Match Screening.
Step 4: Test Link and Network with Several Entities
Post events from several different entities that present one shared value, and assert the response on the event that crosses the threshold. Then post events for one entity across several values, to prove the rule counts entities and not values. Whether the current event is included in the aggregation should be confirmed on the target instance, per Cardinality.
Link and Network: Limits of the Type
This type detects one hop: entities that share a single trait directly. It does not traverse a graph, so it cannot
answer “customers linked to a customer who is linked to fraud”. Chains of two or more traits are built by stacking
rules, one per trait, and combining them in an Activation Rule with And or Or. Anything beyond that is a departure
from the documented taxonomy, and is unsupported.
Link and Network: Common Faults
Symptoms of a misbuilt Link and Network rule, with the likely cause.
| Symptom in a Link and Network rule | Likely cause |
|---|---|
| The Abstraction value is always 0 or 1 | The Search Key is the entity, not the shared trait. The Search Key and Function Key are the wrong way round. |
| Counts are far too high for a common trait | The shared trait is not distinctive (a shared gateway, or a default value). Exempt it with a List. |
| The count is lower than expected | The fetch limit in the model definition truncates the records returned for a popular trait. |
| Slow invocations | Too many Search Keys, or a popular trait returning very large recalls. Narrow the window. |
Examples by Sub-Class
Validated. Every fragment in this section is compiled individually through the Jube Parser, with its payload fields and its TTL Counter, Abstraction, Calculation, List and Sanction tokens declared, so each extension method and argument type is checked against the engine. Compilation does not validate a rule’s logic or its thresholds, which are illustrative placeholders to be tuned on data.
3.1 Shared device and technical identifiers
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 3.1-01 | Many customers on one device | Several unrelated customers transact from the same device (mule farm, synthetic identity ring). | Abstraction Rule DistinctCustomersPerDevice30d: Search Key DeviceId (Request XPath), Search Interval 30 Days, Function Type Distinct Count over CustomerId (placeholder field). The threshold is the bullet’s own value of 3. |
Matched = Abstraction.DistinctCustomersPerDevice30d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Device→Customer · 30d, distinct customers ≥ 3 |
| 3.1-02 | Many accounts on one device | One device enrolled to many accounts. | Abstraction Rule DistinctAccountsPerDevice30d: Search Key DeviceId, Search Interval 30 Days, Distinct Count over AccountId. The threshold is illustrative and is set at build time. |
Matched = Abstraction.DistinctAccountsPerDevice30d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Device→Account · 30d |
| 3.1-03 | Many new accounts opened from one device | Application farming. | Abstraction Rule DistinctApplicationsPerDevice7d: Search Key DeviceId, Search Interval 7 Days, Distinct Count over ApplicationId (placeholder field), with the filter Matched = Payload.TxnType.MatchEqual("Application") (placeholder value) so only application events are counted. |
Matched = Abstraction.DistinctApplicationsPerDevice7d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Device→Application · 7d |
| 3.1-04 | Many customers on one IP address | Residential IP shared by unrelated customers beyond household norm. | Abstraction Rule DistinctCustomersPerIp7d: Search Key IP holding the value normalised upstream by NormaliseIpAddress, Search Interval 7 Days, Distinct Count over CustomerId. The household-norm threshold is set from data, not from this fragment. |
Matched = Abstraction.DistinctCustomersPerIp7d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · IP→Customer · 7d |
| 3.1-05 | Many customers on one browser fingerprint | Canvas/WebGL/audio fingerprint shared across identities. | Abstraction Rule DistinctCustomersPerFingerprint30d: Search Key FingerprintHash (placeholder field, a hash supplied by the device SDK), Search Interval 30 Days, Distinct Count over CustomerId. |
Matched = Abstraction.DistinctCustomersPerFingerprint30d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Fingerprint→Customer · 30d |
| 3.1-06 | Many customers on one SIM/phone number | Phone number shared across identities. | Abstraction Rule DistinctCustomersPerPhone90d: Search Key PhoneDigits (placeholder field holding PhoneNumberDigits applied upstream), Search Interval 90 Days, Distinct Count over CustomerId. Its filter Matched = Payload.Phone.IsValidE164PhoneNumber() keeps invalid numbers out of the count. |
Matched = Abstraction.DistinctCustomersPerPhone90d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Phone→Customer · 90d |
| 3.1-07 | Many customers sharing one email domain pattern | Disposable or sequentially generated emails across customers. | Abstraction Rule DistinctCustomersPerEmailPattern30d: Search Key EmailPattern (placeholder field, the local part with RegexReplace of trailing digits applied upstream), Search Interval 30 Days, Distinct Count over CustomerId. Its filter Matched = Payload.Email.EmailDomain().MatchInList(List.DisposableDomains) restricts the count to disposable domains, and the list is vendor or internal reference data. |
Matched = Abstraction.DistinctCustomersPerEmailPattern30d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Email pattern→Customer · 30d |
| 3.1-08 | Many customers sharing a device-id after factory reset | Emulator/reset device farms. | Abstraction Rule DistinctCustomersPerHardware7d: Search Key DeviceHardwareHash (placeholder field, a hardware-derived hash supplied upstream), Search Interval 7 Days, Distinct Count over CustomerId. The rule detects shared hardware; a factory reset is not observed. |
Matched = Abstraction.DistinctCustomersPerHardware7d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Device model/hardware→Customer · 7d |
| 3.1-09 | Many customers on one emulator signature | Emulator/rooted environment across customers. | Abstraction Rule DistinctCustomersPerEmulator7d: Search Key EmulatorSignature (placeholder field, supplied by the device-intelligence vendor), Search Interval 7 Days, Distinct Count over CustomerId. |
Matched = Abstraction.DistinctCustomersPerEmulator7d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Emulator signature→Customer · 7d |
| 3.1-10 | Device shared between a victim and a known fraudster | Device linked to confirmed fraud account. | List FraudLinkedDevices, fed from the case-management outcome, holds device ids linked to confirmed fraud accounts. Membership has no window; the Activation Rule tests DeviceId (placeholder field) against it. |
Matched = Payload.DeviceId.MatchInList(List.FraudLinkedDevices) |
Tag: Link · Fraud · Device→Account (flagged) · lifetime |
| 3.1-11 | VPN/proxy exit node shared across customers | Many accounts via one anonymising exit. | List AnonymiserExitIps holds vendor exit-node addresses, and Abstraction Rule DistinctCustomersPerExitNode24h uses Search Key IP (normalised upstream), Search Interval 24 Hours and Distinct Count over CustomerId. The Activation Rule requires both list membership and the count; the vendor, not Jube, classifies a VPN or proxy, and IsInAnyCidr can test published ranges instead of a list. |
Matched = Payload.IP.MatchInList(List.AnonymiserExitIps) And Abstraction.DistinctCustomersPerExitNode24h.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · IP→Customer · 24h |
| 3.1-12 | Many customers on one MAC/hardware attribute | Hardware attribute reuse. | Abstraction Rule DistinctCustomersPerMacHash30d: Search Key MacHash (placeholder field, Sha256Hex of the MAC address applied upstream), Search Interval 30 Days, Distinct Count over CustomerId. Operating systems randomise MAC addresses, so this is a weak signal. |
Matched = Abstraction.DistinctCustomersPerMacHash30d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Hardware→Customer · 30d |
| 3.1-13 | Many customers on one remote-access tool session | Screen-share sessions linked to many victims. | Abstraction Rule DistinctCustomersPerRemoteSession7d: Search Key RemoteAccessSessionId (placeholder field, supplied by the vendor), Search Interval 7 Days, Distinct Count over CustomerId. |
Matched = Abstraction.DistinctCustomersPerRemoteSession7d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · RAT session→Customer · 7d |
3.2 Shared personal and contact attributes
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 3.2-01 | Many customers at one residential address | More customers than plausible for an address (synthetic/mule). | Abstraction Rule DistinctCustomersPerResidentialAddress90d: Search Key AddressKey (placeholder field, the address after an address-specific normaliser applied upstream, because NormalisedForMatching is built for names), Search Interval 90 Days, Distinct Count over CustomerId. The occupancy threshold is set from data. |
Matched = Abstraction.DistinctCustomersPerResidentialAddress90d.MatchGreaterOrEqual(3) |
Tag: Link · Both · Address→Customer · 90d |
| 3.2-02 | Many customers at one non-residential address | Mailbox/registered-agent address shared by many entities. | Abstraction Rule DistinctEntitiesPerAddress90d: Search Key AddressKey (placeholder field, normalised upstream), Search Interval 90 Days, Distinct Count over EntityId (placeholder field). A List RegisteredAgentAddresses can be added as a filter to sharpen the signal. |
Matched = Abstraction.DistinctEntitiesPerAddress90d.MatchGreaterOrEqual(3) |
Tag: Link · AML · Address→Entity · 90d |
| 3.2-03 | Many businesses at one address | Shell company cluster. | Abstraction Rule DistinctBusinessesPerAddressLifetime: Search Key AddressKey (normalised upstream), Search Interval set to the longest Days value the Search Key’s TTL keeps, which stands in for lifetime, Distinct Count over BusinessId (placeholder field). Abstraction intervals are counted in Days, so a true unbounded window is not written here. |
Matched = Abstraction.DistinctBusinessesPerAddressLifetime.MatchGreaterOrEqual(3) |
Tag: Link · AML · Address→Business · lifetime |
| 3.2-04 | Many customers with the same phone number | Contact number reused across identities. | Same Abstraction Rule as the SIM row above, DistinctCustomersPerPhone90d, so one retrieval serves both bullets. The key is the phone identity, not the device. |
Matched = Abstraction.DistinctCustomersPerPhone90d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Phone→Customer · 90d |
| 3.2-05 | Many customers with the same email | Email reused across identities. | Abstraction Rule DistinctCustomersPerNormalisedEmail90d: Search Key NormalisedEmail (placeholder field, NormalizeEmailAlias applied upstream so provider-specific variants collapse), Search Interval 90 Days, Distinct Count over CustomerId. |
Matched = Abstraction.DistinctCustomersPerNormalisedEmail90d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Email→Customer · 90d |
| 3.2-06 | Many customers with the same date of birth and similar names | Possible duplicate or synthetic identity cluster. | Abstraction Rule DistinctCustomersPerDobSurname: Search Key DobSurnameKey (placeholder field, built upstream from the date of birth, a separator and Soundex of the surname), Search Interval set to the longest Days value the Search Key’s TTL keeps, Distinct Count over CustomerId. The separator stops the joined key colliding, and the phonetic key misses variants that change the first letter. |
Matched = Abstraction.DistinctCustomersPerDobSurname.MatchGreaterOrEqual(2) |
Tag: Link · Fraud · DOB+name→Customer · lifetime |
| 3.2-07 | Many customers sharing a national identifier fragment | Partial identifier reuse. | Abstraction Rule DistinctCustomersPerNationalIdFragment: Search Key NationalIdFragmentHash (placeholder field, Sha256Hex of the last four characters applied upstream so the raw fragment is never stored), Search Interval set to the longest Days value the Search Key’s TTL keeps, Distinct Count over CustomerId. Four characters collide heavily. |
Matched = Abstraction.DistinctCustomersPerNationalIdFragment.MatchGreaterOrEqual(3) |
Check: National identifier collection, storage and matching are restricted by privacy law in many jurisdictions; confirm the lawful basis and whether a fragment may be keyed at all. Tag: Link · Fraud · ID fragment→Customer · lifetime |
| 3.2-08 | Many customers with same employer details | Fictitious employer reuse. | Abstraction Rule DistinctCustomersPerEmployer90d: Search Key EmployerKey (placeholder field, NormalisedForMatching applied upstream), Search Interval 90 Days, Distinct Count over CustomerId. Large legitimate employers share names, so this is weak alone. |
Matched = Abstraction.DistinctCustomersPerEmployer90d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Employer→Customer · 90d |
| 3.2-09 | Many customers with same IBAN beneficiary | Common recipient across many customers. | Abstraction Rule DistinctCustomersPerBeneficiaryIban30d: Search Key BeneficiaryIbanPrint (placeholder field, IbanPrintFormat applied upstream), Search Interval 30 Days, Distinct Count over CustomerId. Its filter Matched = Payload.BeneficiaryIban.IsValidIban() keeps invalid IBANs from grouping under one key, and IbanBasicBankAccountNumber is not used because it drops the country code and check digits. |
Matched = Abstraction.DistinctCustomersPerBeneficiaryIban30d.MatchGreaterOrEqual(3) |
Tag: Link · Both · Beneficiary IBAN→Customer · 30d |
| 3.2-10 | Shared director/UBO across many companies | Individual appearing as controller of many entities. | Abstraction Rule DistinctCompaniesPerDirector: Search Key DirectorPersonId (placeholder field, the stable person id matched upstream in KYC), Search Interval set to the longest Days value the Search Key’s TTL keeps, Distinct Count over CompanyId (placeholder field). |
Matched = Abstraction.DistinctCompaniesPerDirector.MatchGreaterOrEqual(2) |
Tag: Link · AML · Person→Entity · lifetime |
| 3.2-11 | Shared authorised signatory across unrelated accounts | One signatory across many accounts. | Abstraction Rule DistinctAccountsPerSignatory: Search Key SignatoryId (placeholder field), Search Interval set to the longest Days value the Search Key’s TTL keeps, Distinct Count over AccountId. The unrelated-accounts qualifier is not testable in a fragment, so this counts breadth only. |
Matched = Abstraction.DistinctAccountsPerSignatory.MatchGreaterOrEqual(3) |
Tag: Link · AML · Signatory→Account · lifetime |
| 3.2-12 | Shared power of attorney across accounts | One attorney manages many unrelated accounts. | Abstraction Rule DistinctAccountsPerAttorney: Search Key AttorneyId (placeholder field), Search Interval set to the longest Days value the Search Key’s TTL keeps, Distinct Count over AccountId. Breadth only, as for the signatory row. |
Matched = Abstraction.DistinctAccountsPerAttorney.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Attorney→Account · lifetime |
| 3.2-13 | Shared guarantor across applications | One guarantor for many loan applications. | Abstraction Rule DistinctApplicationsPerGuarantor90d: Search Key GuarantorId (placeholder field), Search Interval 90 Days, Distinct Count over ApplicationId (placeholder field). |
Matched = Abstraction.DistinctApplicationsPerGuarantor90d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Guarantor→Application · 90d |
| 3.2-14 | Shared reference contacts across applications | Same referee/emergency contact across applications. | Abstraction Rule DistinctApplicationsPerReferencePhone90d: Search Key ReferencePhoneDigits (placeholder field, PhoneNumberDigits applied upstream to ReferenceContactPhone), Search Interval 90 Days, Distinct Count over ApplicationId. The same pattern with NormalizeEmailAlias keys a reference email. |
Matched = Abstraction.DistinctApplicationsPerReferencePhone90d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Contact→Application · 90d |
| 3.2-15 | Shared bank account details on payroll | Multiple employees paid to one account (ghost employees). | Abstraction Rule DistinctEmployeesPerPayrollAccount30d: Search Key PayrollAccountPrint (placeholder field, IbanPrintFormat applied upstream), Search Interval 30 Days, Distinct Count over EmployeeId (placeholder field). Payroll events must carry the employee id and the payee account. |
Matched = Abstraction.DistinctEmployeesPerPayrollAccount30d.MatchGreaterOrEqual(2) |
Tag: Link · Fraud · Account→Employee · 30d |
| 3.2-16 | Shared vendor bank account across suppliers | Duplicate vendors paid to one account (fictitious vendor). | Abstraction Rule DistinctVendorsPerBankAccount90d: Search Key VendorAccountPrint (placeholder field, IbanPrintFormat applied upstream), Search Interval 90 Days, Distinct Count over VendorId (placeholder field). |
Matched = Abstraction.DistinctVendorsPerBankAccount90d.MatchGreaterOrEqual(2) |
Tag: Link · Fraud · Account→Vendor · 90d |
| 3.2-17 | Vendor bank account matches employee account | Payment to vendor whose bank details match an employee. | List EmployeeAccounts, refreshed from payroll with lifetime membership. The vendor account is normalised with IbanPrintFormat and tested against the list. |
Matched = Payload.VendorAccount.IbanPrintFormat().MatchNormalisedInList(List.EmployeeAccounts) |
Tag: Link · Fraud · Account→Vendor/Employee · lifetime |
| 3.2-18 | Vendor address matches employee address | Related-party vendor. | List EmployeeAddresses, fed from HR with lifetime membership. The vendor address is tested with the normalised-list step; that folding is less forgiving of unit and postcode variants, so expect split keys. |
Matched = Payload.VendorAddress.MatchNormalisedInList(List.EmployeeAddresses) |
Tag: Link · Fraud · Address→Vendor/Employee · lifetime |
3.3 Money-flow and counterparty networks
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 3.3-01 | Many senders to one beneficiary (fan-in) | A hub account receives from many accounts. | Abstraction Rule DistinctSendersPerBeneficiary7d: Search Key BeneficiaryId (placeholder field), Search Interval 7 Days, Distinct Count over SenderId (placeholder field). The bullet gives no threshold; the value shown is illustrative. |
Matched = Abstraction.DistinctSendersPerBeneficiary7d.MatchGreaterOrEqual(3) |
Tag: Link · AML · Beneficiary→Sender · 7d |
| 3.3-02 | One sender to many beneficiaries (fan-out) | A hub disperses to many accounts. | Abstraction Rule DistinctBeneficiariesPerSender7d: Search Key SenderId (placeholder field), Search Interval 7 Days, Distinct Count over BeneficiaryId (placeholder field). The mirror of fan-in, keyed on the sender. |
Matched = Abstraction.DistinctBeneficiariesPerSender7d.MatchGreaterOrEqual(3) |
Tag: Link · AML · Sender→Beneficiary · 7d |
| 3.3-03 | Fan-in then fan-out through one account | Pass-through hub combining both. | Two Abstraction Rules with 72 Hour windows: DistinctSendersPerBeneficiary72h (Search Key BeneficiaryId, Distinct Count over SenderId) and DistinctBeneficiariesPerSender72h (Search Key SenderId, Distinct Count over BeneficiaryId). Both must hold for the same account, so confirm on the target instance that a counter can be read by a key taken from the other payload role. |
Matched = Abstraction.DistinctSendersPerBeneficiary72h.MatchGreaterOrEqual(3) And Abstraction.DistinctBeneficiariesPerSender72h.MatchGreaterOrEqual(3) |
Tag: Link · AML · Account · 72h |
| 3.3-05 | Bidirectional flows between two accounts | Equal and opposite payments between the same pair. | The net-to-gross share is an Abstraction Calculation, PairNetShare30d, defined as Abstraction.PairNet30d.RatioOf(Abstraction.PairGross30d) over Sum Abstraction Rules keyed on the canonical pair key. CanonicalPairKey writes that key upstream, and DirectionSign writes the signed amount. Below a gross floor the ratio is undefined and returns NaN, which fails the comparison, so the floor is applied inside the calculation. The floor is a placeholder to tune. | Matched = AbstractionCalculation.PairNetShare30d.MatchLessOrEqual(0.05) |
Tag: Link · AML · Account pair · 30d |
| 3.3-07 | Flows from many accounts to a flagged account | Customers paying a confirmed-fraud/mule account. | List FlaggedAccounts, maintained from confirmed fraud and mule outcomes, and Abstraction Rule DistinctSendersPerBeneficiary30d with Search Key BeneficiaryAccount, 30 Days, Distinct Count over SenderId. The Activation Rule tests both the list and the count. |
Matched = Payload.BeneficiaryAccount.MatchInList(List.FlaggedAccounts) And Abstraction.DistinctSendersPerBeneficiary30d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Flagged beneficiary→Sender · 30d |
| 3.3-08 | Multiple victims paying one beneficiary | Scam beneficiary collecting from many victims. | Incrementing Activation Rule Matched = Payload.SenderAccount.MatchInList(List.ConfirmedVictims) ticks TTL Counter VictimInbound7d with Sum off, so each matching payment adds one, keyed on BeneficiaryAccount over 7 Days. The victim list comes from report or case data, and the counter is a count of flags, not a distinct count. The incrementing rule has the earlier Priority. |
Matched = TTLCounter.VictimInbound7d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Beneficiary→Sender · 7d |
| 3.3-09 | Shared counterparty between high-risk customers | Two high-risk customers with the same counterparty. | Abstraction Rule DistinctHighRiskCustomersPerCounterparty90d: Search Key Counterparty (placeholder field), Search Interval 90 Days, Distinct Count over CustomerId, with the filter Matched = Payload.CustomerRiskRating.MatchEqual("High"). The rating field and its definition are firm specific. |
Matched = Abstraction.DistinctHighRiskCustomersPerCounterparty90d.MatchGreaterOrEqual(2) |
Tag: Link · AML · Counterparty→Customer · 90d |
| 3.3-10 | Shared intermediary bank across layered payments | Same correspondent bank used in layering chains. | Abstraction Rule DistinctOriginatorsPerIntermediaryBank30d: Search Key IntermediaryBankCode (placeholder field, BicInstitutionCode applied upstream to IntermediaryBic, which groups by bank rather than branch), Search Interval 30 Days, Distinct Count over OriginatorId. Its filter Matched = Payload.IntermediaryBic.IsValidBic() gates invalid codes. This counts reuse only; the layering context is a separate row. |
Matched = Abstraction.DistinctOriginatorsPerIntermediaryBank30d.MatchGreaterOrEqual(3) |
Tag: Link · AML · Bank→Payment · 30d |
| 3.3-13 | Shared remitter across unrelated beneficiaries (remittance) | One sender used by many “customers” at agent locations. | Abstraction Rule DistinctBeneficiariesPerRemitter30d: Search Key RemitterId (placeholder field), Search Interval 30 Days, Distinct Count over BeneficiaryId. A second rule, DistinctAgentsPerRemitter30d (Distinct Count over AgentId), tests the agent-location clause. |
Matched = Abstraction.DistinctBeneficiariesPerRemitter30d.MatchGreaterOrEqual(3) |
Tag: Link · AML · Sender→Beneficiary · 30d |
| 3.3-14 | Shared beneficiary across agents | One recipient paid through many agents. | Abstraction Rule DistinctAgentsPerBeneficiary30d: Search Key BeneficiaryId (placeholder field), Search Interval 30 Days, Distinct Count over AgentId (placeholder field). |
Matched = Abstraction.DistinctAgentsPerBeneficiary30d.MatchGreaterOrEqual(2) |
Tag: Link · AML · Beneficiary→Agent · 30d |
| 3.3-16 | Account receiving from many first-time senders | High share of new counterparties inbound. | TTL Counter PairHistory365d keyed on PairKey (placeholder field, sender and beneficiary joined with a separator upstream), 365 Days, Sum off. TTL Counters InboundCount7d and FirstInbound7d are keyed on BeneficiaryId (placeholder field), 7 Days. The incrementing rule Matched = TTLCounter.PairHistory365d.MatchIsZero() ticks FirstInbound7d and must have an earlier Priority than the rule that ticks PairHistory365d, so the first-time flag is read before the pair counter increments. The 365-day look-back is an assumption to set, and the share is of payments, not distinct senders. |
Matched = TTLCounter.FirstInbound7d.RatioOf(TTLCounter.InboundCount7d).MatchGreaterOrEqual(0.5) |
Tag: Link · AML · Account · 7d |
| 3.3-17 | Counterparty connected to sanctioned party (one hop) | Counterparty shares an attribute with a sanctioned entity. | Sanction definition on the multipart field CounterpartyName (placeholder field), read as Sanction.CounterpartyName as a distance, combined with List SanctionedIdentifiers of counterparty identifiers from the regulatory lists. Read as one hop, this is a direct screen; the multi-hop reading is graph work. |
Matched = Sanction.CounterpartyName.MatchLessOrEqual(1) Or Payload.CounterpartyIban.MatchInList(List.SanctionedIdentifiers) |
Check: Sanctions list sources, coverage and screening obligations should be confirmed by compliance for each jurisdiction served. Tag: Link · AML · Counterparty · lifetime |
| 3.3-18 | Counterparty connected to PEP (one hop) | Same for politically exposed persons. | List PepIdentifiers, loaded from the PEP source, holds identifiers for politically exposed persons, and a Sanction definition CounterpartyPepName is configured against the PEP list for name matching. |
Matched = Payload.CounterpartyIdentifier.MatchInList(List.PepIdentifiers) Or Sanction.CounterpartyPepName.MatchLessOrEqual(1) |
Check: PEP definitions differ by jurisdiction (domestic versus foreign, family members and close associates, lookback periods); confirm the definition and the list source with compliance. Tag: Link · AML · Counterparty · lifetime |
3.4 Organisational and insider networks
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 3.4-01 | Staff member linked to customer via address or phone | Employee shares attributes with a customer they service. | List StaffPhones, refreshed from HR, and List StaffAddresses, both with lifetime membership. The customer phone is compared as digits and the customer address through the normalised-list step, which is weak on addresses. |
Matched = Payload.CustomerPhone.PhoneNumberDigits().MatchInList(List.StaffPhones) Or Payload.CustomerAddress.MatchNormalisedInList(List.StaffAddresses) |
Tag: Link · Fraud · Employee→Customer · lifetime |
| 3.4-02 | Staff member accessing accounts of connected parties | Insider viewing relatives’/associates’ accounts. | List ConnectedStaffCustomer holds staff and customer pairs built upstream from relationship or disclosure data, joined with a separator. Abstraction Rule DistinctAccountsPerStaff30d uses Search Key StaffId, 30 Days, Distinct Count over AccountId. |
Matched = Payload.StaffId.ConcatWith("/", Payload.CustomerId).MatchInList(List.ConnectedStaffCustomer) And Abstraction.DistinctAccountsPerStaff30d.MatchGreaterOrEqual(2) |
Tag: Link · Fraud · Employee→Account · 30d |
| 3.4-03 | Agent onboarding many customers sharing a device | Agent-device-customer triangle. | Abstraction Rule DistinctCustomersPerAgentDevice7d: Search Key AgentDeviceKey (placeholder field, AgentId and DeviceId joined with a separator upstream), Search Interval 7 Days, Distinct Count over CustomerId. |
Matched = Abstraction.DistinctCustomersPerAgentDevice7d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Agent→Customer · 7d |
| 3.4-04 | Branch with many dormant account reactivations | Branch-level cluster. | Incrementing Activation Rule Matched = Payload.LastActivityDate.AgeInDays().MatchGreaterOrEqual(365) ticks TTL Counter DormantReactivations30d keyed on BranchId (placeholder field), 30 Days, Sum off. The 365-day dormancy is a firm policy placeholder, not a regulatory figure. |
Matched = TTLCounter.DormantReactivations30d.MatchGreaterOrEqual(3) |
Tag: Link · AML · Branch→Account · 30d |
| 3.4-05 | Merchant linked to flagged merchant via owner details | Merchant-owner link to terminated merchant. | List TerminatedMerchantOwners, lifetime membership, holds owner identifiers hashed with Sha256Hex before they are listed. The Activation Rule hashes the incoming owner identifier the same way and tests it against the list. |
Matched = Payload.OwnerIdentifier.Sha256Hex().MatchInList(List.TerminatedMerchantOwners) |
Tag: Link · Fraud · Owner→Merchant · lifetime |
| 3.4-06 | Merchant with many cards linked to one mule network | Merchant acquiring from compromised cards. | Incrementing Activation Rule Matched = Payload.CardId.MatchInList(List.MuleNetworkCards) ticks TTL Counter MuleCardHits7d keyed on MerchantId (placeholder field), 7 Days, Sum off. CardId carries a tokenised value, never a PAN, and the counter counts card hits, not distinct cards. |
Matched = TTLCounter.MuleCardHits7d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Merchant→Card · 7d |
| 3.4-07 | Collusive merchant-customer loops | Merchant refunds/charges linked to a small customer group. | Abstraction Rule DistinctCustomersPerMerchant30d: Search Key MerchantId (placeholder field), Search Interval 30 Days, Distinct Count over CustomerId. TTL Counters RefundsByMerchant30d and ChargesByMerchant30d are keyed on MerchantId over 30 Days with Sum on CurrencyAmount, incremented by rules filtered on Payload.TxnType (placeholder values). True loop detection is a graph cycle and is not in this fragment. |
Matched = Abstraction.DistinctCustomersPerMerchant30d.MatchGreaterOrEqual(3) And TTLCounter.RefundsByMerchant30d.RatioOf(TTLCounter.ChargesByMerchant30d).MatchGreaterOrEqual(0.5) |
Tag: Link · Fraud · Merchant→Customer · 30d |
| 3.4-08 | Broker/introducer with many linked applications | Introducer cluster for fraudulent loans. | Abstraction Rule DistinctApplicationsPerIntroducer30d: Search Key IntroducerId (placeholder field), Search Interval 30 Days, Distinct Count over ApplicationId (placeholder field). |
Matched = Abstraction.DistinctApplicationsPerIntroducer30d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Introducer→Application · 30d |
3.5 Sector-specific link and network rules
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 3.5-01 | Claimant linked to repair shop/medical provider by address or phone (insurance) | Provider-claimant collusion. | Two Abstraction Rules share one Search Key ProviderAddressKey (placeholder field, NormalisedForMatching applied upstream): DistinctClaimantsPerAddress (Distinct Count over ClaimantId) and DistinctProvidersPerAddress (Distinct Count over ProviderId), each with Search Interval set to the longest Days value the key’s TTL keeps. Both roles must hold on the same key, and address normalisation is weak. |
Matched = Abstraction.DistinctClaimantsPerAddress.MatchGreaterOrEqual(1) And Abstraction.DistinctProvidersPerAddress.MatchGreaterOrEqual(1) |
Tag: Link · Fraud · Provider→Claimant · lifetime |
| 3.5-02 | Same witnesses across unrelated claims | Witness reuse. | Abstraction Rule DistinctClaimsPerWitness24m: Search Key WitnessId (placeholder field), Search Interval 730 Days, which is 24 months in Days because Abstraction intervals are counted in Days, Distinct Count over ClaimId (placeholder field). Confirm the engine supports a window this long on the target instance. |
Matched = Abstraction.DistinctClaimsPerWitness24m.MatchGreaterOrEqual(2) |
Tag: Link · Fraud · Witness→Claim · 24m |
| 3.5-03 | Same vehicle in multiple accident claims with different owners | Staged accident rings. | Abstraction Rule DistinctOwnersPerVin24m: Search Key Vin (placeholder field, Upper applied upstream), Search Interval 730 Days, Distinct Count over OwnerId (placeholder field). VIN check digits are validated upstream, not in Jube. |
Matched = Abstraction.DistinctOwnersPerVin24m.MatchGreaterOrEqual(2) |
Tag: Link · Fraud · Vehicle→Claim · 24m |
| 3.5-05 | Broker linked to many claims with early inception | Broker-driven fraud ring. | Incrementing Activation Rule Matched = Payload.InceptionDate.DaysBetween(Payload.ClaimDate).RequireGreaterOrEqual(0).MatchLessOrEqual(90) ticks TTL Counter EarlyClaims12m keyed on BrokerId (placeholder field), 12 Months, Sum off. DaysBetween runs from inception to claim, so a claim before inception does not count, and the 90-day cut-off is a business rule. |
Matched = TTLCounter.EarlyClaims12m.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Broker→Claim · 12m |
| 3.5-06 | Players linked by device sharing chips (gaming) | Chip dumping ring. | Abstraction Rule DistinctPlayersPerDevice30d: Search Key DeviceId, Search Interval 30 Days, Distinct Count over PlayerId (placeholder field). The opposite-outcome test is the next row, so this fires on device sharing alone. |
Matched = Abstraction.DistinctPlayersPerDevice30d.MatchGreaterOrEqual(2) |
Tag: Link · AML · Device→Player · 30d |
| 3.5-07 | Players with shared payment card and opposite outcomes | Collusion. | The net-to-gross share is an Abstraction Calculation, CardNetShare30d, defined as Abstraction.CardNet30d.RatioOf(Abstraction.CardGross30d) keyed on the card, with the same undefined-below-floor behaviour as 3.3-05. The distinct-player count is a second activation rule at a later Priority, not part of this fragment. | Matched = AbstractionCalculation.CardNetShare30d.MatchLessOrEqual(0.05) |
Tag: Link · Both · Card→Player · 30d |
| 3.5-08 | Affiliate linked to many bonus-abuse accounts | Affiliate fraud. | Incrementing Activation Rule Matched = Payload.BonusAbuseFlag.MatchIsTrue() ticks TTL Counter AffiliateBonusAbuse30d keyed on AffiliateId (placeholder field), 30 Days, Sum off. The bonus-abuse flag is supplied upstream. |
Matched = TTLCounter.AffiliateBonusAbuse30d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Affiliate→Player · 30d |
| 3.5-09 | Exchange accounts sharing withdrawal address (crypto) | Common wallet across accounts. | Abstraction Rule DistinctAccountsPerWithdrawalAddress30d: Search Key WithdrawalAddress (placeholder field, Trimmed applied upstream), Search Interval 30 Days, Distinct Count over AccountId. The key is not case folded, because base58 and bech32 addresses are case sensitive. |
Matched = Abstraction.DistinctAccountsPerWithdrawalAddress30d.MatchGreaterOrEqual(2) |
Tag: Link · AML · Address→Account · 30d |
| 3.5-12 | Brokerage accounts trading opposite sides linked by attributes | Matched trading. | Abstraction Rules DistinctBuyAccountsPerTradeKey1h and DistinctSellAccountsPerTradeKey1h, each with Search Interval 1 Hour (illustrative), Distinct Count over AccountId, and filters on Payload.Side (placeholder field) for the Buy and Sell side. The Search Key TradeKey (placeholder field) is built upstream from the instrument, RoundToNearest price and quantity joined with ConcatWith, because exact matching is brittle. The linked-accounts gate comes from the attribute-link rows. |
Matched = Abstraction.DistinctBuyAccountsPerTradeKey1h.MatchGreaterOrEqual(1) And Abstraction.DistinctSellAccountsPerTradeKey1h.MatchGreaterOrEqual(1) |
Tag: Link · AML · Account pair · 30d |
| 3.5-13 | Introducing broker linked to many wash-trade accounts | IB network. | Incrementing Activation Rule Matched = Payload.WashTradeFlag.MatchIsTrue() ticks TTL Counter WashTradeFlags90d keyed on IntroducerId (placeholder field), 90 Days, Sum off. Wash-trade determination is pairwise and upstream, so Jube counts the flag. |
Matched = TTLCounter.WashTradeFlags90d.MatchGreaterOrEqual(3) |
Tag: Link · AML · IB→Account · 90d |
| 3.5-14 | Remittance senders sharing an ID document number | ID reuse at agents. | Abstraction Rule DistinctSendersPerIdDocument90d: Search Key IdDocumentHash (placeholder field, Sha256Hex of the document number applied upstream so the raw number is never stored in a counter), Search Interval 90 Days, Distinct Count over SenderId (placeholder field). |
Matched = Abstraction.DistinctSendersPerIdDocument90d.MatchGreaterOrEqual(2) |
Check: Storing and keying identity document numbers is restricted by data-protection law in many jurisdictions; confirm the lawful basis. Tag: Link · AML · Document→Sender · 90d |
| 3.5-15 | Agent linked to beneficiaries by family name/address | Agent self-dealing. | Two Abstraction Rules share one Search Key FamilyAddressKey (placeholder field, Soundex of the family name joined with NormalisedForMatching of the address, built upstream), 90 Days: DistinctAgentsPerFamilyAddressKey90d (Distinct Count over AgentId) and DistinctBeneficiariesPerFamilyAddressKey90d (Distinct Count over BeneficiaryId). Soundex and normalisation are weak on family names and addresses, so expect false positives and misses. |
Matched = Abstraction.DistinctAgentsPerFamilyAddressKey90d.MatchGreaterOrEqual(1) And Abstraction.DistinctBeneficiariesPerFamilyAddressKey90d.MatchGreaterOrEqual(1) |
Tag: Link · AML · Agent→Beneficiary · 90d |
| 3.5-16 | Mobile-money agents transacting with each other heavily | Agent-to-agent float movement used for laundering. | An unordered agent-pair key is written upstream by CanonicalPairKey to Payload.AgentPairKey, because a TTL Counter’s key is a payload field and no stateless method orders a pair in a fragment. The incrementing Activation Rule Matched = Payload.AgentAFlagged.MatchIsTrue() And Payload.AgentBFlagged.MatchIsTrue() (placeholder fields) ticks TTL Counter AgentPairCount30d keyed on AgentPairKey, 30 Days, Sum off. The reader is a second Activation Rule. |
Matched = TTLCounter.AgentPairCount30d.MatchGreaterOrEqual(3) |
Tag: Link · AML · Agent→Agent · 30d |
| 3.5-17 | Vendor linked to employee by bank/phone/address (corporate) | Conflict of interest. | List EmployeeAccounts built from payroll and List EmployeePhones built from HR, both with lifetime membership. The vendor bank account is normalised with IbanPrintFormat and the vendor phone with PhoneNumberDigits before the match, as a conflict-of-interest test. |
Matched = Payload.VendorBankAccount.IbanPrintFormat().MatchNormalisedInList(List.EmployeeAccounts) Or Payload.VendorPhone.PhoneNumberDigits().MatchNormalisedInList(List.EmployeePhones) |
Tag: Link · Fraud · Vendor→Employee · lifetime |
| 3.5-18 | Multiple vendors sharing tax ID or address | Duplicate vendors. | Abstraction Rule DistinctVendorsPerTaxId: Search Key TaxIdKey (placeholder field, RegexReplace of separators applied upstream), Search Interval set to the longest Days value the Search Key’s TTL keeps, Distinct Count over VendorId (placeholder field). The address clause can be a second Abstraction on the normalised address key. |
Matched = Abstraction.DistinctVendorsPerTaxId.MatchGreaterOrEqual(2) |
Tag: Link · Fraud · Vendor cluster · lifetime |
| 3.5-19 | Employee linked to payroll beneficiary via shared account | Ghost employee. | List EmployeeAccounts, the same list the vendor-to-employee row uses, holds employee accounts from payroll. The payroll account is normalised with IbanPrintFormat before the test. |
Matched = Payload.PayrollAccount.IbanPrintFormat().MatchNormalisedInList(List.EmployeeAccounts) |
Tag: Link · Fraud · Employee→Account · lifetime |
| 3.5-20 | Seller and buyer sharing device (marketplace) | Self-purchase (laundering or ranking manipulation). | Abstraction Rules DistinctSellersPerDevice30d (Search Key DeviceId, Distinct Count over SellerId) and DistinctBuyersPerDevice30d (Search Key DeviceId, Distinct Count over BuyerId), both 30 Days. Both roles write the device key, so the link fires when one device holds a seller and a buyer. |
Matched = Abstraction.DistinctSellersPerDevice30d.MatchGreaterOrEqual(1) And Abstraction.DistinctBuyersPerDevice30d.MatchGreaterOrEqual(1) |
Tag: Link · Both · Device→Seller/Buyer · 30d |
| 3.5-21 | Driver and rider sharing device (ride-hailing) | Fake trips. | Abstraction Rules DistinctDriversPerDevice30d (Search Key DeviceId, Distinct Count over DriverId) and DistinctRidersPerDevice30d (Search Key DeviceId, Distinct Count over RiderId), both 30 Days. This is the same cross-role device key as the seller and buyer row. |
Matched = Abstraction.DistinctDriversPerDevice30d.MatchGreaterOrEqual(1) And Abstraction.DistinctRidersPerDevice30d.MatchGreaterOrEqual(1) |
Tag: Link · Fraud · Device→Driver/Rider · 30d |
| 3.5-22 | Return address shared by many accounts (e-commerce) | Return fraud ring. | Abstraction Rule DistinctAccountsPerReturnAddress90d: Search Key ReturnAddressKey (placeholder field, NormalisedForMatching applied upstream), Search Interval 90 Days, Distinct Count over AccountId. Address normalisation is name oriented, so unit variants can split keys. |
Matched = Abstraction.DistinctAccountsPerReturnAddress90d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Address→Account · 90d |
| 3.5-23 | Loan applicants sharing employer phone number | Fake-employer ring. | Abstraction Rule DistinctApplicantsPerEmployerPhone90d: Search Key EmployerPhoneDigits (placeholder field, PhoneNumberDigits applied upstream to EmployerPhone), Search Interval 90 Days, Distinct Count over ApplicantId (placeholder field). |
Matched = Abstraction.DistinctApplicantsPerEmployerPhone90d.MatchGreaterOrEqual(3) |
Tag: Link · Fraud · Phone→Applicant · 90d |
| 3.5-24 | BNPL accounts sharing delivery address and card | Multi-account fraud. | Abstraction Rule DistinctAccountsPerDeliveryAddressCard30d: Search Key DeliveryAddressCardKey (placeholder field, NormalisedForMatching of the delivery address joined with CardId by ConcatWith, built upstream), Search Interval 30 Days, Distinct Count over AccountId. |
Matched = Abstraction.DistinctAccountsPerDeliveryAddressCard30d.MatchGreaterOrEqual(2) |
Tag: Link · Fraud · Address+Card→Account · 30d |
| 3.5-25 | Guarantor also a borrower on linked loans | Circular guarantees. | Two Abstraction Rules share Search Key PersonId (placeholder field) with Distinct Count over LoanId (placeholder field), each filtered on Payload.IsLinkedLoan.MatchIsTrue() (placeholder field) and on the role: DistinctBorrowerLoansPerPerson with Payload.Role.MatchEqual("Borrower") and DistinctGuarantorLoansPerPerson with Payload.Role.MatchEqual("Guarantor"), both with Search Interval set to the longest Days value the key’s TTL keeps. The circular case is one person holding both roles on linked loans. |
Matched = Abstraction.DistinctBorrowerLoansPerPerson.MatchGreaterOrEqual(1) And Abstraction.DistinctGuarantorLoansPerPerson.MatchGreaterOrEqual(1) |
Tag: Link · Fraud · Person→Loan · lifetime |
| 3.5-26 | Charity donors sharing devices with the charity’s own officers | Donation round-tripping. | Abstraction Rules DistinctDonorsPerDevice90d (Search Key DeviceId, Distinct Count over DonorId) and DistinctOfficersPerDevice90d (Search Key DeviceId, Distinct Count over OfficerId), both 90 Days. The same cross-role device key as the seller and buyer row; round-tripping amounts are a separate test. |
Matched = Abstraction.DistinctDonorsPerDevice90d.MatchGreaterOrEqual(1) And Abstraction.DistinctOfficersPerDevice90d.MatchGreaterOrEqual(1) |
Tag: Link · AML · Device→Donor/Officer · 90d |
3.6 Network metrics as rule inputs
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 3.6-01 | Degree centrality above threshold | Entity connected to an unusual number of others. | Abstraction Rule DistinctCounterpartiesPerEntity30d: Search Key EntityId (placeholder field, both roles feeding the key), Search Interval 30 Days, Distinct Count over Counterparty (placeholder field). This is raw degree only; normalised centrality needs graph-wide state. The threshold is illustrative. |
Matched = Abstraction.DistinctCounterpartiesPerEntity30d.MatchGreater(10) |
Tag: Link · AML · Entity · 30d |
| 3.6-06 | Star pattern (one hub, many spokes) | Classic mule-herder topology. | Abstraction Rule DistinctSpokesPerHub7d: Search Key HubId (placeholder field), Search Interval 7 Days, Distinct Count over Counterparty (placeholder field). It counts hub breadth; it does not test that the spokes are sparse, so the star shape is not confirmed. |
Matched = Abstraction.DistinctSpokesPerHub7d.MatchGreaterOrEqual(3) |
Tag: Link · AML · Hub · 7d |
