pppokerbot

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"
}
Cost to evade. Roughly the price of a used Android phone plus a fresh SIM, per seat. This is the layer that hobbyists fail on and that any serious operator already routed around three years ago.

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.

Folklore versus reality. «The bot folds in 17ms» is folklore; nobody ships a bot that fast in 2026. «The bot's pre-flop think-time distribution is suspiciously narrow against deep stacks» is the kind of thing the platform's analytics actually flag.

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
Cost to evade. Higher than the previous two layers combined. A synthetic opponent-history graph that mimics a real one requires either real cross-club exposure (which means losing chips into real games for cover) or coordinated platform manipulation on a scale no agent has been observed to run.

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:

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

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.