Skip to content

Actors

An actor is WebDecoy’s persistent identity for an automated client. Instead of treating every detection as an isolated event tied to an IP address, WebDecoy correlates detections into actors, so a bot that rotates through dozens of IP addresses shows up as one adversary with one history, not dozens of unrelated events.

WebDecoy Actors page showing persistent adversary identities correlated across detections and IP addresses

The current Actors screen turns repeated events into a history you can investigate and act on.

IP addresses are the weakest possible identity for a bot. Scrapers and attack tools rotate through datacenter ranges and residential proxy pools, and every new address resets an IP-based counter or block. Actors solve this by keying identity to what doesn’t change when the address does.

Each actor is keyed by a composite fingerprint built from signals captured across every detection:

  • Network signals: the JA4 TLS fingerprint captured from the client’s actual TLS handshake, plus request characteristics. These work even for bots that never execute JavaScript.
  • Device signals: browser characteristics collected by Detection Scripts where they’re installed.
  • Cryptographic identity: for clients that present a verifiable signed identity (e.g. HTTP message signatures from AI agents), the signature issuer becomes the strongest identity tier of all.

A correlation worker continuously joins new detections to existing actors using the strongest signals available. When the same fingerprint appears from multiple IP addresses, those observations all fold into the same actor record.

Each descriptive field on an actor (its network, its country, the agent it claimed) is read from the most recent sighting that actually recorded that field, not from the newest detection outright. Geographic and network enrichment is sampled rather than captured on every request, so an actor can easily carry it on 2 of its 50 detections. Reading only the newest row reported “no origin” for actors whose network was sitting two rows further down.

Click Actors in the sidebar to see the adversaries tracked for the property you have selected.

The list is a triage queue rather than a dump of every fingerprint ever recorded. It opens on Returning (actors seen more than once) and sorts by recent activity, because an identity seen exactly once from exactly one address is a detection that happened to get fingerprinted, not an adversary worth reading about.

Every actor carries a stable handle like amber-otter-417, derived from its fingerprint rather than stored. Nobody can recognise 0945d1bd2ca3087366cad125… on a second visit, describe it to a colleague, or notice that the row they dismissed yesterday is back.

The handle is a label for recognition; the fingerprint prefix stays visible beside it and is what API calls, enforcement rules and support conversations actually use. The same actor carries the same handle on every surface: this list, its own page, the Actor column in Detections, and the detection flyout.

Column Meaning
Actor The handle, a threat dot, badges for whichever findings apply, a plain-language identity line, and the fingerprint prefix
Activity What the actor did, as a sentence: hits, distinct addresses, and traps tripped
Origin Whose network it came from (network operator and country)
Last seen Most recent activity, relative
(unlabelled) Dismiss an actor you have already judged, or restore one you dismissed

Dismissal is triage state, not deletion: the list has to be able to get shorter, or there is no reason to open it a second time.

The identity line says who the traffic claims to be and whether the claim survived checking. A declared crawler that could have been checked and was not reads as claiming that agent, unverified, which is the scorer’s own word for a claim that could be tested and was not established. A browser user agent never carries that qualifier, because there is no published identity behind “Chrome” for a check to have failed against.

The Show control narrows the list. Each segment carries its own total, computed over the same scope as the rows beside it, so you can see which findings have anything in them before clicking:

Slice What it holds
Returning Actors that came back, seen more than once (the default view)
Probed Actors that went looking for credential files
Impersonating Actors that claimed a published crawler and failed the proof
Rotating High-threat actors seen from 5 or more addresses
Ready Meets the enforcement gate and can be armed on your WAF
Armed Blocking rule live on your WAF
All Every actor on this property, including identities seen only once

Sort offers recent activity, first seen, most detections, most credential probes, most distinct addresses, and highest aggregate threat. Busiest and most-probing are different questions, so they are different sorts.

Active limits the list to actors seen in the last 24 hours, 7, 14 or 30 days, or all time. The window travels in the URL, so a link from a dashboard tile lands on the same numbers the tile computed rather than quietly reframing the data.

Whatever the view leaves out is disclosed under the list: how many identities were seen only once, and how many have been dismissed, each with a control to bring them back. A filtered list that does not say it is filtered reads as “this is all there is”, which on this page would mean concluding your site is quiet when the view is simply narrow.

Badge Meaning
Probed for secrets Requested a file whose only content is credentials
Rotating identities Claimed several published agents and proved none of them
Impersonating Claimed a declared agent and failed the proof
Verified A sighting proved this actor’s declared identity
Grouped This fingerprint covers more than one party
Ready to arm Meets the enforcement gate
Armed Blocking rule live on your WAF

Click any actor to open its detail page, titled with the handle you clicked:

  • Human likelihood (where behavioral observations exist), from pointer and touch cadence only, kept deliberately separate from the threat score.
  • Fingerprint (crypto, device, network and clearance identities), the composite the actor is keyed by.
  • Enforcement, the actor’s current enforcement state (see Response Actions).
  • Went looking for credentials, on actors that probed: every credential-bearing path it requested, with per-path counts and last-seen times. This is aggregated server side across the actor’s whole history rather than read off the recent-detections sample, so it does not contradict its own heading on an actor with hundreds of probes.
  • Cross-IP footprint, every IP address and user agent the actor has used, with observation counts and last-seen times. This is where rotation becomes visible: one fingerprint, many addresses.
  • Recent detections, the individual detections attributed to this actor, each row showing what the request asked for and opening its full detection detail on click. A link beside the heading opens the actor’s complete history in the detections list, where it can be filtered and exported like any other view.

Every other judgement on an actor reasons from who it claims to be, and all of them are confounded by shared infrastructure. One Cloudflare WARP egress address in production carries twelve different fingerprints: real people reading marketing pages, alongside a client asking for .env, .dev.vars and .docker/config.json while claiming to be Googlebot, then ChatGPT-User, then an Android browser.

What a client asked for is not confounded that way. WebDecoy counts each actor’s requests for files whose only content is credentials: .env, .aws/credentials, .docker/config.json, service-account keys, gcloud application-default credentials and similar. Nothing browsing your site reaches these, and no crawler indexes them.

There is no threshold: one request is enough to flag an actor. The finding leads the identity line ahead of every identity signal, because once a client has asked for .env, who it claimed to be has stopped being the interesting question.

This is also the one slice that does not exclude coarse fingerprints. The exclusions elsewhere exist because those findings are claims about a single party; a credential probe is a claim about a request, and it holds however many parties share the key.

One adversary in costumes, or a key shared by many

Section titled “One adversary in costumes, or a key shared by many”

Several user agents under one fingerprint have two opposite explanations, and counting user agents cannot tell them apart. Verification can. WebDecoy tracks how many distinct published agents an actor has claimed, and how many of those claims were ever proved:

  • Claims that all fail mean one adversary cycling cover stories. The row is badged Rotating identities and says how many agents were claimed and that none were proved. One production actor claimed seven different AI crawlers across 986 hits from three addresses, and verified as none of them.
  • Claims that verify mean unrelated clients sharing a key. Nobody is both Googlebot and Amazonbot, so two identities each proving themselves under one fingerprint is proof the fingerprint describes more than one party.

WebDecoy calls a fingerprint that covers more than one party grouped, whether the evidence is that proof or the older heuristic (a wide spread of user agents under one key). The badge is rendered once even when an actor trips both, and its tooltip carries the reason, preferring the proof over the heuristic.

Rotation across identities outranks any single claim in the wording of a row. “Impersonating ClaudeBot” is true of an actor that also claimed six other crawlers, and it is the least interesting true thing that can be said about it.

A grouped actor’s detail page carries the caveat in full: its addresses belong to several parties rather than one, its IP spread is not counted as rotation, and its fingerprint is never offered for WAF enforcement, because blocking it would block whoever else shares it.

Impersonation is an accusation, and is guarded like one

Section titled “Impersonation is an accusation, and is guarded like one”

“This actor claimed to be Anthropic and failed the proof” is a statement about one party, so WebDecoy withholds it wherever the identity is too wide to be one party, or the evidence too thin to convict. Two guards do that work:

  • A grouped identity is never accused. A key covering dozens of unrelated clients eventually contains somebody else’s failed check. Coarse actors have been excluded from the rotating slice since the model shipped, and are now excluded from the impersonator slice for the same reason.
  • A published list that says nothing cannot condemn. Impersonation is proved by checking the address against the crawler’s own published IP ranges. OpenAI’s list for ChatGPT-User publishes 226 IPv4 prefixes and no IPv6 at all, so every IPv6 request carrying that user agent was being condemned by a document that had never mentioned an address of that kind. A miss is now reported only when the published list could have contained the address. Otherwise the claim stays an unproven claim rather than a proven lie. Absence of an answer is not evidence of lying.

An actor qualifies as a confirmed rotating actor once it has been caught from 5 or more distinct IP addresses with an aggregate threat score of 60 or higher. Low-threat multi-IP traffic (a corporate network sharing one fingerprint, for example) never qualifies, because the gate counts adversaries, not shared connections. The dashboard’s actor row tracks how many rotating actors are Ready to block (safe to enforce but not yet armed) versus Armed on your WAF (blocking rule live).

Because the actor (not the IP) is the unit of identity, it’s also the unit of enforcement. A Response Action triggered by a confirmed rotating actor can push a fingerprint rule to your AWS WAF that applies to the actor across every IP it rotates through, not just the last address you happened to see.

For browser-grade actors, the actor detail page also carries session clearance deny controls: Deny actor puts the actor’s device fingerprint on the clearance deny-list so it can’t mint a clearance token on any IP, and Remove deny reverses it. A decoy hit applies the same denial automatically.

The same deny is available directly from a detection’s detail flyout, so the decision can be made on the screen that proved it rather than two navigations away. It is withheld, with the reason stated, when the actor’s fingerprint is grouped (a deny would lock out whoever else arrives under that key) or when the actor has never carried a clearance token (there is no clearance identity to bind a deny to).

Denying one actor is a one-off. Respond to actors like this opens the Response Action editor so the next one is handled without anybody watching. With zero response actions configured, nothing is ever pushed to a WAF.

Actors are available programmatically:

GET /api/organizations/{organization_id}/actors
GET /api/organizations/{organization_id}/actors/stats
GET /api/organizations/{organization_id}/actors/{actor_id}
PATCH /api/organizations/{organization_id}/actors/{actor_id}/dismissal

The list endpoint takes the same controls the screen does. filter accepts returning, secret_probe, impersonator, rotating, ready and enforced (omit it for every actor); sort, property_id, start_date, end_date, include_dismissed, page and page_size do what their names suggest. The response carries the per-slice totals alongside the rows, so a client can render the counts without a second round trip.

See API Keys for authentication.