feat(detector): serve detector only from per-session provider bundles; always-PoW fallback#2744
Open
HughParry wants to merge 5 commits into
Open
feat(detector): serve detector only from per-session provider bundles; always-PoW fallback#2744HughParry wants to merge 5 commits into
HughParry wants to merge 5 commits into
Conversation
459dece to
b716672
Compare
…lback
Adds the provider-side building blocks for the precomputed, per-session
detector bundle pool (bumblebee-style):
- DetectorBundlePool: load precomputed {id}.js/{id}.json pairs into memory,
uniform-random per-session pick, hot-swap replace() for admin push.
- Redis short-TTL session->bundle mapping (cache:detector:{id}, 60s).
- Frictionless fallback to a real PoW challenge when the pool is initialised
but empty. Dark until the pool is initialised at boot.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
525e857 to
85483e7
Compare
…resolution Wires the detector bundle pool end-to-end (Part 2): - Boot: initialise the pool from PROSOPO_DETECTOR_POOL_DIR ONLY when the dir exists (absent => legacy path; present-but-empty => PoW fallback). - Client assign endpoint (POST .../client/detector/assign): picks a random bundle, stores the short-TTL session->bundle binding, returns the obfuscated detector inline; returns useProviderBundle:false when no pool. Excluded from the Prosopo-User header requirement (runs before the account exists). - Admin replace-pool endpoint (hot-swap the in-memory pool). - decryptPayload resolves the assigned bundle (single deterministic decrypt with its own keypair + inner cipher config) and falls back to the legacy key pool. innerConfig threaded through getBotScore + regenerated decode bundles. - Client (customDetectBot): resolves provider, asks for a bundle, loads it via blob import or falls back to the bundled detector; threads detectorSessionId. - types: ApiParams.detectorSessionId, assign/replace paths + bodies. All dark unless a pool dir is provisioned, so existing detection is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
85483e7 to
41bd528
Compare
…; always-PoW fallback The detector now lives ONLY in the provider-served pool bundles — no bundled/ inlined detector and no legacy detector-key pool. Each session's bundle encrypts everything it produces (score, SIMD readings, behavioural data) with its own RSA keypair + inner ChaCha20-Poly1305 cipher; the provider decrypts each payload with that exact bundle. - Pool is always initialised at boot (missing/empty dir ⇒ empty pool), so the three states collapse to two: bundles present ⇒ per-session serving; no bundles ⇒ always PoW (covers no-pool, empty-pool, and client `detectorUnavailable`). - `detectorSessionId → bundleId` Redis binding resolved at the frictionless hop; the bundleId is promoted onto the durable session record so later hops (SIMD attach, PoW/puzzle/image solution submit) decrypt with the same bundle. - Client: removed the inlined `@prosopo/detector` runtime import (type-only now); when no provider bundle can be obtained/run it signals `detectorUnavailable` and the provider serves PoW. - All server decrypt paths resolve the session bundle and pass its inner cipher; legacy key-pool brute force + env fallback removed. Decrypt failure fails closed. - Session record + schema gain `bundleId`; frictionless request gains `detectorUnavailable`. Tests updated for the bundle-only model. Note: temporary [POOL-DEBUG] logging retained for local validation; strip before merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… detector dep
Fixes two CI failures from the detector-only-on-providers change:
- e2e: the no-detector PoW fallback built session params from the client's
(empty) token, and sendCaptcha rejects a falsy token ("Session parameters
must be set before sending a pow captcha") → 400 → widget stuck. The bypass
session legitimately has no detection token, so synthesise a unique one.
- lint:refs: `@prosopo/detector` is now a type-only import in
procaptcha-frictionless, so it must be a tsconfig reference, not a runtime
dependency. Removed it from package.json dependencies and the tsconfig
references (type still resolves via the workspace); lockfile updated.
Adds a regression test asserting the no-detector fallback never passes an empty
token to sendPowCaptcha.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sopo/detector
The previous change removed @prosopo/detector from procaptcha-frictionless's deps
and tsconfig references (it's now type-only) — but `typeof import("@prosopo/detector")`
still needs the package's .d.ts, so `tsc --build` failed in CI ("Cannot find module
'@prosopo/detector'") because detector is no longer built as a project reference.
Declare the detector's default-export signature locally from shared @prosopo/types
primitives instead. This satisfies both the ref-linter (no detector dep/ref) and
`tsc --build` (no detector build needed), with no behavioural change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 1 of the per-session detector bundle delivery feature (provider/public side)
Adds the provider-side foundations for serving precomputed, per-session, individually-keyed detector bundles (the "bumblebee-style" pool), plus a graceful fallback. All changes are dark until the pool is initialised at boot (a follow-up), so existing frictionless detection is unaffected.
What's here
DetectorBundlePool(tasks/detection/bundlePool.ts) — Node analogue of bumblebee'sbundle_manager: loads precomputed{id}.js/{id}.jsonbundle pairs from disk into memory, uniform-random per-session selection, hot-swapreplace()for an admin push channel. Fails closed on an empty pool.database/src/redisCache.ts) —cacheDetectorBundle/getDetectorBundlebinding a detector session id → assigned bundle (cache:detector:{id}, default 60s), added toSESSION_KEY_PATTERNS.getFrictionlessCaptchaChallenge/shortCircuit.ts) — when the pool is initialised but empty (no bundle can be assigned), serve a real PoW challenge instead of failing the request. When the pool is not initialised (feature off), the legacy detection path is used unchanged — so this PR is safe to land ahead of the boot wiring.Tests
DetectorBundlePool: load/skip-malformed/uniform-coverage/hot-swap (8 tests).Not in this PR (follow-ups)
assign/serve/admin-replace endpointsdecryptPayloadresolution by bundle id (replacing the brute-force key loop)The companion encryption-layer + pool-build changes live in the private
captcha-privaterepo PR.🤖 Generated with Claude Code