Repository navigation
Analysis and findings
What the analysis finds, how auto-expand follows the money, and what none of it proves.
-
Auto-expand - follow the money several transactions deep in one go. Select a transaction or a wallet and press Auto-expand (left panel), or right-click it, or use the buttons in its details. Direction is forward (who spent the outputs) or backward (what funded the inputs); Hops is how many transactions deep; Follow chooses every output (the largest first, up to a limit per transaction), the largest output only, the likely change output (stops where the evidence does not single one out, rather than guessing) or a peel chain (the large output of each one-input, two-output transaction whose small output is at most half the large one and at least 1,000 satoshis; stops at anything else). It also stops at Stop after this many transactions and or this many requests (whichever comes first, on top of the page's usual request limit), ignores amounts below a minimum you set, and stops at wallets you labelled or tagged or that are on the built-in list (so a trail ends at an exchange) and at CoinJoin-like transactions (following money through a mix is meaningless). It loads level by level, only fetching spending information for levels it will still look at, can be cancelled with what is loaded kept, and finishes with one line saying what happened and why each branch ended (labelled wallet, unspent output, CoinJoin, no clear change, a limit). In the Trace layout everything it loaded is shown, placed by the current arrangement (in Columns, beside the vertex it came from and without moving anything already there), and one
Ctrl+Ztakes the whole step back; in other layouts the Focus view is switched off so the whole path shows. A wallet can be the starting point (its history is loaded if nothing is known about it yet). -
Analysis - patterns in the transactions that are loaded, each with its reasons. Badges above a transaction: CONSOL n→m (5 or more inputs gathered into 1 or 2 outputs), BATCH n→m (up to 3 inputs paying 6 or more outputs), COINJOIN? (3 or more input wallets and several outputs of exactly the same value to different addresses) and PEEL i/n (step i of a run of n or more transactions that each pay a smaller amount, not a dust-sized one (under 1,000 satoshis), out of a single coin and pass the rest on). A peel chain is named after the transaction it starts at (P-65a1, longer only where two chains would start alike) and has one colour, so the name and colour stay the same whatever else is loaded, in the Findings panel and in a report. Badges only show their text where it fits: zoomed out, or where two would sit on top of each other, a small dot in the pattern's colour stands in, and the vertex you point at or select shows its text and its chain name (PEEL 3/6 · P-65a1). The four kinds can be switched off one by one (remembered in this browser). Detections (under the findings, in the right-hand panel) lists every pattern found, oldest first, with the reason for each, the same list the report has and without the cap of the short lists above it: filter by kind or by peel chain, click a row to select that transaction and bring it into view (one that is not on screen is brought in, as with Find), Select these selects what is listed and Copy list copies it as tab-separated text. Pick a peel chain there (or open any of its steps) and Show on graph lights its path up in the chain's colour, numbers the steps and fades everything that does not touch it (a chip at the top left says which chain and clears it); Trace this chain starts the Trace layout from its first step and reveals the others in order, and Follow ▶▶ on a step carries on along the same rule beyond what is loaded; ◀ Step and Step ▶ in a step's details walk along the chain. Assistant (its own section): Copy for assistant copies the instructions and the evidence as text for any AI assistant to read, with a task to choose (triage, a draft of the findings text, an explanation of the selected vertex, a challenge of the findings). The findings come from the analysis and the model only reads, ranks or words them; every transaction id and address in an answer is then compared with the evidence (Check ids) and the ones that are not there are named. Ask the local model does it for you with a model on this computer (Ollama, LM Studio or a llama.cpp server): only localhost, 127.0.0.1 or ::1 is accepted, no key is sent, and nothing leaves the machine. Claude is the one exception: it is off whenever the page opens, and when you switch it on and give your own Anthropic API key it shows the exact text first and sends the evidence to Anthropic (api.anthropic.com) only after you confirm each time; the Assistant heading carries a Claude on marker while it is on. The same evidence is available without the page as JSON (
tools/hopline-cli.js, see docs/interface.md). Likely change: a transaction's details name the output that most likely returns to the sender, with a confidence and the evidence (its address is also an input; it is the only output of the same address type as the inputs; the other output is a round amount or pays a known or tagged wallet; it is smaller than every input, which a wallet would not have needed extra inputs to pay) and that output's edge is coloured amber with likely change or change? beside the amount. When two outputs are a close call nothing is named. Clusters: addresses spent together as inputs of one transaction (the common-input rule, usually one owner) are drawn with a dashed ring in one colour per cluster, listed in the panel (click to select the members) and in the address's details, where Tag the cluster… tags them all at once. CoinJoin-like transactions are left out, and on any transaction you can press Not the same owner: exclude to leave it out by hand (remembered in the session and the export). A guess of medium or low confidence is called Possible change (change? on the edge) and says so: it is a lead, follow both outputs, and a guess that rests on one sign says that too. Two outputs within 1% of each other are not named unless one address is also an input, because an even split looks like two payments, not a payment and its change. Each badge, the change marks and the clusters have a switch under Analysis, and the exports gainpatterns,cluster,cluster_size,excluded_from_clustering(nodes) andlikely_change(edges) columns. -
Loaded but not on screen - the line under the canvas says how much is loaded that is not drawn (N shown · M more loaded, or N of M vertices), and the findings are over everything loaded, not only what is drawn. When something notable is among the vertices that are not on screen (a pattern, a cluster, a change guess, a named wallet, a service hand-off, an open end) a grey chip Not on screen counts them, its tooltip says what is among them, and it opens a section at the top of the Findings that lists the named wallets, hand-offs and open ends, each one click from being brought into view (in a trace a named wallet is added as another starting point, a hand-off or an open end shows its route;
Ctrl+Ztakes it back). The report and the Visible part only export cover what is on screen: the report says at the top how many loaded vertices it leaves out (the count is in its embedded evidence too), so nobody reads it as the whole workspace. -
Routes and open ends - for a wallet with a name (the Known wallets in the Findings), whether and how money ran between it and the reference: the first starting point of the trace, unless you pick another next to Relative to (a wallet tagged THREAT, or the one vertex selected, when there is no trace). Each known wallet says ◀ upstream (money it spent reached the reference) or ▶ downstream (the reference's money reached it) with the number of transactions, the ones with a route first, or that there is none in what is loaded. Click it to light the route: the coins in the order they moved, numbered on the transactions, in the same gold highlight as a peel chain (the chip at the top left clears it). In the Trace layout what was not on screen is added next to what is, in one step that
Ctrl+Ztakes back; the Findings list the steps (transaction, time, inputs and outputs, the coin that moved) with Copy route. A route is found by following each coin to the transaction that spent it, never by matching addresses: an address used again years later does not join two unrelated flows. It is never carried across a service wallet (a name from a list that is not a ransomware or sanctions entry, or a vertex tagged EXCH, MIXER, SERVICE, MARKET or GAMBLING) unless there is no other way, and then it says so, because such a wallet mixes everyone's coins. A step that merges many inputs is named too: coins of other origins join the route there. A route shows that the money could have travelled this way, not who controls the wallets between, and no route only ever means none in the loaded transactions. Adding a wallet in the Trace layout that has a route to the reference offers it (Show route), and the details of such a wallet or transaction show it as well. Open ends (a chip on the canvas and a section of the Findings, forward or backward) list where the trail from the reference is still open: outputs whose spend was never looked up, spenders that are not loaded (forward) or inputs whose funding transaction is not loaded (backward), with the amounts, where it stopped at a named wallet, and what sits unspent. Continue from these follows them with the Auto-expand settings and stops at labelled wallets, showing the way from the reference first; a wallet with no route may still be connected beyond them. No route also offers search upstream and search downstream: they load from both ends, round by round, the largest open ends going forward from one wallet and the largest going backward from the other (named wallets and CoinJoin-like transactions end a branch), until a route appears or the limit of Auto-expand is reached, and then say why they stopped (nothing left to load, a round that got nothing back, the round or request limit). Money only moves forward in time, so an end that cannot fit the route is left until nothing else is: nothing before the first time the start wallet spent, nothing after the last payment to the end. When one end is a service wallet, whose deposits or withdrawals are thousands of unrelated payments, the ones nearest in time to the other end go first, so a route is reached in fewer rounds for the same requests. Service hand-offs (a chip and a section of the Findings) list where the trail from the reference meets a service wallet: money going into one (a deposit) or coming out of one (a withdrawal), oldest first, with the transaction, the time, the amount and the wallet just before a deposit or just after a withdrawal (a candidate for the deposit address, or for the customer's wallet): what a request to the service needs. The table can be copied or downloaded as CSV, is in the report and the assistant evidence, andtools/hopline-cli.jsprints it (trace,handoffs --csv,route; see docs/interface.md). A service is a name from a list that is not a ransomware or sanctions entry, or a tag EXCH, MIXER, SERVICE, MARKET or GAMBLING. A wallet that a sanctions or ransomware list names is not a service, so the trail goes on through it, but it is reported on the way: it is a row too, marked named (the direction reads into the wallet or out of the wallet, and the CSV has a kind column, service or named), and the chip and the section are then called Hand-offs. Your own labels and tags are not rows: you put them there. Suggested names offer a name for wallets that were spent in the same transaction as a named wallet (an exchange sweeping its deposit addresses into its own wallet, for example): the name on a list, or a label you gave that wallet (the row says which): a guess from the common-input rule, never applied by itself; Name them gives each a label "Name (by co-spending)" and a note saying which transaction it rests on and whose name it is, and a cluster that holds two different names is reported as a conflict instead. A label you gave counts as the name only in a cluster where no list names a wallet: next to a list's name it is left out, since a label may describe a wallet rather than say who owns it. A label that Name them wrote is a guess and is never used as the name to spread to the next wallet. Adding a wallet that has thousands of transactions says that only the newest are loaded, so an older payment will not show there: trace toward it instead. The wallet you pick under Relative to is kept in the saved session and in the exported file. -
What was found, in view - a strip of chips over the graph, above the counts, says what the analysis and the lists found in what is loaded: Sanctions (red, wallets on a sanctions list), COINJOIN?, CONSOL, BATCH, PEEL (chains), Clusters, Change guesses and Known wallets, each with its count. A click selects what the chip counts (the same as the Select these buttons) and makes those vertices pulse for a few seconds - a glow that breathes and a ring that spreads out from each, drawn over everything and never smaller than a few pixels, so one vertex can be found in a crowded graph (the same happens for a row of the Findings or Detections lists, a Find result and Select these; with reduce motion set in the system the ring and glow stand still), and the chip also opens the Findings panel on the right at that group (Findings ▸ opens it at the top). The right-hand panel has two views, Details (what is selected) and Findings, switched by tabs when both have something to show: with nothing selected it shows the findings, clicking on the graph turns it to Details, and acting from the findings (a row, Select these) keeps it on Findings. The Findings list the sanctions hits, the patterns, the possible peel chains, the address clusters, the change guesses and the known wallets, followed by the Detections list; the left panel keeps only the switches under Analysis. A folded panel's tab says Findings (n). The strip only shows what there is, counts what was found whatever the switches under Analysis draw on the graph, and the heading Analysis (n) carries the same count when the section is folded.
-
Where a transaction came from, and its wallet's habits - every transaction fetched records the API it came from and the time (shown in its details, in the report and in the exports:
data_source,retrieved_unix,retrieved_utc). It also keeps its wallet's habits: the version, whether the locktime is a recent block height (the "anti-fee-sniping" Bitcoin Core does), the sequence numbers (RBF or not), and BIP 69 ordering where an ordering means something. The details show them as Wallet habits, and the likely-change reasoning uses them: an output whose next spender has the same habits as this transaction, while the other output's spender does not, is a little likelier to be change (a weak sign, weaker still when the habits are the defaults of many wallets).
What the analysis is, and is not. Every result under Analysis is a heuristic from the loaded data and none is proof of who controls what. A peel chain is a shape that ordinary wallets paying out of a single coin also produce (the default needs 4 in a row); the change guess is wrong whenever a wallet pays change to a fresh address of another type or rounds its change; clusters join addresses that were spent together, which a CoinJoin, a PayJoin, a shared custodial wallet or a multisig can make misleading (the CoinJoin check is itself a guess: 3 or more input wallets and several outputs of exactly the same value to different addresses). Only loaded transactions can be analysed: one that is not loaded has no inputs or outputs to look at, so a cluster, chain or flag can appear or change as you load more. Address clustering by change (joining a change output to its inputs' cluster) is deliberately not done: a wrong change guess would merge two owners. Quote the reasons the page shows, not the badge. How often each guess was right on test data, and where it breaks, is on Accuracy.