devn-bot-detector-server 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/memory.ts","../src/rate-limit.ts","../src/risk/engine.ts","../src/token.ts","../src/BotDetectionServer.ts","../src/adapters/redis.ts","../src/middleware/index.ts"],"names":["randomBytes","clamp","createHmac","timingSafeEqual","DEFAULT_SESSION_DURATION_MS","mergeRiskThresholds","DEFAULT_RISK_TOKEN_DURATION_MS","MAX_TELEMETRY_BYTES","normalizeHostname","hostnameAllowed","extractHostnameFromOrigin","validateCreateSessionRequest","BotDetectorError","ipMatches","validateAnalyzeRequest","riskToken","expiresAt","decisionFromScore","riskLevelFromScore","count","allowed","SITE_KEY_HEADER"],"mappings":"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","file":"index.cjs","sourcesContent":["import { randomBytes } from 'node:crypto'\nimport type { SessionData, SiteInfo } from 'devn-bot-detector-core'\nimport type { SessionStore, SiteResolver, TokenRevocationStore } from './interfaces'\n\nexport function secureId(prefix: string, bytes = 16): string {\n return `${prefix}_${randomBytes(bytes).toString('hex')}`\n}\n\n/** In-memory site resolver for development/testing only. */\nexport class MemorySiteResolver implements SiteResolver {\n private readonly sites = new Map<string, SiteInfo>()\n\n constructor(entries: Array<{ siteKey: string; site: SiteInfo }> = []) {\n for (const entry of entries) {\n this.sites.set(entry.siteKey, entry.site)\n }\n }\n\n set(siteKey: string, site: SiteInfo): void {\n this.sites.set(siteKey, site)\n }\n\n async resolveSite(siteKey: string): Promise<SiteInfo | null> {\n return this.sites.get(siteKey) ?? null\n }\n}\n\n/** In-memory session store for development/testing only. */\nexport class MemorySessionStore implements SessionStore {\n private readonly sessions = new Map<string, SessionData>()\n\n async create(session: SessionData): Promise<void> {\n this.sessions.set(session.sessionId, {\n ...session,\n usedNonces: [...session.usedNonces],\n })\n }\n\n async get(sessionId: string): Promise<SessionData | null> {\n const s = this.sessions.get(sessionId)\n if (!s) return null\n return {\n ...s,\n usedNonces: [...s.usedNonces],\n }\n }\n\n async update(session: SessionData): Promise<void> {\n this.sessions.set(session.sessionId, {\n ...session,\n usedNonces: [...session.usedNonces],\n })\n }\n\n async invalidate(sessionId: string): Promise<void> {\n this.sessions.delete(sessionId)\n }\n}\n\n/** In-memory token revocation store for single-instance / dev usage. */\nexport class MemoryTokenRevocationStore implements TokenRevocationStore {\n private readonly revoked = new Map<string, number>()\n\n isRevoked(tokenId: string): boolean {\n const expiresAt = this.revoked.get(tokenId)\n if (!expiresAt) return false\n if (Date.now() >= expiresAt) {\n this.revoked.delete(tokenId)\n return false\n }\n return true\n }\n\n revoke(tokenId: string, ttlMs: number): void {\n this.revoked.set(tokenId, Date.now() + Math.max(1000, ttlMs))\n }\n}\n\n","import type { RateLimitResult } from 'devn-bot-detector-core'\nimport type { RateLimiter } from './interfaces'\n\ninterface Bucket {\n count: number\n resetAt: number\n}\n\n/** Simple in-memory sliding-window rate limiter (dev/default). */\nexport class MemoryRateLimiter implements RateLimiter {\n private readonly buckets = new Map<string, Bucket>()\n\n check(key: string, limit: number, windowMs: number): RateLimitResult {\n const now = Date.now()\n let bucket = this.buckets.get(key)\n if (!bucket || now >= bucket.resetAt) {\n bucket = { count: 0, resetAt: now + windowMs }\n this.buckets.set(key, bucket)\n }\n\n if (bucket.count >= limit) {\n return {\n allowed: false,\n remaining: 0,\n retryAfter: Math.max(1, Math.ceil((bucket.resetAt - now) / 1000)),\n }\n }\n\n bucket.count += 1\n return {\n allowed: true,\n remaining: Math.max(0, limit - bucket.count),\n }\n }\n\n /** Best-effort cleanup for long-running processes. */\n prune(): void {\n const now = Date.now()\n for (const [key, bucket] of this.buckets) {\n if (now >= bucket.resetAt) this.buckets.delete(key)\n }\n }\n}\n","import type {\n RequestContext,\n RiskDecision,\n RiskLevel,\n RiskReason,\n Telemetry,\n} from 'devn-bot-detector-core'\nimport { clamp } from 'devn-bot-detector-core'\n\nexport interface RiskInput {\n readonly telemetry: Telemetry\n readonly request: RequestContext\n readonly sessionAgeMs: number\n readonly sequence: number\n readonly action?: string\n readonly metadata?: Record<string, unknown>\n}\n\nexport interface RiskEvaluation {\n readonly score: number\n readonly confidence: number\n readonly riskLevel: RiskLevel\n readonly decision?: RiskDecision\n readonly internalReasons: RiskReason[]\n /** Optional additive risk from custom scoring (0–100 contribution scale). */\n readonly risk?: number\n}\n\nexport type Detector = (input: RiskInput) => RiskReason[]\n\n/** Automation indicators — never decisive alone. */\nexport const AutomationDetector: Detector = (input) => {\n const reasons: RiskReason[] = []\n const browser = input.telemetry.browser\n if (!browser) return reasons\n if (browser.webdriver) {\n reasons.push({ code: 'WEBDRIVER', weight: 25, detail: 'navigator.webdriver true' })\n }\n const ua = browser.userAgent.toLowerCase()\n if (ua.includes('headless') || ua.includes('phantomjs')) {\n reasons.push({ code: 'HEADLESS_UA', weight: 20 })\n }\n return reasons\n}\n\nexport const HeadlessAdvancedDetector: Detector = (input) => {\n const reasons: RiskReason[] = []\n const browser = input.telemetry.browser\n if (!browser) return reasons\n\n const renderer = (browser.webglRenderer || '').toLowerCase()\n if (\n renderer.includes('swiftshader') ||\n renderer.includes('llvmpipe') ||\n renderer.includes('softpipe') ||\n renderer.includes('mesa offscreen')\n ) {\n reasons.push({\n code: 'SOFTWARE_WEBGL',\n weight: 22,\n detail: 'Software WebGL renderer detected (virtualized/headless)',\n })\n }\n\n if (browser.userAgent.includes('Chrome') && browser.hasChrome === false) {\n reasons.push({\n code: 'MISSING_CHROME_OBJ',\n weight: 18,\n detail: 'UA reports Chrome but window.chrome is absent',\n })\n }\n\n const ua = browser.userAgent.toLowerCase()\n const isDesktop =\n !ua.includes('mobile') &&\n !ua.includes('android') &&\n !ua.includes('iphone') &&\n !ua.includes('ipad') &&\n !ua.includes('jsdom')\n if (isDesktop && browser.pluginsCount === 0) {\n reasons.push({ code: 'ZERO_DESKTOP_PLUGINS', weight: 12 })\n }\n\n return reasons\n}\n\nexport const EntropyDetector: Detector = (input) => {\n const reasons: RiskReason[] = []\n const pointer = input.telemetry.pointer\n if (\n pointer &&\n pointer.eventCount >= 25 &&\n pointer.movementDistance > 350 &&\n pointer.directionChanges === 0\n ) {\n reasons.push({\n code: 'LINEAR_POINTER_PATH',\n weight: 20,\n detail: 'Unnatural linear pointer trajectory without direction variance',\n })\n }\n return reasons\n}\n\nexport const ActionRiskDetector: Detector = (input) => {\n const reasons: RiskReason[] = []\n if (!input.action) return reasons\n\n const action = input.action.toLowerCase()\n const sensitivePatterns = ['login', 'checkout', 'pay', 'transfer', 'signup', 'reset_password']\n const isSensitive = sensitivePatterns.some((p) => action.includes(p))\n\n if (isSensitive) {\n const interaction = input.telemetry.interaction\n if (interaction && interaction.interactionCount === 0) {\n reasons.push({\n code: 'SENSITIVE_ACTION_NO_INTERACTION',\n weight: 25,\n detail: `Sensitive action \"${input.action}\" attempted with 0 prior interactions`,\n })\n } else if (\n interaction &&\n interaction.timeToFirstInteraction != null &&\n interaction.timeToFirstInteraction < 150 &&\n interaction.interactionCount > 0\n ) {\n reasons.push({\n code: 'SENSITIVE_ACTION_SUB_HUMAN_SPEED',\n weight: 20,\n detail: `Sensitive action \"${input.action}\" executed under 150ms`,\n })\n }\n }\n\n return reasons\n}\n\nexport const VelocityDetector: Detector = (input) => {\n const reasons: RiskReason[] = []\n if (input.sequence > 40 && input.sessionAgeMs < 10_000) {\n reasons.push({ code: 'HIGH_SEQUENCE_VELOCITY', weight: 30 })\n }\n const pointer = input.telemetry.pointer\n if (pointer && pointer.eventCount > 800 && pointer.activeDuration < 2000) {\n reasons.push({ code: 'POINTER_VELOCITY', weight: 20 })\n }\n return reasons\n}\n\nexport const TimingDetector: Detector = (input) => {\n const reasons: RiskReason[] = []\n const kb = input.telemetry.keyboard\n if (kb && kb.keydownCount >= 12 && kb.timingVariance != null && kb.timingVariance < 1) {\n reasons.push({ code: 'KEYBOARD_TOO_UNIFORM', weight: 18 })\n }\n if (\n kb &&\n kb.averageInterval != null &&\n kb.averageInterval < 15 &&\n kb.keydownCount >= 10\n ) {\n reasons.push({ code: 'KEYBOARD_TOO_FAST', weight: 15 })\n }\n const interaction = input.telemetry.interaction\n if (\n interaction &&\n interaction.timeToFirstInteraction != null &&\n interaction.timeToFirstInteraction < 20 &&\n interaction.interactionCount > 0\n ) {\n reasons.push({ code: 'IMPOSSIBLE_TTFI', weight: 12 })\n }\n return reasons\n}\n\nexport const InteractionDetector: Detector = (input) => {\n const reasons: RiskReason[] = []\n const interaction = input.telemetry.interaction\n if (!interaction) return reasons\n\n // Zero interaction is mildly suspicious for sensitive ops — not decisive.\n if (interaction.interactionCount === 0) {\n reasons.push({ code: 'ZERO_INTERACTION', weight: 10 })\n }\n\n // Positive / human-lowering signals encoded as negative weights.\n if (\n interaction.timeToFirstInteraction != null &&\n interaction.timeToFirstInteraction > 200 &&\n interaction.timeToFirstInteraction < 120_000\n ) {\n reasons.push({ code: 'NATURAL_TTFI', weight: -8 })\n }\n if (interaction.interactionCount >= 3) {\n reasons.push({ code: 'MIXED_INTERACTION', weight: -6 })\n }\n return reasons\n}\n\nexport const BrowserConsistencyDetector: Detector = (input) => {\n const reasons: RiskReason[] = []\n const browser = input.telemetry.browser\n if (!browser) return reasons\n\n if (browser.languagesLength === 0) {\n reasons.push({ code: 'NO_LANGUAGES', weight: 8 })\n }\n if (browser.screenWidth <= 0 || browser.screenHeight <= 0) {\n reasons.push({ code: 'INVALID_SCREEN', weight: 10 })\n }\n\n // Do NOT penalize mobile, privacy browsers, or missing hardwareConcurrency alone.\n if (input.request.userAgent && browser.userAgent) {\n const reqUa = input.request.userAgent.slice(0, 64)\n const telUa = browser.userAgent.slice(0, 64)\n if (reqUa && telUa && reqUa !== telUa) {\n reasons.push({ code: 'UA_MISMATCH', weight: 15 })\n }\n }\n return reasons\n}\n\nexport const SessionDetector: Detector = (input) => {\n const reasons: RiskReason[] = []\n if (input.sessionAgeMs < 50 && input.sequence > 1) {\n reasons.push({ code: 'SESSION_TOO_YOUNG', weight: 12 })\n }\n const pointer = input.telemetry.pointer\n if (pointer && pointer.eventCount >= 20 && pointer.directionChanges === 0) {\n reasons.push({ code: 'POINTER_NO_DIRECTION_CHANGE', weight: 10 })\n }\n if (pointer && pointer.movementDistance > 0 && pointer.directionChanges > 5) {\n reasons.push({ code: 'NATURAL_POINTER', weight: -8 })\n }\n const scroll = input.telemetry.scroll\n if (scroll && scroll.scrollCount > 0) {\n reasons.push({ code: 'HAS_SCROLL', weight: -4 })\n }\n const focus = input.telemetry.focus\n if (focus && (focus.focusCount > 0 || focus.blurCount > 0)) {\n reasons.push({ code: 'HAS_FOCUS_CHANGES', weight: -3 })\n }\n return reasons\n}\n\nexport const DEFAULT_DETECTORS: Detector[] = [\n AutomationDetector,\n HeadlessAdvancedDetector,\n EntropyDetector,\n ActionRiskDetector,\n VelocityDetector,\n TimingDetector,\n InteractionDetector,\n BrowserConsistencyDetector,\n SessionDetector,\n]\n\nexport interface RiskEngineOptions {\n readonly detectors?: Detector[]\n readonly detectorWeights?: Record<string, number>\n}\n\nexport class RiskEngine {\n private readonly detectors: Detector[]\n private readonly weights: Record<string, number>\n\n constructor(options?: Detector[] | RiskEngineOptions) {\n if (Array.isArray(options)) {\n this.detectors = options\n this.weights = {}\n } else {\n this.detectors = options?.detectors ?? DEFAULT_DETECTORS\n this.weights = options?.detectorWeights ?? {}\n }\n }\n\n evaluate(input: RiskInput): RiskEvaluation {\n const internalReasons: RiskReason[] = []\n for (const detector of this.detectors) {\n for (const reason of detector(input)) {\n const configuredWeight = this.weights[reason.code]\n const weight = configuredWeight !== undefined ? configuredWeight : reason.weight\n if (weight !== 0) {\n internalReasons.push({ ...reason, weight })\n }\n }\n }\n\n let riskScore = 20 // base uncertainty\n for (const reason of internalReasons) {\n riskScore += reason.weight\n }\n riskScore = clamp(riskScore, 0, 100)\n const score = clamp(100 - riskScore, 0, 100)\n\n const confidence = clamp(0.4 + Math.abs(score - 50) / 100, 0.4, 0.95)\n const riskLevel: RiskLevel = score >= 70 ? 'low' : score >= 40 ? 'medium' : 'high'\n\n return {\n score,\n confidence: Number(confidence.toFixed(2)),\n riskLevel,\n internalReasons,\n }\n }\n}\n\n","import { createHmac, timingSafeEqual } from 'node:crypto'\nimport type { RiskDecision, RiskTokenPayload, VerifyRiskTokenOptions } from 'devn-bot-detector-core'\n\nfunction b64url(input: Buffer | string): string {\n const buf = typeof input === 'string' ? Buffer.from(input, 'utf8') : input\n return buf\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/g, '')\n}\n\nfunction fromB64url(input: string): Buffer {\n const padded = input.replace(/-/g, '+').replace(/_/g, '/')\n const pad = padded.length % 4 === 0 ? '' : '='.repeat(4 - (padded.length % 4))\n return Buffer.from(padded + pad, 'base64')\n}\n\nexport class TokenManager {\n constructor(private readonly secret: string) {\n if (!secret || secret.length < 16) {\n throw new Error('TokenManager requires a secret of at least 16 characters')\n }\n }\n\n createRiskToken(payload: RiskTokenPayload): string {\n const body = b64url(JSON.stringify(payload))\n const sig = b64url(createHmac('sha256', this.secret).update(body).digest())\n return `rt_${body}.${sig}`\n }\n\n verifyRiskToken(token: string, options?: VerifyRiskTokenOptions): RiskTokenPayload | null {\n if (!token.startsWith('rt_')) return null\n const raw = token.slice(3)\n const parts = raw.split('.')\n if (parts.length !== 2) return null\n const [body, sig] = parts\n if (!body || !sig) return null\n\n const expected = b64url(createHmac('sha256', this.secret).update(body).digest())\n const a = Buffer.from(sig)\n const b = Buffer.from(expected)\n if (a.length !== b.length || !timingSafeEqual(a, b)) return null\n\n try {\n const payload = JSON.parse(fromB64url(body).toString('utf8')) as RiskTokenPayload\n if (\n typeof payload.requestId !== 'string' ||\n typeof payload.siteId !== 'string' ||\n typeof payload.sessionId !== 'string' ||\n typeof payload.decision !== 'string' ||\n typeof payload.issuedAt !== 'number' ||\n typeof payload.expiresAt !== 'number'\n ) {\n return null\n }\n const now = Date.now()\n if (now >= payload.expiresAt) return null\n\n if (options?.expectedAction && payload.action !== options.expectedAction) {\n return null\n }\n\n if (options?.maxAgeMs != null && now - payload.issuedAt > options.maxAgeMs) {\n return null\n }\n\n return payload\n } catch {\n return null\n }\n }\n}\n\nexport type { RiskDecision, RiskTokenPayload, VerifyRiskTokenOptions }\n\n","import {\n BotDetectorError,\n DEFAULT_RISK_TOKEN_DURATION_MS,\n DEFAULT_SESSION_DURATION_MS,\n MAX_TELEMETRY_BYTES,\n decisionFromScore,\n extractHostnameFromOrigin,\n hostnameAllowed,\n ipMatches,\n mergeRiskThresholds,\n normalizeHostname,\n riskLevelFromScore,\n validateAnalyzeRequest,\n validateCreateSessionRequest,\n type AnalyzeRequest,\n type CreateSessionRequest,\n type CreateSessionResponse,\n type RequestContext,\n type RiskConfig,\n type RiskResult,\n type RiskTokenPayload,\n type SessionData,\n type SiteInfo,\n type TokenRevocationStore,\n type VerifyRiskTokenOptions,\n} from 'devn-bot-detector-core'\nimport type { EventSink, RateLimiter, SessionStore, SiteResolver } from './interfaces'\nimport { MemorySessionStore, MemorySiteResolver, secureId } from './memory'\nimport { MemoryRateLimiter } from './rate-limit'\nimport { RiskEngine, type RiskEvaluation, type RiskInput } from './risk'\nimport { TokenManager } from './token'\n\nexport interface ServerOptions {\n readonly secret: string\n readonly sessionDuration?: number\n readonly riskThresholds?: Partial<RiskConfig>\n readonly actionThresholds?: Record<string, Partial<RiskConfig>>\n readonly ipAllowlist?: readonly string[]\n readonly ipDenylist?: readonly string[]\n readonly detectorWeights?: Record<string, number>\n readonly tokenRevocationStore?: TokenRevocationStore\n readonly enableRiskToken?: boolean\n readonly riskTokenDuration?: number\n readonly trustProxy?: boolean\n readonly debug?: boolean\n readonly siteResolver?: SiteResolver\n readonly sessionStore?: SessionStore\n readonly rateLimiter?: RateLimiter\n readonly eventSink?: EventSink\n readonly maxTelemetryBytes?: number\n readonly rateLimits?: {\n readonly sitePerMinute?: number\n readonly sessionPerMinute?: number\n readonly ipPerMinute?: number\n }\n readonly customScoring?: (\n input: RiskInput,\n ) => Partial<RiskEvaluation> | Promise<Partial<RiskEvaluation>>\n}\n\nexport interface CreateSessionInput extends CreateSessionRequest {\n readonly siteKey: string\n readonly request?: RequestContext\n}\n\nexport interface AnalyzeInput extends AnalyzeRequest {\n readonly siteKey: string\n readonly request?: RequestContext\n}\n\nfunction debugLog(enabled: boolean, ...args: unknown[]): void {\n if (enabled) console.debug('[bot-detector-server]', ...args)\n}\n\nexport class BotDetectionServer {\n private readonly sessionDuration: number\n private readonly thresholds: RiskConfig\n private readonly actionThresholds?: Record<string, Partial<RiskConfig>>\n private readonly ipAllowlist: readonly string[]\n private readonly ipDenylist: readonly string[]\n private readonly tokenRevocationStore?: TokenRevocationStore\n private readonly enableRiskToken: boolean\n private readonly riskTokenDuration: number\n private readonly trustProxy: boolean\n private readonly debug: boolean\n private readonly maxTelemetryBytes: number\n private readonly siteResolver: SiteResolver\n private readonly sessionStore: SessionStore\n private readonly rateLimiter: RateLimiter\n private readonly eventSink?: EventSink\n private readonly tokens: TokenManager\n private readonly engine: RiskEngine\n private readonly customScoring?: ServerOptions['customScoring']\n private readonly rateLimits: {\n sitePerMinute: number\n sessionPerMinute: number\n ipPerMinute: number\n }\n\n constructor(options: ServerOptions) {\n if (!options.secret || options.secret.length < 16) {\n throw new Error('BotDetectionServer requires secret (>= 16 chars)')\n }\n this.sessionDuration = options.sessionDuration ?? DEFAULT_SESSION_DURATION_MS\n this.thresholds = mergeRiskThresholds(options.riskThresholds)\n this.actionThresholds = options.actionThresholds\n this.ipAllowlist = options.ipAllowlist ?? []\n this.ipDenylist = options.ipDenylist ?? []\n this.tokenRevocationStore = options.tokenRevocationStore\n this.enableRiskToken = options.enableRiskToken ?? true\n this.riskTokenDuration = options.riskTokenDuration ?? DEFAULT_RISK_TOKEN_DURATION_MS\n this.trustProxy = options.trustProxy ?? false\n this.debug = options.debug ?? false\n this.maxTelemetryBytes = options.maxTelemetryBytes ?? MAX_TELEMETRY_BYTES\n this.siteResolver = options.siteResolver ?? new MemorySiteResolver()\n this.sessionStore = options.sessionStore ?? new MemorySessionStore()\n this.rateLimiter = options.rateLimiter ?? new MemoryRateLimiter()\n this.eventSink = options.eventSink\n this.tokens = new TokenManager(options.secret)\n this.engine = new RiskEngine({ detectorWeights: options.detectorWeights })\n this.customScoring = options.customScoring\n this.rateLimits = {\n sitePerMinute: options.rateLimits?.sitePerMinute ?? 600,\n sessionPerMinute: options.rateLimits?.sessionPerMinute ?? 60,\n ipPerMinute: options.rateLimits?.ipPerMinute ?? 120,\n }\n }\n\n async getSite(siteKey: string): Promise<SiteInfo | null> {\n return this.siteResolver.resolveSite(siteKey)\n }\n\n validateHostname(hostname: string, site: SiteInfo, request?: RequestContext): boolean {\n const host = normalizeHostname(hostname)\n if (!hostnameAllowed(host, site.allowedDomains)) return false\n\n const originHost = extractHostnameFromOrigin(request?.origin)\n if (originHost && !hostnameAllowed(originHost, site.allowedDomains)) return false\n\n return true\n }\n\n async createSession(input: CreateSessionInput): Promise<CreateSessionResponse> {\n const requestId = secureId('req')\n const parsed = validateCreateSessionRequest(input)\n if (!parsed.ok) {\n throw new BotDetectorError('MALFORMED_REQUEST', parsed.message, requestId)\n }\n\n const site = await this.resolveActiveSite(input.siteKey, requestId)\n if (!this.validateHostname(parsed.value.hostname, site, input.request)) {\n throw new BotDetectorError(\n 'INVALID_HOSTNAME',\n 'Hostname is not allowed for this site.',\n requestId,\n )\n }\n\n const ip = this.resolveIp(input.request)\n if (ip && ipMatches(ip, this.ipDenylist)) {\n throw new BotDetectorError('IP_BLOCKED', 'Access denied from this IP.', requestId)\n }\n\n await this.assertRateLimits(input.siteKey, undefined, input.request, requestId)\n\n const now = Date.now()\n const sessionId = secureId('ses')\n const nonce = secureId('nonce', 12)\n const expiresAt = now + this.sessionDuration\n\n const session: SessionData = {\n sessionId,\n siteKey: input.siteKey,\n siteId: site.siteId,\n tenantId: site.tenantId,\n nonce,\n createdAt: now,\n expiresAt,\n hostname: normalizeHostname(parsed.value.hostname),\n sequence: 0,\n usedNonces: [],\n }\n\n await this.sessionStore.create(session)\n await this.eventSink?.onSessionCreated?.(session)\n debugLog(this.debug, 'session created', { sessionId, siteId: site.siteId })\n\n return {\n sessionId,\n nonce,\n expiresAt: new Date(expiresAt).toISOString(),\n }\n }\n\n async analyze(input: AnalyzeInput): Promise<RiskResult> {\n const requestId = secureId('req')\n const parsed = validateAnalyzeRequest(input, this.maxTelemetryBytes)\n if (!parsed.ok) {\n const code =\n parsed.message.includes('bytes') || parsed.message.includes('forbidden')\n ? 'INVALID_TELEMETRY'\n : 'MALFORMED_REQUEST'\n throw new BotDetectorError(code, parsed.message, requestId)\n }\n\n const site = await this.resolveActiveSite(input.siteKey, requestId)\n if (!this.validateHostname(parsed.value.hostname, site, input.request)) {\n throw new BotDetectorError(\n 'INVALID_HOSTNAME',\n 'Hostname is not allowed for this site.',\n requestId,\n )\n }\n\n const session = await this.sessionStore.get(parsed.value.sessionId)\n if (!session || session.siteKey !== input.siteKey) {\n throw new BotDetectorError('INVALID_SESSION', 'Invalid or unknown session.', requestId)\n }\n if (Date.now() >= session.expiresAt) {\n await this.sessionStore.invalidate(session.sessionId)\n throw new BotDetectorError('SESSION_EXPIRED', 'Invalid or expired session.', requestId)\n }\n if (normalizeHostname(session.hostname) !== normalizeHostname(parsed.value.hostname)) {\n throw new BotDetectorError(\n 'INVALID_HOSTNAME',\n 'Hostname does not match session.',\n requestId,\n )\n }\n\n if (parsed.value.sequence <= session.sequence) {\n throw new BotDetectorError(\n 'INVALID_SEQUENCE',\n 'Sequence replay or out of order.',\n requestId,\n )\n }\n\n if (parsed.value.nonce && session.usedNonces.includes(parsed.value.nonce)) {\n throw new BotDetectorError('INVALID_NONCE', 'Nonce has already been used.', requestId)\n }\n\n const ip = this.resolveIp(input.request)\n if (ip && ipMatches(ip, this.ipDenylist)) {\n throw new BotDetectorError('IP_BLOCKED', 'Access denied from this IP.', requestId)\n }\n\n if (ip && ipMatches(ip, this.ipAllowlist)) {\n debugLog(this.debug, 'allowlisted IP bypassed risk check', { ip })\n const issuedAt = Date.now()\n const tokenExpires = issuedAt + this.riskTokenDuration\n let riskToken: string | undefined\n let expiresAt: string | undefined\n if (this.enableRiskToken) {\n riskToken = this.createRiskToken({\n requestId,\n siteId: site.siteId,\n sessionId: session.sessionId,\n decision: 'allow',\n issuedAt,\n expiresAt: tokenExpires,\n action: parsed.value.action,\n score: 100,\n })\n expiresAt = new Date(tokenExpires).toISOString()\n }\n return {\n requestId,\n score: 100,\n confidence: 0.99,\n decision: 'allow',\n riskLevel: 'low',\n action: parsed.value.action,\n ...(riskToken ? { riskToken } : {}),\n ...(expiresAt ? { expiresAt } : {}),\n timestamp: Date.now(),\n }\n }\n\n await this.assertRateLimits(input.siteKey, session.sessionId, input.request, requestId)\n\n const evaluation = await this.calculateRisk({\n telemetry: parsed.value.telemetry,\n request: this.normalizeRequest(input.request),\n sessionAgeMs: Date.now() - session.createdAt,\n sequence: parsed.value.sequence,\n action: parsed.value.action,\n metadata: parsed.value.metadata,\n })\n\n const effectiveThresholds =\n parsed.value.action && this.actionThresholds?.[parsed.value.action]\n ? mergeRiskThresholds(this.actionThresholds[parsed.value.action])\n : this.thresholds\n\n const decision = evaluation.decision ?? decisionFromScore(evaluation.score, effectiveThresholds)\n const riskLevel = evaluation.riskLevel ?? riskLevelFromScore(evaluation.score)\n\n let riskToken: string | undefined\n let expiresAt: string | undefined\n if (this.enableRiskToken) {\n const issuedAt = Date.now()\n const tokenExpires = issuedAt + this.riskTokenDuration\n riskToken = this.createRiskToken({\n requestId,\n siteId: site.siteId,\n sessionId: session.sessionId,\n decision,\n issuedAt,\n expiresAt: tokenExpires,\n action: parsed.value.action,\n score: evaluation.score,\n })\n expiresAt = new Date(tokenExpires).toISOString()\n }\n\n const usedNonces = [...session.usedNonces]\n if (parsed.value.nonce) {\n usedNonces.push(parsed.value.nonce)\n if (usedNonces.length > 32) usedNonces.splice(0, usedNonces.length - 32)\n }\n await this.sessionStore.update({\n ...session,\n sequence: parsed.value.sequence,\n usedNonces,\n nonce: secureId('nonce', 12),\n })\n\n const result: RiskResult = {\n requestId,\n score: evaluation.score,\n confidence: evaluation.confidence,\n decision,\n riskLevel,\n action: parsed.value.action,\n ...(riskToken ? { riskToken } : {}),\n ...(expiresAt ? { expiresAt } : {}),\n timestamp: Date.now(),\n }\n\n await this.eventSink?.onAnalysis?.(result)\n debugLog(this.debug, 'analyzed', {\n requestId,\n decision: result.decision,\n score: result.score,\n action: result.action,\n })\n return result\n }\n\n async calculateRisk(input: RiskInput): Promise<RiskEvaluation> {\n const base = this.engine.evaluate(input)\n if (!this.customScoring) return base\n\n const custom = await this.customScoring(input)\n const riskFromCustom = custom.risk ?? 0\n let score = base.score - riskFromCustom\n if (typeof custom.score === 'number') score = custom.score\n score = Math.max(0, Math.min(100, Math.round(score)))\n\n const internalReasons = [\n ...base.internalReasons,\n ...(custom.internalReasons ?? []),\n ...(riskFromCustom ? [{ code: 'CUSTOM_SCORING', weight: riskFromCustom }] : []),\n ]\n\n return {\n score,\n confidence: custom.confidence ?? base.confidence,\n riskLevel: custom.riskLevel ?? riskLevelFromScore(score),\n decision: custom.decision,\n internalReasons,\n }\n }\n\n createRiskToken(payload: RiskTokenPayload): string {\n return this.tokens.createRiskToken(payload)\n }\n\n verifyRiskToken(token: string, options?: VerifyRiskTokenOptions): RiskTokenPayload | null {\n return this.tokens.verifyRiskToken(token, options)\n }\n\n /**\n * Validates risk token and consumes it (one-time use) if tokenRevocationStore is configured.\n * Throws TOKEN_ALREADY_USED if already consumed or MALFORMED_REQUEST if invalid/expired.\n */\n async consumeRiskToken(\n token: string,\n options?: VerifyRiskTokenOptions,\n ): Promise<RiskTokenPayload> {\n const requestId = secureId('req')\n const payload = this.verifyRiskToken(token, options)\n if (!payload) {\n throw new BotDetectorError('MALFORMED_REQUEST', 'Invalid or expired risk token.', requestId)\n }\n\n if (this.tokenRevocationStore) {\n const revoked = await this.tokenRevocationStore.isRevoked(token)\n if (revoked) {\n throw new BotDetectorError(\n 'TOKEN_ALREADY_USED',\n 'Risk token has already been consumed.',\n requestId,\n )\n }\n const remainingTtl = Math.max(1000, payload.expiresAt - Date.now())\n await this.tokenRevocationStore.revoke(token, remainingTtl)\n }\n\n return payload\n }\n\n private async resolveActiveSite(siteKey: string, requestId: string): Promise<SiteInfo> {\n const site = await this.siteResolver.resolveSite(siteKey)\n if (!site || site.status !== 'active') {\n throw new BotDetectorError('INVALID_SITE_KEY', 'Invalid or inactive site key.', requestId)\n }\n return site\n }\n\n private async assertRateLimits(\n siteKey: string,\n sessionId: string | undefined,\n request: RequestContext | undefined,\n requestId: string,\n ): Promise<void> {\n const site = await this.rateLimiter.check(\n `site:${siteKey}`,\n this.rateLimits.sitePerMinute,\n 60_000,\n )\n if (!site.allowed) {\n throw new BotDetectorError('RATE_LIMITED', 'Rate limit exceeded.', requestId)\n }\n if (sessionId) {\n const sess = await this.rateLimiter.check(\n `session:${sessionId}`,\n this.rateLimits.sessionPerMinute,\n 60_000,\n )\n if (!sess.allowed) {\n throw new BotDetectorError('RATE_LIMITED', 'Rate limit exceeded.', requestId)\n }\n }\n const ip = this.resolveIp(request)\n if (ip) {\n const ipLimit = await this.rateLimiter.check(`ip:${ip}`, this.rateLimits.ipPerMinute, 60_000)\n if (!ipLimit.allowed) {\n throw new BotDetectorError('RATE_LIMITED', 'Rate limit exceeded.', requestId)\n }\n }\n }\n\n private resolveIp(request: RequestContext | undefined): string | undefined {\n if (!request) return undefined\n if (this.trustProxy && request.forwardedFor) {\n return request.forwardedFor.split(',')[0]?.trim()\n }\n return request.ip\n }\n\n private normalizeRequest(request: RequestContext | undefined): RequestContext {\n if (!request) return {}\n return {\n ...(request.ip ? { ip: request.ip } : {}),\n ...(request.userAgent ? { userAgent: request.userAgent } : {}),\n ...(request.origin ? { origin: request.origin } : {}),\n ...(request.host ? { host: request.host } : {}),\n ...(this.trustProxy && request.forwardedFor\n ? { forwardedFor: request.forwardedFor }\n : {}),\n }\n }\n}\n\n","import type { RateLimitResult, SessionData, TokenRevocationStore } from 'devn-bot-detector-core'\nimport type { RateLimiter, RedisLikeClient, SessionStore } from '../interfaces'\n\nexport interface RedisSessionStoreOptions {\n readonly prefix?: string\n readonly defaultTtlSeconds?: number\n}\n\n/** Production Redis Session Store with automatic TTL expiry. */\nexport class RedisSessionStore implements SessionStore {\n private readonly client: RedisLikeClient\n private readonly prefix: string\n private readonly defaultTtl: number\n\n constructor(client: RedisLikeClient, options?: RedisSessionStoreOptions) {\n this.client = client\n this.prefix = options?.prefix ?? 'bd:sess:'\n this.defaultTtl = options?.defaultTtlSeconds ?? 1800\n }\n\n private key(sessionId: string): string {\n return `${this.prefix}${sessionId}`\n }\n\n async create(session: SessionData): Promise<void> {\n const remaining = Math.ceil((session.expiresAt - Date.now()) / 1000)\n const ttl = remaining > 0 ? remaining : this.defaultTtl\n await this.client.set(this.key(session.sessionId), JSON.stringify(session), 'EX', ttl)\n }\n\n async get(sessionId: string): Promise<SessionData | null> {\n const data = await this.client.get(this.key(sessionId))\n if (!data) return null\n try {\n return JSON.parse(data) as SessionData\n } catch {\n return null\n }\n }\n\n async update(session: SessionData): Promise<void> {\n const remaining = Math.ceil((session.expiresAt - Date.now()) / 1000)\n const ttl = remaining > 0 ? remaining : this.defaultTtl\n await this.client.set(this.key(session.sessionId), JSON.stringify(session), 'EX', ttl)\n }\n\n async invalidate(sessionId: string): Promise<void> {\n await this.client.del(this.key(sessionId))\n }\n}\n\nexport interface RedisRateLimiterOptions {\n readonly prefix?: string\n}\n\n/** Production Redis Rate Limiter with atomic INCR + EXPIRE via Lua or pipeline. */\nexport class RedisRateLimiter implements RateLimiter {\n private readonly client: RedisLikeClient\n private readonly prefix: string\n\n constructor(client: RedisLikeClient, options?: RedisRateLimiterOptions) {\n this.client = client\n this.prefix = options?.prefix ?? 'bd:rl:'\n }\n\n async check(key: string, limit: number, windowMs: number): Promise<RateLimitResult> {\n const fullKey = `${this.prefix}${key}`\n const windowSec = Math.max(1, Math.ceil(windowMs / 1000))\n\n try {\n if (typeof this.client.eval === 'function') {\n const lua = `\n local current = redis.call('INCR', KEYS[1])\n if current == 1 then\n redis.call('EXPIRE', KEYS[1], ARGV[1])\n end\n return current\n `\n const count = Number(await this.client.eval(lua, 1, fullKey, windowSec))\n const allowed = count <= limit\n const remaining = Math.max(0, limit - count)\n return {\n allowed,\n remaining,\n ...(allowed ? {} : { retryAfter: windowSec }),\n }\n }\n\n // Fallback\n const raw = await this.client.get(fullKey)\n const count = (raw ? parseInt(raw, 10) : 0) + 1\n await this.client.set(fullKey, String(count), 'EX', windowSec)\n const allowed = count <= limit\n return {\n allowed,\n remaining: Math.max(0, limit - count),\n ...(allowed ? {} : { retryAfter: windowSec }),\n }\n } catch {\n // Fail-open on cache infrastructure errors\n return { allowed: true, remaining: limit }\n }\n }\n}\n\nexport interface RedisTokenRevocationStoreOptions {\n readonly prefix?: string\n}\n\n/** Production Redis token revocation store for one-time token consumption / replay prevention. */\nexport class RedisTokenRevocationStore implements TokenRevocationStore {\n private readonly client: RedisLikeClient\n private readonly prefix: string\n\n constructor(client: RedisLikeClient, options?: RedisTokenRevocationStoreOptions) {\n this.client = client\n this.prefix = options?.prefix ?? 'bd:revoked:'\n }\n\n async isRevoked(tokenId: string): Promise<boolean> {\n const exists = await this.client.get(`${this.prefix}${tokenId}`)\n return exists !== null\n }\n\n async revoke(tokenId: string, ttlMs: number): Promise<void> {\n const ttlSec = Math.max(1, Math.ceil(ttlMs / 1000))\n await this.client.set(`${this.prefix}${tokenId}`, '1', 'EX', ttlSec)\n }\n}\n","import {\n BotDetectorError,\n SITE_KEY_HEADER,\n type RiskTokenPayload,\n type VerifyRiskTokenOptions,\n} from 'devn-bot-detector-core'\nimport type { BotDetectionServer } from '../BotDetectionServer'\n\nexport interface FrameworkRequestLike {\n headers?: Record<string, string | string[] | undefined>\n header?(name: string): string | undefined\n get?(name: string): string | undefined\n body?: unknown\n ip?: string\n}\n\nexport interface FrameworkResponseLike {\n status(code: number): this\n json(body: unknown): void\n}\n\nexport interface TokenGuardOptions extends VerifyRiskTokenOptions {\n /** If true, the token is consumed (one-time use, preventing replay attacks). Defaults to false. */\n readonly consume?: boolean\n /** Header to read the token from. Defaults to 'x-risk-token'. */\n readonly headerName?: string\n /** Fallback property to read the token from request body. Defaults to 'riskToken'. */\n readonly bodyField?: string\n}\n\n/**\n * Universal helper to extract and verify or consume a risk token from any incoming HTTP request.\n * Useful for Next.js Server Actions, Nuxt, SvelteKit, Remix, NestJS, Fastify, Express, and Hono.\n */\nexport async function verifyRequestRiskToken(\n server: BotDetectionServer,\n request: FrameworkRequestLike,\n options?: TokenGuardOptions,\n): Promise<RiskTokenPayload> {\n const headerName = (options?.headerName ?? 'x-risk-token').toLowerCase()\n const bodyField = options?.bodyField ?? 'riskToken'\n\n let token: string | undefined\n if (typeof request.header === 'function') {\n token = request.header(headerName)\n } else if (typeof request.get === 'function') {\n token = request.get(headerName)\n } else if (request.headers) {\n const raw = request.headers[headerName]\n token = Array.isArray(raw) ? raw[0] : raw\n }\n\n if (!token && request.body && typeof request.body === 'object') {\n const fromBody = (request.body as Record<string, unknown>)[bodyField]\n if (typeof fromBody === 'string') token = fromBody\n }\n\n if (!token) {\n throw new BotDetectorError(\n 'MALFORMED_REQUEST',\n `Missing risk token in header '${headerName}' or body field '${bodyField}'.`,\n 'req_guard',\n )\n }\n\n if (options?.consume) {\n return server.consumeRiskToken(token, options)\n }\n\n const payload = server.verifyRiskToken(token, options)\n if (!payload) {\n throw new BotDetectorError('MALFORMED_REQUEST', 'Invalid or expired risk token.', 'req_guard')\n }\n\n return payload\n}\n\n/** Express / Connect helper creating ready-to-mount route handlers and guards. */\nexport function createExpressBotDetector(server: BotDetectionServer) {\n return {\n /** Route handler for POST /v1/session */\n sessionHandler: async (req: any, res: any) => {\n try {\n const siteKey = String(req.header(SITE_KEY_HEADER) || req.headers[SITE_KEY_HEADER.toLowerCase()] || '')\n const result = await server.createSession({\n siteKey,\n hostname: req.body?.hostname,\n userAgent: req.body?.userAgent,\n timestamp: req.body?.timestamp,\n request: {\n ip: req.ip,\n userAgent: req.get?.('user-agent'),\n origin: req.get?.('origin'),\n host: req.get?.('host'),\n forwardedFor: req.get?.('x-forwarded-for'),\n },\n })\n res.json(result)\n } catch (err) {\n if (err instanceof BotDetectorError) {\n res.status(err.code === 'RATE_LIMITED' ? 429 : err.code === 'IP_BLOCKED' ? 403 : 400).json(err.toJSON())\n return\n }\n res.status(500).json({ error: { code: 'INTERNAL_ERROR', message: 'Internal error.', requestId: 'req_err' } })\n }\n },\n\n /** Route handler for POST /v1/analyze */\n analyzeHandler: async (req: any, res: any) => {\n try {\n const siteKey = String(req.header(SITE_KEY_HEADER) || req.headers[SITE_KEY_HEADER.toLowerCase()] || '')\n const result = await server.analyze({\n siteKey,\n ...req.body,\n request: {\n ip: req.ip,\n userAgent: req.get?.('user-agent'),\n origin: req.get?.('origin'),\n host: req.get?.('host'),\n forwardedFor: req.get?.('x-forwarded-for'),\n },\n })\n res.json(result)\n } catch (err) {\n if (err instanceof BotDetectorError) {\n res.status(err.code === 'RATE_LIMITED' ? 429 : err.code === 'IP_BLOCKED' ? 403 : 400).json(err.toJSON())\n return\n }\n res.status(500).json({ error: { code: 'INTERNAL_ERROR', message: 'Internal error.', requestId: 'req_err' } })\n }\n },\n\n /** Middleware protecting sensitive routes with risk token verification */\n requireRiskToken: (options?: TokenGuardOptions) => {\n return async (req: any, res: any, next: any) => {\n try {\n const payload = await verifyRequestRiskToken(server, req, options)\n req.riskTokenPayload = payload\n next()\n } catch (err) {\n if (err instanceof BotDetectorError) {\n res.status(err.code === 'TOKEN_ALREADY_USED' ? 409 : 403).json(err.toJSON())\n return\n }\n res.status(403).json({ error: { code: 'MALFORMED_REQUEST', message: 'Forbidden', requestId: 'req_guard' } })\n }\n }\n },\n }\n}\n"]}
@@ -0,0 +1,258 @@
1
+ import { SiteInfo, SessionData, RateLimitResult, RiskResult, Telemetry, RequestContext, RiskReason, RiskLevel, RiskDecision, AnalyzeRequest, RiskConfig, TokenRevocationStore, CreateSessionRequest, CreateSessionResponse, RiskTokenPayload, VerifyRiskTokenOptions } from 'devn-bot-detector-core';
2
+ export { API_PATHS, BotDetectorError, RequestContext, RiskDecision, RiskResult, RiskTokenPayload, SITE_KEY_HEADER, SessionData, SiteInfo, Telemetry, TokenRevocationStore, VerifyRiskTokenOptions } from 'devn-bot-detector-core';
3
+
4
+ interface SiteResolver {
5
+ resolveSite(siteKey: string): Promise<SiteInfo | null>;
6
+ }
7
+ interface SessionStore {
8
+ create(session: SessionData): Promise<void>;
9
+ get(sessionId: string): Promise<SessionData | null>;
10
+ update(session: SessionData): Promise<void>;
11
+ invalidate(sessionId: string): Promise<void>;
12
+ }
13
+ interface RateLimiter {
14
+ check(key: string, limit: number, windowMs: number): RateLimitResult | Promise<RateLimitResult>;
15
+ }
16
+ interface EventSink {
17
+ onAnalysis?(result: RiskResult): Promise<void> | void;
18
+ onSessionCreated?(session: SessionData): Promise<void> | void;
19
+ onError?(error: {
20
+ code: string;
21
+ message: string;
22
+ requestId: string;
23
+ }): Promise<void> | void;
24
+ }
25
+ /** Minimal duck-typed Redis interface compatible with ioredis, @redis/client, upstash, etc. */
26
+ interface RedisLikeClient {
27
+ get(key: string): Promise<string | null>;
28
+ set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
29
+ del(key: string): Promise<unknown>;
30
+ eval?(script: string, numkeys: number, ...args: (string | number)[]): Promise<unknown>;
31
+ }
32
+
33
+ interface RiskInput {
34
+ readonly telemetry: Telemetry;
35
+ readonly request: RequestContext;
36
+ readonly sessionAgeMs: number;
37
+ readonly sequence: number;
38
+ readonly action?: string;
39
+ readonly metadata?: Record<string, unknown>;
40
+ }
41
+ interface RiskEvaluation {
42
+ readonly score: number;
43
+ readonly confidence: number;
44
+ readonly riskLevel: RiskLevel;
45
+ readonly decision?: RiskDecision;
46
+ readonly internalReasons: RiskReason[];
47
+ /** Optional additive risk from custom scoring (0–100 contribution scale). */
48
+ readonly risk?: number;
49
+ }
50
+ type Detector = (input: RiskInput) => RiskReason[];
51
+ /** Automation indicators — never decisive alone. */
52
+ declare const AutomationDetector: Detector;
53
+ declare const HeadlessAdvancedDetector: Detector;
54
+ declare const EntropyDetector: Detector;
55
+ declare const ActionRiskDetector: Detector;
56
+ declare const VelocityDetector: Detector;
57
+ declare const TimingDetector: Detector;
58
+ declare const InteractionDetector: Detector;
59
+ declare const BrowserConsistencyDetector: Detector;
60
+ declare const SessionDetector: Detector;
61
+ declare const DEFAULT_DETECTORS: Detector[];
62
+ interface RiskEngineOptions {
63
+ readonly detectors?: Detector[];
64
+ readonly detectorWeights?: Record<string, number>;
65
+ }
66
+ declare class RiskEngine {
67
+ private readonly detectors;
68
+ private readonly weights;
69
+ constructor(options?: Detector[] | RiskEngineOptions);
70
+ evaluate(input: RiskInput): RiskEvaluation;
71
+ }
72
+
73
+ interface ServerOptions {
74
+ readonly secret: string;
75
+ readonly sessionDuration?: number;
76
+ readonly riskThresholds?: Partial<RiskConfig>;
77
+ readonly actionThresholds?: Record<string, Partial<RiskConfig>>;
78
+ readonly ipAllowlist?: readonly string[];
79
+ readonly ipDenylist?: readonly string[];
80
+ readonly detectorWeights?: Record<string, number>;
81
+ readonly tokenRevocationStore?: TokenRevocationStore;
82
+ readonly enableRiskToken?: boolean;
83
+ readonly riskTokenDuration?: number;
84
+ readonly trustProxy?: boolean;
85
+ readonly debug?: boolean;
86
+ readonly siteResolver?: SiteResolver;
87
+ readonly sessionStore?: SessionStore;
88
+ readonly rateLimiter?: RateLimiter;
89
+ readonly eventSink?: EventSink;
90
+ readonly maxTelemetryBytes?: number;
91
+ readonly rateLimits?: {
92
+ readonly sitePerMinute?: number;
93
+ readonly sessionPerMinute?: number;
94
+ readonly ipPerMinute?: number;
95
+ };
96
+ readonly customScoring?: (input: RiskInput) => Partial<RiskEvaluation> | Promise<Partial<RiskEvaluation>>;
97
+ }
98
+ interface CreateSessionInput extends CreateSessionRequest {
99
+ readonly siteKey: string;
100
+ readonly request?: RequestContext;
101
+ }
102
+ interface AnalyzeInput extends AnalyzeRequest {
103
+ readonly siteKey: string;
104
+ readonly request?: RequestContext;
105
+ }
106
+ declare class BotDetectionServer {
107
+ private readonly sessionDuration;
108
+ private readonly thresholds;
109
+ private readonly actionThresholds?;
110
+ private readonly ipAllowlist;
111
+ private readonly ipDenylist;
112
+ private readonly tokenRevocationStore?;
113
+ private readonly enableRiskToken;
114
+ private readonly riskTokenDuration;
115
+ private readonly trustProxy;
116
+ private readonly debug;
117
+ private readonly maxTelemetryBytes;
118
+ private readonly siteResolver;
119
+ private readonly sessionStore;
120
+ private readonly rateLimiter;
121
+ private readonly eventSink?;
122
+ private readonly tokens;
123
+ private readonly engine;
124
+ private readonly customScoring?;
125
+ private readonly rateLimits;
126
+ constructor(options: ServerOptions);
127
+ getSite(siteKey: string): Promise<SiteInfo | null>;
128
+ validateHostname(hostname: string, site: SiteInfo, request?: RequestContext): boolean;
129
+ createSession(input: CreateSessionInput): Promise<CreateSessionResponse>;
130
+ analyze(input: AnalyzeInput): Promise<RiskResult>;
131
+ calculateRisk(input: RiskInput): Promise<RiskEvaluation>;
132
+ createRiskToken(payload: RiskTokenPayload): string;
133
+ verifyRiskToken(token: string, options?: VerifyRiskTokenOptions): RiskTokenPayload | null;
134
+ /**
135
+ * Validates risk token and consumes it (one-time use) if tokenRevocationStore is configured.
136
+ * Throws TOKEN_ALREADY_USED if already consumed or MALFORMED_REQUEST if invalid/expired.
137
+ */
138
+ consumeRiskToken(token: string, options?: VerifyRiskTokenOptions): Promise<RiskTokenPayload>;
139
+ private resolveActiveSite;
140
+ private assertRateLimits;
141
+ private resolveIp;
142
+ private normalizeRequest;
143
+ }
144
+
145
+ declare function secureId(prefix: string, bytes?: number): string;
146
+ /** In-memory site resolver for development/testing only. */
147
+ declare class MemorySiteResolver implements SiteResolver {
148
+ private readonly sites;
149
+ constructor(entries?: Array<{
150
+ siteKey: string;
151
+ site: SiteInfo;
152
+ }>);
153
+ set(siteKey: string, site: SiteInfo): void;
154
+ resolveSite(siteKey: string): Promise<SiteInfo | null>;
155
+ }
156
+ /** In-memory session store for development/testing only. */
157
+ declare class MemorySessionStore implements SessionStore {
158
+ private readonly sessions;
159
+ create(session: SessionData): Promise<void>;
160
+ get(sessionId: string): Promise<SessionData | null>;
161
+ update(session: SessionData): Promise<void>;
162
+ invalidate(sessionId: string): Promise<void>;
163
+ }
164
+ /** In-memory token revocation store for single-instance / dev usage. */
165
+ declare class MemoryTokenRevocationStore implements TokenRevocationStore {
166
+ private readonly revoked;
167
+ isRevoked(tokenId: string): boolean;
168
+ revoke(tokenId: string, ttlMs: number): void;
169
+ }
170
+
171
+ /** Simple in-memory sliding-window rate limiter (dev/default). */
172
+ declare class MemoryRateLimiter implements RateLimiter {
173
+ private readonly buckets;
174
+ check(key: string, limit: number, windowMs: number): RateLimitResult;
175
+ /** Best-effort cleanup for long-running processes. */
176
+ prune(): void;
177
+ }
178
+
179
+ declare class TokenManager {
180
+ private readonly secret;
181
+ constructor(secret: string);
182
+ createRiskToken(payload: RiskTokenPayload): string;
183
+ verifyRiskToken(token: string, options?: VerifyRiskTokenOptions): RiskTokenPayload | null;
184
+ }
185
+
186
+ interface RedisSessionStoreOptions {
187
+ readonly prefix?: string;
188
+ readonly defaultTtlSeconds?: number;
189
+ }
190
+ /** Production Redis Session Store with automatic TTL expiry. */
191
+ declare class RedisSessionStore implements SessionStore {
192
+ private readonly client;
193
+ private readonly prefix;
194
+ private readonly defaultTtl;
195
+ constructor(client: RedisLikeClient, options?: RedisSessionStoreOptions);
196
+ private key;
197
+ create(session: SessionData): Promise<void>;
198
+ get(sessionId: string): Promise<SessionData | null>;
199
+ update(session: SessionData): Promise<void>;
200
+ invalidate(sessionId: string): Promise<void>;
201
+ }
202
+ interface RedisRateLimiterOptions {
203
+ readonly prefix?: string;
204
+ }
205
+ /** Production Redis Rate Limiter with atomic INCR + EXPIRE via Lua or pipeline. */
206
+ declare class RedisRateLimiter implements RateLimiter {
207
+ private readonly client;
208
+ private readonly prefix;
209
+ constructor(client: RedisLikeClient, options?: RedisRateLimiterOptions);
210
+ check(key: string, limit: number, windowMs: number): Promise<RateLimitResult>;
211
+ }
212
+ interface RedisTokenRevocationStoreOptions {
213
+ readonly prefix?: string;
214
+ }
215
+ /** Production Redis token revocation store for one-time token consumption / replay prevention. */
216
+ declare class RedisTokenRevocationStore implements TokenRevocationStore {
217
+ private readonly client;
218
+ private readonly prefix;
219
+ constructor(client: RedisLikeClient, options?: RedisTokenRevocationStoreOptions);
220
+ isRevoked(tokenId: string): Promise<boolean>;
221
+ revoke(tokenId: string, ttlMs: number): Promise<void>;
222
+ }
223
+
224
+ interface FrameworkRequestLike {
225
+ headers?: Record<string, string | string[] | undefined>;
226
+ header?(name: string): string | undefined;
227
+ get?(name: string): string | undefined;
228
+ body?: unknown;
229
+ ip?: string;
230
+ }
231
+ interface FrameworkResponseLike {
232
+ status(code: number): this;
233
+ json(body: unknown): void;
234
+ }
235
+ interface TokenGuardOptions extends VerifyRiskTokenOptions {
236
+ /** If true, the token is consumed (one-time use, preventing replay attacks). Defaults to false. */
237
+ readonly consume?: boolean;
238
+ /** Header to read the token from. Defaults to 'x-risk-token'. */
239
+ readonly headerName?: string;
240
+ /** Fallback property to read the token from request body. Defaults to 'riskToken'. */
241
+ readonly bodyField?: string;
242
+ }
243
+ /**
244
+ * Universal helper to extract and verify or consume a risk token from any incoming HTTP request.
245
+ * Useful for Next.js Server Actions, Nuxt, SvelteKit, Remix, NestJS, Fastify, Express, and Hono.
246
+ */
247
+ declare function verifyRequestRiskToken(server: BotDetectionServer, request: FrameworkRequestLike, options?: TokenGuardOptions): Promise<RiskTokenPayload>;
248
+ /** Express / Connect helper creating ready-to-mount route handlers and guards. */
249
+ declare function createExpressBotDetector(server: BotDetectionServer): {
250
+ /** Route handler for POST /v1/session */
251
+ sessionHandler: (req: any, res: any) => Promise<void>;
252
+ /** Route handler for POST /v1/analyze */
253
+ analyzeHandler: (req: any, res: any) => Promise<void>;
254
+ /** Middleware protecting sensitive routes with risk token verification */
255
+ requireRiskToken: (options?: TokenGuardOptions) => (req: any, res: any, next: any) => Promise<void>;
256
+ };
257
+
258
+ export { ActionRiskDetector, type AnalyzeInput, AutomationDetector, BotDetectionServer, BrowserConsistencyDetector, type CreateSessionInput, DEFAULT_DETECTORS, type Detector, EntropyDetector, type EventSink, type FrameworkRequestLike, type FrameworkResponseLike, HeadlessAdvancedDetector, InteractionDetector, MemoryRateLimiter, MemorySessionStore, MemorySiteResolver, MemoryTokenRevocationStore, type RateLimiter, type RedisLikeClient, RedisRateLimiter, type RedisRateLimiterOptions, RedisSessionStore, type RedisSessionStoreOptions, RedisTokenRevocationStore, type RedisTokenRevocationStoreOptions, RiskEngine, type RiskEngineOptions, type RiskEvaluation, type RiskInput, type ServerOptions, SessionDetector, type SessionStore, type SiteResolver, TimingDetector, type TokenGuardOptions, TokenManager, VelocityDetector, createExpressBotDetector, secureId, verifyRequestRiskToken };