@osqd/bothandlerjs 0.3.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.
Files changed (186) hide show
  1. package/CHANGELOG.md +516 -0
  2. package/LICENSE +102 -0
  3. package/README.md +308 -0
  4. package/bin/bothandlerjs.mjs +14 -0
  5. package/dist/actions/index.d.ts +22 -0
  6. package/dist/actions/types.d.ts +59 -0
  7. package/dist/adapters/fastify.d.ts +43 -0
  8. package/dist/adapters/fetch.d.ts +51 -0
  9. package/dist/adapters/index.cjs +723 -0
  10. package/dist/adapters/index.cjs.map +1 -0
  11. package/dist/adapters/index.d.ts +8 -0
  12. package/dist/adapters/index.js +692 -0
  13. package/dist/adapters/index.js.map +1 -0
  14. package/dist/adapters/koa.d.ts +27 -0
  15. package/dist/adapters/node.d.ts +29 -0
  16. package/dist/adapters/shared.d.ts +39 -0
  17. package/dist/audit.d.ts +172 -0
  18. package/dist/challenge/index.d.ts +176 -0
  19. package/dist/challenge/language.d.ts +57 -0
  20. package/dist/challenge/page.d.ts +42 -0
  21. package/dist/challenge/pow.d.ts +33 -0
  22. package/dist/challenge/token.d.ts +72 -0
  23. package/dist/cli.cjs +13388 -0
  24. package/dist/cli.cjs.map +1 -0
  25. package/dist/cli.d.ts +5 -0
  26. package/dist/cli.js +13358 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/client/index.cjs +139 -0
  29. package/dist/client/index.cjs.map +1 -0
  30. package/dist/client/index.d.ts +63 -0
  31. package/dist/client/index.js +112 -0
  32. package/dist/client/index.js.map +1 -0
  33. package/dist/config.d.ts +316 -0
  34. package/dist/core.d.ts +443 -0
  35. package/dist/corpus/adversarial.d.ts +2 -0
  36. package/dist/corpus/advertising-email.d.ts +2 -0
  37. package/dist/corpus/ai-crawlers.d.ts +2 -0
  38. package/dist/corpus/benign-bots.d.ts +2 -0
  39. package/dist/corpus/cdn-gateways.d.ts +2 -0
  40. package/dist/corpus/cookies.d.ts +49 -0
  41. package/dist/corpus/crawlers-regional.d.ts +2 -0
  42. package/dist/corpus/crawlers-vertical.d.ts +2 -0
  43. package/dist/corpus/headers.d.ts +403 -0
  44. package/dist/corpus/humans-apps.d.ts +2 -0
  45. package/dist/corpus/humans-browsers.d.ts +2 -0
  46. package/dist/corpus/humans.d.ts +2 -0
  47. package/dist/corpus/index.cjs +4196 -0
  48. package/dist/corpus/index.cjs.map +1 -0
  49. package/dist/corpus/index.d.ts +37 -0
  50. package/dist/corpus/index.js +4132 -0
  51. package/dist/corpus/index.js.map +1 -0
  52. package/dist/corpus/infrastructure.d.ts +17 -0
  53. package/dist/corpus/libraries-extended.d.ts +2 -0
  54. package/dist/corpus/ranges.d.ts +31 -0
  55. package/dist/corpus/reputation.d.ts +21 -0
  56. package/dist/corpus/runner.d.ts +130 -0
  57. package/dist/corpus/schema.d.ts +185 -0
  58. package/dist/corpus/tooling.d.ts +2 -0
  59. package/dist/corpus/unwanted.d.ts +2 -0
  60. package/dist/crawler-ranges.d.ts +113 -0
  61. package/dist/dashboard/client.generated.d.ts +7 -0
  62. package/dist/dashboard/feed.d.ts +186 -0
  63. package/dist/dashboard/index.d.ts +5 -0
  64. package/dist/dashboard/page.d.ts +51 -0
  65. package/dist/dashboard/parse-request.d.ts +28 -0
  66. package/dist/dashboard/preview.d.ts +45 -0
  67. package/dist/dashboard/server.d.ts +70 -0
  68. package/dist/dashboard/types.d.ts +714 -0
  69. package/dist/detectors/accept-signature.d.ts +6 -0
  70. package/dist/detectors/browsing-coherence.d.ts +44 -0
  71. package/dist/detectors/cadence.d.ts +28 -0
  72. package/dist/detectors/clearance.d.ts +25 -0
  73. package/dist/detectors/client-hints.d.ts +19 -0
  74. package/dist/detectors/client-signals.d.ts +27 -0
  75. package/dist/detectors/crawl-breadth.d.ts +28 -0
  76. package/dist/detectors/crawler-verification.d.ts +39 -0
  77. package/dist/detectors/fetch-metadata.d.ts +20 -0
  78. package/dist/detectors/header-integrity.d.ts +26 -0
  79. package/dist/detectors/header-order.d.ts +27 -0
  80. package/dist/detectors/identity-rotation.d.ts +34 -0
  81. package/dist/detectors/index.d.ts +56 -0
  82. package/dist/detectors/ip-intelligence.d.ts +30 -0
  83. package/dist/detectors/known-bots.d.ts +94 -0
  84. package/dist/detectors/probe-signature.d.ts +50 -0
  85. package/dist/detectors/rate-anomaly.d.ts +36 -0
  86. package/dist/detectors/self-identified.d.ts +33 -0
  87. package/dist/detectors/session-integrity.d.ts +24 -0
  88. package/dist/detectors/tls-fingerprint.d.ts +34 -0
  89. package/dist/detectors/trap.d.ts +98 -0
  90. package/dist/detectors/types.d.ts +70 -0
  91. package/dist/detectors/ua-coherence.d.ts +32 -0
  92. package/dist/evidence.d.ts +45 -0
  93. package/dist/facts.d.ts +46 -0
  94. package/dist/index.cjs +9600 -0
  95. package/dist/index.cjs.map +1 -0
  96. package/dist/index.d.ts +63 -0
  97. package/dist/index.js +9444 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/internal/async.d.ts +16 -0
  100. package/dist/internal/clock.d.ts +17 -0
  101. package/dist/internal/crypto.d.ts +26 -0
  102. package/dist/internal/dns.d.ts +100 -0
  103. package/dist/internal/emitter.d.ts +18 -0
  104. package/dist/internal/http.d.ts +22 -0
  105. package/dist/internal/ip.d.ts +73 -0
  106. package/dist/internal/lru.d.ts +34 -0
  107. package/dist/internal/matcher.d.ts +41 -0
  108. package/dist/internal/pattern.d.ts +14 -0
  109. package/dist/internal/ua.d.ts +92 -0
  110. package/dist/metrics.d.ts +120 -0
  111. package/dist/notify/hub.d.ts +70 -0
  112. package/dist/notify/index.d.ts +7 -0
  113. package/dist/notify/redact.d.ts +48 -0
  114. package/dist/notify/sinks.d.ts +62 -0
  115. package/dist/notify/types.d.ts +56 -0
  116. package/dist/policy/index.d.ts +7 -0
  117. package/dist/policy/match.d.ts +20 -0
  118. package/dist/policy/policy.d.ts +123 -0
  119. package/dist/policy/presets.d.ts +161 -0
  120. package/dist/policy/types.d.ts +155 -0
  121. package/dist/robots.d.ts +83 -0
  122. package/dist/state.d.ts +161 -0
  123. package/dist/stores/index.d.ts +5 -0
  124. package/dist/stores/memory.d.ts +33 -0
  125. package/dist/stores/redis.d.ts +59 -0
  126. package/dist/stores/types.d.ts +37 -0
  127. package/dist/types.d.ts +250 -0
  128. package/docs/challenge/index.md +152 -0
  129. package/docs/challenge/localisation.md +105 -0
  130. package/docs/concepts/actors.md +113 -0
  131. package/docs/concepts/evidence.md +133 -0
  132. package/docs/concepts/the-guard.md +121 -0
  133. package/docs/concepts/threat-model.md +120 -0
  134. package/docs/concepts/verdicts.md +108 -0
  135. package/docs/course/01-first-assessment.md +178 -0
  136. package/docs/course/02-proof-and-suspicion.md +164 -0
  137. package/docs/course/03-verdicts-and-scores.md +160 -0
  138. package/docs/course/04-the-guard.md +200 -0
  139. package/docs/course/05-detectors.md +178 -0
  140. package/docs/course/06-identity.md +161 -0
  141. package/docs/course/07-actors.md +178 -0
  142. package/docs/course/08-traps.md +183 -0
  143. package/docs/course/09-rules.md +224 -0
  144. package/docs/course/10-actions-and-presets.md +216 -0
  145. package/docs/course/11-the-challenge.md +227 -0
  146. package/docs/course/12-going-live.md +211 -0
  147. package/docs/course/13-operating-it.md +238 -0
  148. package/docs/course/14-scaling.md +195 -0
  149. package/docs/course/15-extending.md +259 -0
  150. package/docs/course/16-proving-it.md +266 -0
  151. package/docs/course/index.md +112 -0
  152. package/docs/design/decisions.md +216 -0
  153. package/docs/detection/client-signals.md +96 -0
  154. package/docs/detection/detectors.md +376 -0
  155. package/docs/detection/index.md +134 -0
  156. package/docs/detection/signatures.md +111 -0
  157. package/docs/detection/verification.md +123 -0
  158. package/docs/detection/writing-a-detector.md +145 -0
  159. package/docs/index.md +100 -0
  160. package/docs/integration/adapters.md +138 -0
  161. package/docs/integration/client-ip.md +114 -0
  162. package/docs/integration/index.md +55 -0
  163. package/docs/integration/stores.md +76 -0
  164. package/docs/operations/audit.md +116 -0
  165. package/docs/operations/dashboard.md +725 -0
  166. package/docs/operations/index.md +101 -0
  167. package/docs/operations/metrics.md +74 -0
  168. package/docs/operations/notifications.md +72 -0
  169. package/docs/operations/runtime-changes.md +118 -0
  170. package/docs/policy/actions.md +196 -0
  171. package/docs/policy/index.md +84 -0
  172. package/docs/policy/presets.md +191 -0
  173. package/docs/policy/robots.md +128 -0
  174. package/docs/policy/rules.md +134 -0
  175. package/docs/reference/api.md +126 -0
  176. package/docs/reference/configuration.md +175 -0
  177. package/docs/start/choosing-a-policy.md +127 -0
  178. package/docs/start/first-integration.md +127 -0
  179. package/docs/start/installation.md +63 -0
  180. package/docs/start/upgrading.md +72 -0
  181. package/docs/testing/cli.md +119 -0
  182. package/docs/testing/corpus.md +129 -0
  183. package/docs/testing/index.md +61 -0
  184. package/docs/testing/replay.md +102 -0
  185. package/docs/testing/try-it.md +189 -0
  186. package/package.json +145 -0
@@ -0,0 +1,316 @@
1
+ import { IpRangeSet } from "./internal/ip.js";
2
+ import type { PresetName } from "./policy/presets.js";
3
+ import type { Clock } from "./internal/clock.js";
4
+ import type { DnsResolver } from "./internal/dns.js";
5
+ import type { Detector } from "./detectors/types.js";
6
+ import type { BotSignature } from "./detectors/known-bots.js";
7
+ import type { ActionParams, FalsePositivePolicy, Rule } from "./policy/types.js";
8
+ import type { CustomHandler } from "./actions/types.js";
9
+ import type { BotHandlerStore } from "./stores/types.js";
10
+ import type { AuditOptions, TrafficAnomaly } from "./audit.js";
11
+ import type { Decision } from "./policy/types.js";
12
+ import type { MetricsOptions } from "./metrics.js";
13
+ import type { NotificationOptions } from "./notify/hub.js";
14
+ import type { ChallengeOptions } from "./challenge/index.js";
15
+ import type { Assessment, RequestFacts } from "./types.js";
16
+ /**
17
+ * How the client address is recovered from behind a proxy.
18
+ *
19
+ * This is the highest-consequence configuration in the library and the easiest to get
20
+ * wrong, so it has no convenient default: `trustProxy` is off, and with it off the
21
+ * socket address is used and `X-Forwarded-For` is ignored entirely.
22
+ *
23
+ * The reason is that `X-Forwarded-For` is a *client-supplied header*. If you trust it
24
+ * without knowing how many proxies actually sit in front of you, anyone can prepend a
25
+ * fake hop and choose the address you rate-limit, allowlist and block on. The
26
+ * failure is silent, and it turns every per-actor mechanism here into an attacker
27
+ * input. Configure `trustedProxies` if you can — it is the only variant that does not
28
+ * depend on getting a number right.
29
+ */
30
+ export interface ProxyConfig {
31
+ /** Read the forwarded header at all. Default false. */
32
+ trustProxy?: boolean;
33
+ /**
34
+ * CIDRs of proxies you operate. The header is walked from the right, discarding
35
+ * addresses inside these ranges, and the first address outside them is the client.
36
+ * Robust against an extra hop appearing, and the recommended setting.
37
+ *
38
+ * The connecting peer counts as the first hop and is checked the same way. A
39
+ * request that arrives from outside these ranges did not come through your proxies,
40
+ * so its forwarded header is not evidence of anything and the socket address is
41
+ * used instead — which is what makes this variant safe on a server that is
42
+ * reachable both through the load balancer and directly.
43
+ */
44
+ trustedProxies?: readonly string[];
45
+ /**
46
+ * How many proxies sit in front of this process, when you cannot enumerate them.
47
+ * The Nth address from the right is taken. Default 1.
48
+ *
49
+ * If the chain turns out to be shorter than this, the socket address is used
50
+ * instead: a short chain means the request did not come through the expected
51
+ * topology, and every entry in it is then client-controlled. Prefer
52
+ * `trustedProxies`, which does not depend on getting a count right.
53
+ */
54
+ hops?: number;
55
+ /** Header carrying the chain. Default `"x-forwarded-for"`. */
56
+ header?: string;
57
+ }
58
+ export interface BotHandlerConfig {
59
+ /** Replaces the built-in detector set entirely. */
60
+ detectors?: readonly Detector[];
61
+ /** Appended to the built-in set. Ignored when `detectors` is given. */
62
+ extraDetectors?: readonly Detector[];
63
+ /** A named starting policy. Combined with `rules`, which are evaluated first. */
64
+ preset?: PresetName;
65
+ /** Your own rules, evaluated before any preset's. */
66
+ rules?: readonly Rule[];
67
+ /** Action when nothing matches. Default `allow`. */
68
+ defaultAction?: import("./policy/types.js").ActionName;
69
+ defaultActionParams?: ActionParams;
70
+ /** How strictly terminal actions are gated. Default `strict`. See {@link FalsePositivePolicy}. */
71
+ falsePositivePolicy?: FalsePositivePolicy;
72
+ /** Substituted when the guard blocks a terminal action. Default `challenge`. */
73
+ fallbackAction?: import("./policy/types.js").ActionName;
74
+ /** Score needed for a terminal action under `balanced`. Default 85. */
75
+ terminalScoreThreshold?: number;
76
+ /** Score at or above which an unproven request is `suspected-bot`. Default 60. */
77
+ suspectThreshold?: number;
78
+ /**
79
+ * Reject `certain` evidence that carries no `deterministicBasis`. Defaults to true
80
+ * unless `NODE_ENV` is `production`, where it downgrades to a warning so a
81
+ * third-party detector cannot take a live site down.
82
+ */
83
+ strictEvidence?: boolean;
84
+ /** Addresses and CIDRs exempt from detection entirely. Your monitors, your office, your CI. */
85
+ allowlist?: readonly string[];
86
+ /** Addresses and CIDRs treated as proven automation. An explicit local decision. */
87
+ denylist?: readonly string[];
88
+ /** Hosting-provider ranges. Supply your own; none ships with the library. */
89
+ datacenterRanges?: readonly string[];
90
+ /** Published crawler ranges, keyed by signature id, e.g. `{ gptbot: ["1.2.3.0/24"] }`. */
91
+ crawlerRanges?: Readonly<Record<string, readonly string[]>>;
92
+ proxy?: ProxyConfig;
93
+ /**
94
+ * Derives the key an actor is tracked under. Defaults to the client address.
95
+ *
96
+ * Worth replacing. An address is a poor identity — shared by a whole office,
97
+ * changed by a phone every few minutes — and every behavioural detector is only as
98
+ * good as this function. A session id, an authenticated user id, or an address
99
+ * combined with a TLS fingerprint all make the same detectors much sharper.
100
+ */
101
+ actorKey?: (facts: RequestFacts) => string;
102
+ /** Paths detection skips entirely: health checks, your own polling endpoints, static assets. */
103
+ ignorePaths?: readonly (string | RegExp)[];
104
+ /**
105
+ * Lets your application declare a request human — an authenticated session, a
106
+ * completed payment, whatever bar you set. Produces `certain` human evidence, the
107
+ * only conclusive human signal available, because it comes from you and not from
108
+ * the client.
109
+ */
110
+ isHuman?: (facts: RequestFacts) => boolean;
111
+ /** Enables the challenge action. Without it, rules asking for one degrade to `tag`. */
112
+ challenge?: Omit<ChallengeOptions, "store" | "clock">;
113
+ store?: BotHandlerStore;
114
+ /**
115
+ * Share `priorConfirmations` between replicas through the store. Default false.
116
+ *
117
+ * Behavioural state is process-local by design — see `state.ts` — because a round
118
+ * trip per request would buy accuracy for signals that are only ever allowed to raise
119
+ * suspicion. A **confirmation is not one of those.** `confirmed-bot` is a proven
120
+ * verdict: something declared itself, forged an identity, or walked into a trap. That
121
+ * is a fact about the client rather than a judgement about it, and behind eight
122
+ * replicas a fact established on one of them is unknown to the other seven — so a
123
+ * rule reading `minPriorConfirmations: 1` fires roughly an eighth as often as it
124
+ * reads.
125
+ *
126
+ * So proof travels and suspicion stays home. The cost is one store read the **first
127
+ * time each instance sees an actor** — not one per request — and it is never awaited:
128
+ * the request that triggered it is judged with whatever arrived before the detectors
129
+ * finished, and every request after it has the number. A store outage means the count
130
+ * falls back to what this process saw itself, which is where it was before.
131
+ *
132
+ * Needs a shared `store`. With the default in-memory one there is nothing to share
133
+ * with, and setting this does nothing at all.
134
+ */
135
+ shareConfirmations?: boolean;
136
+ notifications?: NotificationOptions;
137
+ handlers?: readonly CustomHandler[];
138
+ /** Replaces the built-in signature database. */
139
+ signatures?: readonly BotSignature[];
140
+ /** Appended to the built-in database. */
141
+ extraSignatures?: readonly BotSignature[];
142
+ /** Budget for each `io` detector, ms. Default 300. Exceeding it drops that detector, not the request. */
143
+ detectorTimeoutMs?: number;
144
+ /** How long an idle actor is remembered, ms. Default 900000. */
145
+ actorWindowMs?: number;
146
+ /**
147
+ * Maximum actors tracked concurrently. Default 20000.
148
+ *
149
+ * This is a memory budget: see the cap arithmetic in `state.ts`. Past the limit the
150
+ * least recently seen actor is evicted, never one still sending traffic.
151
+ */
152
+ maxActors?: number;
153
+ /**
154
+ * Put verdict headers on the *response*. Default false.
155
+ *
156
+ * Leave it off in production. An `X-Bot-Score` in the response is a live feedback
157
+ * signal for anyone tuning a scraper against you — they change one header, watch
158
+ * the number fall, and iterate. Request-side tagging gives your application the
159
+ * same information and tells the client nothing.
160
+ */
161
+ exposeVerdictHeaders?: boolean;
162
+ /**
163
+ * Collect counters. Default true — see {@link BotHandler.metrics}. Switching it off
164
+ * saves a few integer increments per request and costs you the ability to see what
165
+ * your policy is doing.
166
+ */
167
+ metrics?: boolean | MetricsOptions;
168
+ /**
169
+ * Watches the *shape* of your traffic and raises {@link BotHandlerConfig.onAnomaly}
170
+ * when it changes. `false` switches it off; the default is on with the shipped
171
+ * checks. See {@link AuditOptions}.
172
+ */
173
+ audit?: AuditOptions | false;
174
+ resolver?: DnsResolver;
175
+ clock?: Clock;
176
+ /** Every assessment, including the ones that concluded nothing. The firehose. */
177
+ onAssessment?: (assessment: Assessment) => void;
178
+ /** Every decision, paired with the assessment behind it. */
179
+ onDecision?: (event: {
180
+ assessment: Assessment;
181
+ decision: Decision;
182
+ }) => void;
183
+ /** A request that was actually denied: `block`, `drop` or `redirect`. */
184
+ onDenial?: (event: {
185
+ assessment: Assessment;
186
+ decision: Decision;
187
+ }) => void;
188
+ /**
189
+ * The safety guard replaced a terminal action with something recoverable.
190
+ *
191
+ * The most useful hook here, and the one worth paging on a rise in: it means your
192
+ * rules are asking to deny requests the evidence does not prove.
193
+ */
194
+ onDowngrade?: (event: {
195
+ assessment: Assessment;
196
+ decision: Decision;
197
+ }) => void;
198
+ /** A challenge was issued, solved or rejected. */
199
+ onChallenge?: (event: {
200
+ phase: "issued" | "solved" | "rejected";
201
+ actorKey?: string | undefined;
202
+ }) => void;
203
+ /**
204
+ * A detector threw or timed out. Operational: usually a resolver or a store, and a
205
+ * rising rate means detection is degraded rather than that traffic changed.
206
+ */
207
+ onDetectorFailure?: (event: {
208
+ detector: string;
209
+ reason: string;
210
+ message: string;
211
+ requestId: string;
212
+ }) => void;
213
+ /** The rule set was replaced at runtime. Your audit trail for {@link BotHandler.updatePolicy}. */
214
+ onPolicyChange?: (event: {
215
+ rules: readonly string[];
216
+ warnings: readonly string[];
217
+ by?: string | undefined;
218
+ }) => void;
219
+ /**
220
+ * The guard settings changed at runtime — `falsePositivePolicy`, `fallbackAction`,
221
+ * `defaultAction`, `terminalScoreThreshold` or `suspectThreshold`.
222
+ *
223
+ * Worth its own hook rather than folding into `onPolicyChange`: this is the change
224
+ * that decides whether an unproven verdict can deny anybody, and it is the one an
225
+ * audit trail wants named. Both the before and the after travel with it.
226
+ */
227
+ onGuardChange?: (event: {
228
+ before: import("./policy/policy.js").GuardSettings & {
229
+ suspectThreshold: number;
230
+ };
231
+ after: import("./policy/policy.js").GuardSettings & {
232
+ suspectThreshold: number;
233
+ };
234
+ by?: string | undefined;
235
+ }) => void;
236
+ /**
237
+ * A range set was replaced at runtime.
238
+ *
239
+ * The allowlist is the one worth watching: an address on it is not judged leniently,
240
+ * it is not judged at all, so a change to it changes what your bot handling can see.
241
+ */
242
+ onRangeChange?: (event: {
243
+ name: string;
244
+ size: number;
245
+ entries: readonly string[];
246
+ by?: string | undefined;
247
+ }) => void;
248
+ /** An actor's behavioural memory was forgotten, or the actor was cleared as human. */
249
+ onActorChange?: (event: {
250
+ key: string;
251
+ action: "forget" | "clear";
252
+ until?: number | undefined;
253
+ by?: string | undefined;
254
+ }) => void;
255
+ /** The audit noticed the traffic change shape. See {@link AuditOptions}. */
256
+ onAnomaly?: (anomaly: TrafficAnomaly) => void;
257
+ /** Called when a detector, sink or store fails. Wire it to your logs. */
258
+ onError?: (error: unknown, context: {
259
+ source: string;
260
+ }) => void;
261
+ /** Called on a misconfiguration noticed at runtime. Wire it to your logs. */
262
+ onWarning?: (message: string) => void;
263
+ }
264
+ export interface ResolvedConfig {
265
+ detectors: Detector[];
266
+ rules: Rule[];
267
+ ranges: Map<string, IpRangeSet>;
268
+ signatures: readonly BotSignature[];
269
+ proxy: Required<Omit<ProxyConfig, "trustedProxies">> & {
270
+ trustedProxies: IpRangeSet | undefined;
271
+ };
272
+ actorKey: (facts: RequestFacts) => string;
273
+ ignorePaths: readonly (string | RegExp)[];
274
+ suspectThreshold: number;
275
+ strictEvidence: boolean;
276
+ detectorTimeoutMs: number;
277
+ actorWindowMs: number;
278
+ maxActors: number;
279
+ exposeVerdictHeaders: boolean;
280
+ clock: Clock;
281
+ onError: (error: unknown, context: {
282
+ source: string;
283
+ }) => void;
284
+ onWarning: (message: string) => void;
285
+ /** Problems found while resolving. Fatal ones throw; these are advisory. */
286
+ warnings: string[];
287
+ }
288
+ /**
289
+ * Checks a rule list for the mistakes that are silent at runtime.
290
+ *
291
+ * Shared with {@link BotHandler.updatePolicy}, because rules can now be replaced while
292
+ * the process runs and a list swapped in from a dashboard deserves the same reading
293
+ * as one written in a config file. Structural problems throw; problems that merely
294
+ * make a rule useless are returned as warnings, since a policy that refuses to load
295
+ * over a dead rule is worse than one that says so.
296
+ */
297
+ export declare function validateRules(rules: readonly Rule[]): string[];
298
+ export declare class ConfigError extends Error {
299
+ readonly name = "ConfigError";
300
+ }
301
+ /**
302
+ * Validates and normalises configuration.
303
+ *
304
+ * Invalid input throws here, at construction, rather than degrading quietly at
305
+ * request time. A mistyped CIDR that silently matches nothing is a security control
306
+ * that looks configured and is not — the worst possible state to be in, because
307
+ * everything appears to be working.
308
+ */
309
+ export declare function resolveConfig(config?: BotHandlerConfig): ResolvedConfig;
310
+ /**
311
+ * Recovers the client address from the socket address and the request headers.
312
+ *
313
+ * Exported because getting this right matters more than almost anything else here,
314
+ * and you may want to call it directly or test it in isolation.
315
+ */
316
+ export declare function resolveClientIp(socketAddress: string | undefined, headers: Record<string, string | undefined>, proxy: ResolvedConfig["proxy"]): string;