@osqd/bothandlerjs 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +177 -21
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +177 -21
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/dashboard/client/feed.d.ts +16 -0
- package/dist/dashboard/client/format.d.ts +18 -0
- package/dist/dashboard/client/pager.d.ts +32 -0
- package/dist/dashboard/client/store.d.ts +60 -0
- package/dist/dashboard/client.generated.d.ts +1 -1
- package/dist/detectors/crawler-verification.d.ts +34 -1
- package/dist/detectors/index.d.ts +4 -1
- package/dist/detectors/known-bots.d.ts +49 -1
- package/dist/element/index.cjs +345 -39
- package/dist/element/index.cjs.map +1 -1
- package/dist/element/index.js +345 -39
- package/dist/element/index.js.map +1 -1
- package/dist/index.cjs +177 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +177 -21
- package/dist/index.js.map +1 -1
- package/dist/state.d.ts +1 -1
- package/docs/detection/signatures.md +36 -0
- package/docs/operations/dashboard.md +26 -0
- package/package.json +1 -1
package/dist/state.d.ts
CHANGED
|
@@ -153,7 +153,7 @@ export declare class ActorRegistry {
|
|
|
153
153
|
* A read, and only a read: it neither records a request against an actor nor moves
|
|
154
154
|
* one up the LRU, so watching the list cannot change what it lists.
|
|
155
155
|
*/
|
|
156
|
-
top(limit: number, now: number): ActorSummary[];
|
|
156
|
+
top(limit: number, now: number, offset?: number): ActorSummary[];
|
|
157
157
|
forget(key: string): void;
|
|
158
158
|
clear(): void;
|
|
159
159
|
/** Marks an actor as holding valid human clearance until `until`. */
|
|
@@ -41,6 +41,18 @@ individual name does not.
|
|
|
41
41
|
| `library` | curl, wget, python-requests, Go-http-client, okhttp | usually challenge |
|
|
42
42
|
| `headless` | HeadlessChrome, Playwright, Puppeteer, Selenium | usually challenge |
|
|
43
43
|
| `embedded` | Smart TVs, set-top boxes, game consoles | allow |
|
|
44
|
+
| `commerce` | idealo, Kelkoo, PriceRunner, Trivago, Skyscanner, Indeedbot | a commercial decision |
|
|
45
|
+
| `academic` | Crossref, OpenAlex, university web-science crawls | usually allow |
|
|
46
|
+
| `accessibility` | Siteimprove and other WCAG auditors | allow |
|
|
47
|
+
|
|
48
|
+
The last three are separate from their nearest neighbours because the decision is. A price
|
|
49
|
+
comparator is not an SEO auditor — nothing there is auditing your site for you, and the
|
|
50
|
+
same crawler is a distribution channel to one retailer and a competitor's research tool to
|
|
51
|
+
the next, which is why nothing in `commerce` is marked benign. A citation index is not a
|
|
52
|
+
model being trained, and an operator refusing the second is often glad of the first. And an
|
|
53
|
+
accessibility crawler is not an uptime probe: it is usually commissioned by the site's own
|
|
54
|
+
owner and then forgotten about, so blocking it does not reduce load, it makes an
|
|
55
|
+
accessibility report look clean by removing the evidence.
|
|
44
56
|
|
|
45
57
|
```ts
|
|
46
58
|
{ id: "no-ai", match: { category: "ai" }, action: "block", reason: "Not for model training." }
|
|
@@ -54,6 +66,7 @@ What, if anything, can check the claim:
|
|
|
54
66
|
type Verification =
|
|
55
67
|
| { kind: "fcrdns"; domains: readonly string[] } // reverse DNS, forward-confirmed
|
|
56
68
|
| { kind: "ip-ranges"; publishedAt?: string } // an address list the operator publishes
|
|
69
|
+
| { kind: "proof"; via: string } // something only you can check
|
|
57
70
|
| { kind: "none" }; // no published mechanism
|
|
58
71
|
```
|
|
59
72
|
|
|
@@ -62,6 +75,29 @@ them, and for those the claim is **unfalsifiable**. The library neither confirms
|
|
|
62
75
|
accuses — it records what the client said and lets the policy decide what a self-declared
|
|
63
76
|
identity is worth. See [verifying a crawler](verification.md).
|
|
64
77
|
|
|
78
|
+
**But you may be able to check what this library cannot.** Your CDN has often already
|
|
79
|
+
verified the crawler and says so in a header it adds; some bots now sign their requests;
|
|
80
|
+
you may hold ASN data. None of those belong inside a detection library — two need a
|
|
81
|
+
network dependency and the third needs a key it has no business fetching — so instead you
|
|
82
|
+
supply the answer:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
new BotHandler({
|
|
86
|
+
crawlerVerification: {
|
|
87
|
+
verifiers: {
|
|
88
|
+
// Whatever you can prove, however you prove it.
|
|
89
|
+
gptbot: (ctx) => (ctx.facts.headers["cf-verified-bot"] === "gptbot" ? "verified" : "unknown"),
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
A verifier returns `"verified"`, `"refuted"` or `"unknown"`, runs before the built-in check
|
|
96
|
+
for that signature, and a definite answer settles it — which also means no DNS lookup.
|
|
97
|
+
`"unknown"` falls through to whatever the library can do on its own, and so does a throw:
|
|
98
|
+
an unreachable key server must never read as an accusation, or an outage becomes a wave of
|
|
99
|
+
blocked crawlers.
|
|
100
|
+
|
|
65
101
|
## What a name is worth
|
|
66
102
|
|
|
67
103
|
A matched signature makes `self-identified` produce `certain` evidence, and this is the
|
|
@@ -29,6 +29,12 @@ User-Agent, the verdict, the score, the action and the rule that chose it. Filte
|
|
|
29
29
|
proven, suspected, human, guard stops, denied, mitigated or served; or type into the
|
|
30
30
|
search box.
|
|
31
31
|
|
|
32
|
+
The feed pages, fifty requests at a time. The newest page follows the stream; stepping
|
|
33
|
+
back holds the list still while you read it — a feed that renumbers itself under somebody
|
|
34
|
+
paging through it cannot be read — and says **held while you read** so the stillness is
|
|
35
|
+
not mistaken for quiet. Requests keep arriving and keep being counted; they are at the
|
|
36
|
+
front when you return to it.
|
|
37
|
+
|
|
32
38
|
The search takes fields, and negation, because the two cases people actually reach for
|
|
33
39
|
it are an address that also appears inside a User-Agent and a path that is a prefix of
|
|
34
40
|
ten others:
|
|
@@ -108,6 +114,17 @@ regularity (near zero is a metronome, which no person is), prior confirmations,
|
|
|
108
114
|
whether they hold clearance. **In feed** sends one to the live feed as an `actor:` filter,
|
|
109
115
|
which makes it a shareable URL like every other view.
|
|
110
116
|
|
|
117
|
+
This screen pages too, twenty-five at a time, busiest first. The registry holds far more
|
|
118
|
+
clients than the feed's ring holds requests, and paging is what reaches them: the feed
|
|
119
|
+
already shows you what is loudest, and the population behind it is the reason this screen
|
|
120
|
+
exists. Ranking something that is still moving means a client can shift between pages
|
|
121
|
+
while you read; the order is a snapshot of a live list, not a stable index.
|
|
122
|
+
|
|
123
|
+
The **Actors tracked** counter above the tab strip is the way in: it is a button, so
|
|
124
|
+
pressing it — or reaching it with the keyboard and pressing Enter — opens this screen.
|
|
125
|
+
Where the `registry` section is switched off there is no Actors screen to open, and the
|
|
126
|
+
counter stays an ordinary tile rather than offering to go somewhere that does not exist.
|
|
127
|
+
|
|
111
128
|
**Statistics** — a traffic timeline (1m/5m/15m/1h) split by outcome, which says how
|
|
112
129
|
much history the window actually holds rather than drawing a flat line through time it
|
|
113
130
|
never had; the assessment-latency histogram with mean, p95 and max; and the **score
|
|
@@ -383,6 +400,15 @@ capped at `maxEventsPerSecond` (100 by default, `0` to remove it). What is cappe
|
|
|
383
400
|
still see every request, and the feed says how many were not streamed. A thinned feed
|
|
384
401
|
must never look like a quiet one.
|
|
385
402
|
|
|
403
|
+
**And it does not stop at saying so.** Next to that count is **Load them**, which fetches
|
|
404
|
+
the ring and merges what is missing back into the feed, in the order it happened. Nothing
|
|
405
|
+
was ever lost — the cap and the lag guard both keep entries off the *stream* and leave the
|
|
406
|
+
ring alone — so this is the same data arriving by a different road. It is a button rather
|
|
407
|
+
than something automatic on purpose: entries are skipped exactly when the origin is
|
|
408
|
+
busiest, and a dashboard that answered every skip by re-fetching the whole ring would be a
|
|
409
|
+
load amplifier pointed at the process it is watching, which is what the cap exists to
|
|
410
|
+
prevent.
|
|
411
|
+
|
|
386
412
|
**A viewer that stops reading is not allowed to cost you memory.** A socket that has
|
|
387
413
|
stopped draining — a laptop that slept with the tab open, a phone in a tunnel, a proxy
|
|
388
414
|
that stopped reading — used to accumulate frames in this process, one queue per viewer,
|