The cheapest way to manufacture a trustworthy agent is to build two of them and have them pay each other. Both look clean in isolation. This is how the pattern works, why one-sided checks cannot see it, and what actually gives it away.
Reputation you can buy from yourself
Any system that scores agents on settled payments has an obvious exploit, and it is worth stating plainly because we build one of those systems.
An operator registers two agents. Agent A pays agent B, repeatedly, in small amounts. Every payment is a genuine on-chain transfer with a real hash. Neither agent is doing anything a protocol would reject. After a few dozen rounds both have a settlement history, both score well, and the operator has spent only gas and the float between its own wallets.
Now agent B goes and sells to a stranger, carrying a reputation it manufactured. That is the attack, and it costs almost nothing.
Why checking one side cannot work
Look at agent B on its own and everything is fine. Registered, KYA passed, dozens of completed settlements, no revocations, reasonable tenure. Every individual fact is true.
Look at agent A on its own and the same applies.
The fraud does not live in either agent. It lives in the relationship between them, and a check that takes one agent id as input cannot see a relationship by construction. This is not a matter of the model being insufficiently clever; the information is not in the input.
That is why a two-sided check is a different product rather than a better version of the same one. It takes both parties and asks whether they are independent.
What actually gives it away
Four signals, in rough order of how hard they are to fake.
The first is funding lineage. Two agents whose wallets were funded from the same source, especially in the same transaction or within a few blocks, are related until proven otherwise. An operator can launder this with intermediate hops, but each hop costs effort and leaves more graph.
The second is the shape of the payment graph. Honest commerce is lopsided and open: an agent pays many counterparties, receives from a different set, and value flows through it. Manufactured history is closed: the same pair, back and forth, value circulating rather than arriving. Measure how much of an agent's volume comes from its top counterparty. A number near one is not a customer relationship, it is a loop.
The third is timing regularity. Humans and real workloads are bursty. Payments spaced with machine regularity, at the same interval, in the same amount, are a script rather than a business.
The fourth is registration and deployment fingerprints: agents registered minutes apart, from the same address, with endpoints on the same host or the same certificate. Individually weak, jointly strong.
The problem with catching it
Every one of those signals has a legitimate version, and that is the hard part.
Two agents funded from the same treasury might be two products of the same honest company, which is normal and not fraud. A high concentration with one counterparty might be an agent with one large genuine customer. Regular timing might be a genuine scheduled workload.
So this cannot be a boolean. Treat it as evidence that raises or lowers confidence, weight it against the amount at stake, and report it as a reason rather than a verdict. "These two parties appear to share funding and 94 percent of this agent's volume is with the other" is something a human can adjudicate. "SELF_DEALING_DETECTED" is a support ticket.
The right response to a strong signal is usually WARN, not DENY. You are saying the relationship looks closed, not that a crime occurred.
What this means if you are honest and look suspicious
If you run several agents from one treasury, you will trip some of these signals, and there is a reasonable way to handle that.
Fund agents from distinct wallets where you can. Do not have your own agents transact with each other purely to generate history, because that history is worth nothing and the pattern is what gets flagged. Build settlement history with real third parties, even a small number, because concentration is measured against your whole graph and a handful of genuine external counterparties changes the shape.
The general principle: a reputation built from transactions with strangers is the only kind that means anything, which is inconvenient early and is also the entire point.
Where the check belongs in your flow
Run it before you commit value, alongside the counterparty risk check rather than instead of it. They answer different questions: one asks whether this party is sound, the other asks whether this pair is independent.
It matters most at the moment an agent you have never dealt with presents a strong reputation. That is exactly the situation the attack is designed to produce, so a good history from an unknown counterparty should raise the bar for the two-sided check rather than lower it.
Our own data has something uncomfortable to say here. Of the 120 settlements our trust oracle has taken, the two-sided check accounts for 6, about 5 percent. It is by a wide margin the least-bought thing we sell. Either it is under-explained, which is on us, or the ecosystem is still small enough that nobody has been burned yet. We cannot tell which from the data, and only one of those fixes itself.
Trying it
`counterparty_check` takes both agent ids and the intended amount, and returns a verdict with the reasons that produced it. It is the only call we offer that examines a relationship rather than a party.
If you are building your own version of this rather than using ours, that is a good outcome too. The pattern is cheap to run and currently under-defended across the whole ecosystem, and the first operators to exploit it at scale will do it against whoever is not looking.