Bot detection layers
Four layers of signal, ordered from cheapest to most expensive to evade. We document the model that PPPoker and its larger clubs actually use, separated from the folklore that circulates on forums.
The detection question is not «can the platform tell that a particular player is a bot.» That question is answerable but rarely the one that decides outcomes. The deciding question is whether the four parties that look at any given player — the client device, the table software, the club's analytics, and a human reviewer — produce a consistent verdict, and whether the cost of the false positive is low enough to act on.
Each layer below has been independently corroborated against either platform documentation, club operator interviews, or hand-history forensics performed on our side. Where we are extrapolating, we say so.
Layer 1 — Device fingerprint
The PPPoker client is a native Android/iOS app with a desktop wrapper. On install and on every session start it reports a fingerprint composed of, at minimum: device model, OS version, screen resolution, GPU string, root/jailbreak signal, ADB/USB-debug flag, installed-input-methods list, and a stable device ID hashed into the platform's own identifier space.
What this layer catches: emulators with default fingerprints (BlueStacks/LDPlayer/MEmu out of the box), automation tools that hook the client process, and multi-accounting from a single physical device.
What it does not catch: a real Android phone running a real session with a real ID. A well-resourced operator runs phone farms — racks of real handsets each with a unique IMEI and a unique platform ID — precisely because Layer 1 cannot distinguish them from a player's daily-driver phone.
# signals collected at session start (illustrative; field names obfuscated)
{
"dev_model": "SM-G991B",
"dev_os": "android-12",
"dev_id": "<sha256(platform-secret + hw-fingerprint)>",
"dev_root": false,
"dev_emu": false,
"input_methods": ["com.google.android.inputmethod.latin"],
"gpu": "Mali-G78 MP14"
}
Layer 2 — Behaviour timing
The interesting layer. The client reports per-action timing on a millisecond scale: time-from-deal to first interaction, time-to-action distribution, drag distance and trajectory of the slider for bet sizing, the kinematics of the «check» tap (single-finger vs multi-finger touch envelope), idle gestures while not acting, app foreground/background transitions during a session.
The detection target on this layer is not «too fast.» Bots can introduce arbitrary jitter and a competent operator does. The target is shape: the joint distribution of (decision-difficulty × time-taken × confidence-of-action) does not look human when generated from a deterministic policy.
Concretely, a human who has just been three-bet on a wet board on the river thinks for a long time and then sizes either small or pot-committed; a polished bot that has tuned its jitter on the marginals will think for a varied time but sizes a continuous distribution. Both produce a histogram of think-times that passes a chi-squared test against the human prior; only one passes a joint test against decision context.
This is also the layer where touch envelope matters. A real index finger landing on a 320-pixel button generates a different touch-area trajectory than a synthetic event injected via the accessibility API, and the difference is in the noise floor of the contact patch, which the client samples at session start to establish a baseline.
Layer 3 — Opponent-history graph
The layer that changed the game post-2024. PPPoker maintains a per-player history of opponents-faced, expressed as a graph: each player is a node, an edge weight encodes hands-played-together and net chips exchanged. The platform queries this graph to score collusion and bot-pair signals.
The detection pattern that is hard to break: a synthetic player will, in the long run, accumulate edges only with the population that frequents the clubs the operator runs the bot in. A real player accumulates a long tail of edges with players in other clubs (joined to follow a friend, played a freeroll, took a guest seat in a union game). The «opponent-distribution tail thinness» is a strong statistical signal and is cheap to compute.
The graph also exposes circles: if N suspected accounts have been at the same tables together far more often than the union liquidity model predicts, that is detection without needing any per-account behaviour analysis at all. This is the layer that catches phone farms whose accounts are otherwise indistinguishable at Layers 1 and 2.
# conceptual: opponent-graph score
def graph_score(player, window=90_days):
edges = opponent_edges(player, window)
tail_thinness = 1 - shannon_entropy(edges) / log(len(edges) + 1)
circle_density = max_clique_density(edges, threshold=0.05)
return alpha * tail_thinness + beta * circle_density
Layer 4 — Human review
The final layer and the only one with binding authority. A human reviewer — on the platform's trust-and-safety team, or, more commonly, on a club owner's staff working from a hand-history export — looks at flagged sessions and forms a judgement.
Three signals tend to land a session in front of a human:
- Layer-2 anomaly score above a configured threshold for the table stake.
- Layer-3 graph score in the top decile of the club's distribution.
- A counterparty complaint — another player or an agent flagging the account.
The human review is not deterministic. We have seen accounts with strong Layer-2 and Layer-3 signals survive review because the reviewer was on the operator side, and we have seen accounts with weak signals get banned because a senior agent escalated the case to a union-level dispute. The unevenness of this layer is its own feature; deterministic detection at Layer 4 would commoditise evasion within a quarter.
What follows from this model
- Single-account hobbyist bots are caught at Layer 1 or Layer 2 and rarely escalate.
- Phone-farm bots route around Layer 1 cleanly but accumulate a Layer-3 graph signature within weeks, the time window in which an operator needs to either rotate seats aggressively or hide them inside a cooperative club's regular play.
- Liquidity bots running with the owner's permission inside a single club are detectable in principle but operationally invisible, because the human reviewer authorised by that club is the owner. The detection regime that matters for them is the union's external view, which is dominated by Layer 3.
- Reverse engineering one of these layers in isolation is a category mistake. The layers are jointly informative; defeating one shifts the cost basis of the next.
If this is your week
If you are running a club and the question «how exposed are we» is recurring, the people who built and stress-tested detection from the operator side are reachable directly. The first conversation is private and free, and we do not publish a price list because the work is never the same twice.