Node.js में ALTCHA कैसे हल करें और उसे Fetch से Submit करें

Node.js में ALTCHA को आप एक ही call से हल कर सकते हैं, और पूरे stack में कहीं भी ब्राउज़र की ज़रूरत नहीं पड़ती। ALTCHA पहचान नहीं, proof of work है: साइट एक challenge जारी करती है, और client को तब तक hash करना होता है जब तक वह counter न मिल जाए जो उस challenge को पूरा करे। यहां देखने के लिए कुछ होता ही नहीं, इसलिए न WebDriver लगता है, न headless Chrome और न कोई user agent, और जवाब अंदाज़े से नहीं, गणना से निकलता है। CapSkip ने यह टाइप version 1.2.6 में जोड़ा और Node SDK इसे एक ही method के रूप में देता है। इसी वजह से यह उन गिने-चुने कैप्चा टाइप में से है जहां पूरा काम एक सामान्य HTTP script है: page fetch करें, उसमें से challenge पढ़ें, हल करें, token वापस भेजें, और यह सब सिर्फ़ global fetch और एक SDK call से।
आपको क्या चाहिए
- किसी Windows मशीन पर चल रहा CapSkip 1.2.6 या उसके बाद का संस्करण। ALTCHA सपोर्ट उसी रिलीज़ में आया था।
- Node 18 या उसके बाद का, जो package की अपनी ज़रूरत है और नीचे इस्तेमाल हुआ global fetch भी वहीं से आता है। TypeScript definitions package के अंदर ही आती हैं, इसलिए साथ में कोई types package इंस्टॉल करने की ज़रूरत नहीं।
- उस page का URL जिस पर widget लगा है, और वह endpoint जिससे widget अपना challenge मांगता है।
- सॉल्वर के लिए एक address। Local mode सिर्फ़ उसी डिवाइस के लिए 127.0.0.1 पर जवाब देता है; Server mode आपके network address या public IP पर सुनता है ताकि कोई दूसरी मशीन उस तक पहुंच सके। कौन सा लागू होगा यह Step 4 में बताया गया है, और दोनों यहां मिलते हैं: कनेक्शन सेटिंग्स.
# npm install capskip npm install capskip
Step 1: solve call, और challenge कहां से आता है
एक method, दो arguments: पहले page URL, फिर एक options object जिसमें challenge होता है। इसे endpoint थमा दें और CapSkip खुद challenge ले आता है।
// npm install capskip
const { CapSkip } = require('capskip');
const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });
// CapSkip fetches the challenge, then hashes until the counter fits.
const result = await solver.altcha('https://example.com/signup', {
challengeUrl: 'https://example.com/altcha/challenge',
});
console.log(result.token); // base64 payload for the form field
console.log(result.number); // the counter that satisfied itresult के दो fields सिर्फ़ ALTCHA के हैं। token वह base64 payload है जो form को चाहिए, और number वह counter है जिसने challenge हल किया। code field में वही string होती है जो token में है, इसलिए दोनों में से कोई भी चलेगा, पर token का नाम उसी field पर है जिसमें वह जाता है और call वाली जगह पर वह ज़्यादा साफ़ पढ़ा जाता है। GeeTest के fields और Turnstile वाला user agent यहां मौजूद नहीं होते।
इस option का नाम एक से ज़्यादा तरह से लिखा जा सकता है। challengeUrl और challenge_url, दोनों एक ही API parameter तक पहुंचते हैं, और यही बात challengeJson और challenge_json पर भी लागू होती है। camel case वाली spelling ही Node docs इस्तेमाल करते हैं और बाकी SDK से भी वही मेल खाती है, इसलिए उसी को चुनें और एक जैसा लिखते रहें; snake case वाले aliases इसलिए हैं ताकि PHP या Python गाइड से कॉपी किया गया sample भी चल जाए।
वह endpoint ढूंढें जिसे widget मांगता है
DevTools खोलें, Network tab पर जाएं और जिस page पर widget लगा है उसे दोबारा लोड करें। widget अपने challenge के लिए एक request करता है, आम तौर पर ऐसे path पर जिसमें altcha होता है। वही request URL आपको पास करना है, और उससे लौटने वाला JSON ही challenge document है, जिसे आप उसकी जगह पास कर सकते हैं।
जो attribute इसका नाम बताता है उसका अनुमान न लगाएँ, क्योंकि widget की पीढ़ियों के बीच यह बदल चुका है। पेज सोर्स पढ़ें।
| Widget पीढ़ी | वह attribute जो challenge का नाम बताता है |
|---|---|
| v1 और v2 | endpoint के लिए challengeurl, और इनलाइन challenge के लिए अलग challengejson attribute |
| v3 और उसके बाद | challenge, और वही attribute या तो URL लेता है या challenge डेटा |
<!-- v1 and v2 name the endpoint on its own attribute --> <altcha-widget challengeurl="https://example.com/altcha/challenge"></altcha-widget> <!-- v3 and later put both forms behind one attribute --> <altcha-widget challenge="https://example.com/altcha/challenge"></altcha-widget>
तीनों display styles, यानी native, checkbox और switch, सिर्फ़ दिखने में अलग हैं। तीनों वही payload submit करते हैं और यह फ़र्क सॉल्वर तक कभी पहुंचता ही नहीं, इसलिए आपको यह पता लगाने की ज़रूरत नहीं कि सामने कौन सा है। इन attributes के बारे में ALTCHA ने खुद लिखा है, देखें अपनी इंटीग्रेशन गाइड.
इसके बजाय challenge डॉक्यूमेंट पास करना
अगर आपके स्क्रैपर ने challenge पहले ही page से पढ़ लिया है, तो document ही पास कर दें और कोई network request होगी ही नहीं।
// No fetch happens: the document is already here.
const result = await solver.altcha('https://example.com/signup', {
challengeJson: {
algorithm: 'SHA-256',
challenge: 'YOUR_CHALLENGE_HASH',
salt: 'YOUR_SALT',
signature: 'YOUR_SIGNATURE',
maxnumber: 1000000,
},
});यह option एक object लेता है, जिसे आपके लिए serialise कर दिया जाता है, या फिर JSON string अगर वह आपके पास पहले से है। endpoint और document दोनों भेजना मना नहीं है, और ऐसे में inline document जीतता है, क्योंकि fetch करने से वही चीज़ दोबारा मिलेगी जो आपने अभी दी है। हां, load पड़ने पर ये दोनों रास्ते अलग तरह से बर्ताव करते हैं। जो inline challenge पहले ही expire हो चुका है उसे बेकार में hash करने के बजाय सीधे मना कर दिया जाता है, जबकि endpoint देने पर सॉल्वर एक नया challenge ले सकता है, अगर पहला वाला job के queue में पड़े रहने के दौरान खत्म हो गया हो।
solver किन algorithm को कवर करता है
एक ही method दोनों पीढ़ियों को संभालती है। legacy scheme SHA-1, SHA-256, SHA-384 और SHA-512 के साथ कवर होती है, और proof-of-work v2 PBKDF2 और iterative SHA के साथ। PBKDF2 वही डिफ़ॉल्ट है जिसकी सिफ़ारिश खुद ALTCHA करता है, इसलिए जो दायरा कवर होता है वह चालू साइटों के बड़े बहुमत तक पहुंच जाता है।
Argon2id और scrypt अपवाद हैं, और इन्हें आज़माने के बजाय मना कर दिया जाता है: इनमें से किसी एक वाला task करीब एक तिहाई सेकंड में ERROR_CAPTCHA_UNSOLVABLE के साथ लौट आता है और दोबारा कभी नहीं आज़माया जाता। यह जानबूझकर है। memory-hard function वह चीज़ नहीं है जिसे retry ठीक कर दे, इसलिए व्यस्त दिखने से बेहतर है तुरंत फेल हो जाना। ALTCHA में यह नतीजा किसी न पढ़े जाने वाले image की नहीं, बल्कि algorithm की ओर इशारा करता है।
Step 2: पूरा काम fetch से करें, बिना ब्राउज़र के
यहां render करने को कुछ है ही नहीं, इसलिए जिस page से challenge चाहिए वह बस एक document है जिसे आप fetch कर सकते हैं। यह बात साफ़ कहने लायक है, क्योंकि हर widget कैप्चा टाइप में ईमानदार जवाब में कहीं न कहीं ब्राउज़र आ ही जाता है। यहां नहीं आता। page fetch करें, markup में से वह attribute निकालें, और उसे सीधे सॉल्वर को दे दें।
// npm install capskip
const PAGE = 'https://example.com/signup';
// The page is only a document here: no browser, no rendering.
const html = await (await fetch(PAGE)).text();
// v1 and v2 use challengeurl; v3 and later use challenge.
const found = html.match(/(?:challengeurl|challenge)="([^"]+)"/i);
if (!found) throw new Error('no ALTCHA widget on this page');
const result = await solver.altcha(PAGE, { challengeUrl: found[1] });एक जाने-पहचाने page के लिए regular expression ठीक है, पर crawler के लिए यह बुरा विचार है, इसलिए जैसे ही आप ऐसा markup संभालें जो आपने खुद नहीं लिखा, असली HTML parser उठा लें। sample में असल बात parsing नहीं, ढांचा है: एक request, एक string, एक solve, और लॉन्च या बंद करने के लिए कोई process नहीं। इसी वजह से यह टाइप किसी serverless function या थोड़ी देर जीने वाले worker में अच्छा चलता है, जहां Chromium शुरू करने की लागत solve के सामने बहुत भारी पड़ जाती है।
v3 attribute पर एक चेतावनी। इसमें या तो URL होता है या खुद challenge document, इसलिए पास करने से पहले देख लें कि आपको क्या मिला है। अगर value किसी scheme के बजाय ब्रेस से शुरू होती है, तो वह inline challenge है, और उसकी जगह पिछले हिस्से वाला document option है।
result को type करना, अगर आप TypeScript पर हैं
definitions package के अंदर ही आती हैं, इसलिए कोई types package इंस्टॉल नहीं करना पड़ता। SDK जितने भी कैप्चा टाइप हल करता है, उन सबके लिए एक ही result type है, यानी जो field सिर्फ़ किसी एक की है उसे optional घोषित किया गया है। token और number ALTCHA के fields हैं, इसलिए compiler token को string या undefined मानता है और उसे ऐसी किसी जगह नहीं देने देगा जहां सीधी string चाहिए।
// npm install capskip
import { CapSkip, SolveResult, AltchaOptions } from 'capskip';
const options: AltchaOptions = { challengeUrl: found[1] };
const result: SolveResult = await solver.altcha(PAGE, options);
// One check, right after the call, and the type is settled.
if (!result.token) throw new Error('no ALTCHA token on this result');
const token: string = result.token;यही इशारा Turnstile user agent भी आपको देता है, देखें Node.js Turnstile गाइड, फ़र्क बस इतना है कि यहां नतीजा ज़्यादा तीखा है: user agent गायब हो तो आपका submit रिजेक्ट होता है, जबकि token गायब हो तो submit करने के लिए कुछ बचता ही नहीं। non-null assertion तभी इस्तेमाल करें जब आपको पूरा भरोसा हो, क्योंकि वह उसी एक जांच को चुप करा देती है जो बताती है कि गलत method कॉल हुई थी।
एक चीज़ ये types नहीं पकड़ेंगे। options interface में एक index signature है, इसलिए आप जो भी अतिरिक्त key लिखें उसे compiler मान लेता है। नतीजा यह कि गलत spelling वाला option बिना शिकायत build हो जाता है और फिर चलते वक्त फेल होता है, क्योंकि SDK ऐसे parameter को मना कर देता है जिसे ALTCHA लेता ही नहीं। ऊपर की तरह options object पर annotation लगाने से कम से कम वे keys तो जांच ली जाती हैं जिन्हें वह जानता है।
स्टेप 3: token को बिना बदले वापस पोस्ट करें, उसके समाप्त होने से पहले
widget अपना payload altcha नाम के फॉर्म फ़ील्ड में सबमिट करता है, इसलिए आपका token भी वहीं जाता है। यही वह स्टेप है जो चुपचाप टूट जाता है।
// Send it exactly as it came back: no trimming,
// no re-encoding, no reordering.
const response = await fetch('https://example.com/signup', {
method: 'POST',
body: new URLSearchParams({
email: '[email protected]',
altcha: token,
}),
});token एक JSON डॉक्यूमेंट का base64 है, जिसके फ़ील्ड सर्वर के अपने HMAC signature के दायरे में आते हैं। कोई भी बदलाव इसे अमान्य कर देता है, इसलिए सफ़ाई जैसी दिखने वाली हर चीज़ सबमिट को तोड़ देगी: व्हाइटस्पेस हटाना, इसे डिकोड करके फिर से एनकोड करना, या keys को अलग क्रम में रखकर JSON दोबारा बनाना। कुछ इंटीग्रेशन payload को फॉर्म फ़ील्ड के बजाय JSON बॉडी फ़ील्ड से पढ़ते हैं, इसलिए देखें कि पेज का अपना सबमिट क्या भेजता है और वैसा ही करें।
यह step दूसरे तरीके से, यानी समय की वजह से फेल होता है। challenge की खिड़की छोटी होती है और कुछ साइटें उसे दो मिनट के अंदर ही बंद कर देती हैं। जब एक expire हो जाता है, तो साइट जवाब को एक सूखी verification failure के साथ मना कर देती है, जो बिल्कुल गलत जवाब जैसी दिखती है, और response में ऐसा कुछ नहीं होता जो बताए कि दोनों में से हुआ क्या। तीन आदतें इससे बचाती हैं: challenge को लंबी run की शुरुआत में नहीं, बल्कि हल करने से ठीक पहले fetch करें, token को उसी काम की इकाई में submit करें जिसने उसे हल किया, और जब तक कोई इंसान form भर रहा हो तब तक token को कभी रोककर न रखें।
आपको रोकने वाली चीज़ क्लाइंट के अपने polling timeouts नहीं हैं, क्योंकि challenge की खिड़की इन दोनों में से किसी के भी पूरा होने से बहुत पहले बंद हो जाती है। ALTCHA ब्राउज़र session नहीं, CPU का काम है, इसलिए यह डिफ़ॉल्ट polling timeout पर चलता है, लंबे वाले reCAPTCHA timeout पर नहीं।
| कंस्ट्रक्टर ऑप्शन | डिफ़ॉल्ट | यह क्या कवर करता है |
|---|---|---|
| defaultTimeout | 120 सेकंड | ALTCHA और इमेज कैप्चा की पोलिंग |
| recaptchaTimeout | 300 सेकंड | reCAPTCHA, Turnstile और GeeTest की पोलिंग |
| pollingInterval | अधिकतम 5 सेकंड | पोलिंग 0.25 सेकंड से शुरू होती है और बढ़कर इस तक पहुँचती है |
स्टेप 4: solver कहाँ चलता है, और उसके लिए कौन सा कनेक्शन मोड चाहिए
ऊपर के samples में 127.0.0.1 इसलिए है क्योंकि जब आपका Node process और सॉल्वर एक ही मशीन पर हों तो यही सही है। जैसे ही call करने वाला code कहीं और चलता है, जैसे किसी container, CI runner, VPS या managed host पर, loopback सॉल्वर की ओर इशारा करना बंद कर देता है, और पहला ही solve एक NetworkException के साथ नकार दिया जाता है।
CapSkip को Server mode पर स्विच करें और वह इसके बजाय आपके network address या public IP पर सुनने लगता है, ताकि इनमें से कोई भी उसी HTTP API के ज़रिए उस तक पहुंच सके। जब रास्ता इंटरनेट से होकर जाए तो static public IP की सलाह दी जाती है, साथ में ऐसा firewall rule जो सिर्फ़ उन्हीं addresses को आने दे जिनकी आपको उम्मीद है। Server mode सिर्फ़ यह बदलता है कि सॉल्वर कहां सुनता है, और कुछ नहीं: यह अब भी आपका hardware है, और अब भी unmetered है। host और port को environment से पढ़ें ताकि एक ही build दोनों जगह काम करे। client CAPSKIP_HOST या CAPSKIP_PORT को अपने आप नहीं पढ़ता, इसलिए इन्हें constructor को पास करें, जैसा नीचे दिया गया पूरा उदाहरण करता है।
| Node process कहां चलता है | कौन-सा कनेक्शन मोड |
|---|---|
| CapSkip वाली मशीन पर, script या local server के रूप में | Local mode। 127.0.0.1 सचमुच सही है |
| उसी नेटवर्क पर किसी दूसरी मशीन पर | Server मोड, उस मशीन के प्राइवेट पते पर |
| किसी container में, VPS पर या managed platform पर | स्टैटिक पब्लिक IP और एक फ़ायरवॉल नियम के साथ Server mode |
प्रॉक्सी को लेकर ALTCHA से जुड़ी एक बात। यहाँ प्रॉक्सी सपोर्टेड है, लेकिन उसका इस्तेमाल सिर्फ़ challenge लाने के लिए होता है। रूट करने के लिए कोई ब्राउज़र सेशन होता ही नहीं, इसलिए खुद proof of work पर इसका कोई असर नहीं पड़ता।
पूरा चलने वाला उदाहरण
// npm install capskip
import { CapSkip, ApiException, TimeoutException, NetworkException } from 'capskip';
const solver = new CapSkip({
host: process.env.CAPSKIP_HOST || '127.0.0.1',
port: Number(process.env.CAPSKIP_PORT || 8080),
});
export async function signUp(email: string) {
try {
// Fetch, solve and submit in one unit of work.
const result = await solver.altcha('https://example.com/signup', {
challengeUrl: 'https://example.com/altcha/challenge',
});
if (!result.token) throw new Error('not an ALTCHA result');
const response = await fetch('https://example.com/signup', {
method: 'POST',
body: new URLSearchParams({ email, altcha: result.token }),
});
console.log(response.status, 'after counter', result.number);
} catch (err) {
// ERROR_CAPTCHA_UNSOLVABLE here means Argon2id or scrypt.
if (err instanceof ApiException) console.log('refused:', err.message);
else if (err instanceof TimeoutException) console.log('gave up waiting');
else if (err instanceof NetworkException) console.log('solver unreachable');
else throw err;
}
}बाकी टाइप भी इसी ढांचे के हैं, बस method अलग है। reCAPTCHA वाली call एक sitekey और एक page URL लेती है, Turnstile भी इसी तरह काम करता है, GeeTest page URL के साथ एक gt value और एक challenge लेता है, और image solving एक file path, एक URL या base64 लेता है। पूरी method सूची के लिए देखें Node.js कैप्चा सॉल्वर पेज, और यही methods हर आधिकारिक package में मौजूद हैं, देखें SDK पेज.
Turnstile इकलौता ऐसा टाइप है जिसे पूरे challenge page के रूप में आने पर sitekey से ज़्यादा की ज़रूरत पड़ती है। उसकी अतिरिक्त values के बारे में जानने के लिए देखें Node.js Turnstile गाइड.
आम errors और उनका मतलब
| आप जो देखते हैं | कारण | फिक्स |
|---|---|---|
| compiler token को मना कर देता है, यह कहते हुए कि string या undefined कोई string नहीं है | एक ही result type हर कैप्चा टाइप को कवर करता है, इसलिए सिर्फ़ ALTCHA वाले fields optional हैं | solve के बाद एक बार उसे narrow करें, फिर उसी narrow की हुई value का इस्तेमाल करें |
| गलत spelling वाला option बिना शिकायत build होता है और चलते वक्त फेल हो जाता है | options interface में एक index signature है, इसलिए अनजान keys भी निकल जाती हैं | options object पर ALTCHA options type की annotation लगाएं और spelling जांच लें |
| चलते वक्त token undefined दिखता है | वह field सिर्फ़ ALTCHA के लिए भरा जाता है | ALTCHA वाली method कॉल करें। ALTCHA के result में code field में वही string होती है |
| साइट से एक सूखी वेरिफ़िकेशन फेल्योर, जबकि token ठीक दिखता है | फॉर्म सबमिट होने से पहले ही challenge समाप्त हो गया | लाना, हल करना और सबमिट करना, तीनों एक ही काम की इकाई में करें |
| ApiException के अंदर ERROR_CAPTCHA_UNSOLVABLE, करीब एक तिहाई सेकंड में | challenge Argon2id या scrypt इस्तेमाल करता है | दोबारा कोशिश करने जैसा कुछ नहीं। वे दोनों जानबूझकर मना किए जाते हैं |
| कॉल पर एक ValidationException | कोई भी challenge ऑप्शन नहीं दिया गया, या ऐसा ऑप्शन पास किया गया जो ALTCHA नहीं लेता | challenge endpoint या challenge डॉक्यूमेंट पास करें, और बाकी सब हटा दें |
| पहले solve पर एक NetworkException | CapSkip चल नहीं रहा, या host और port गलत हैं | CapSkip शुरू करें, फिर देखें कि उसे Local मोड में होना चाहिए या Server मोड में |
| फॉर्म ऐसे token को ठुकरा देता है जिसे आपके लॉग हल हुआ दिखाते हैं | किसी चीज़ ने payload को दोबारा एनकोड किया, ट्रिम किया या उसका क्रम बदल दिया | स्ट्रिंग को जैसी है वैसी ही, बिना छेड़े आगे भेजें |
FAQ
क्या ALTCHA वाले page के लिए मुझे Puppeteer या Playwright चाहिए?
नहीं, और यही इसकी सबसे काम की बात है। ALTCHA देखने लायक कुछ नहीं, बल्कि एक hashing problem देता है, इसलिए काम सिर्फ़ CPU का है और milliseconds में पूरा हो जाता है। न ब्राउज़र लगता है, न WebDriver और न कोई user agent। global fetch वाली एक सादी script काफ़ी है, यानी यह किसी worker, queue consumer या serverless function के अंदर भी आराम से चलता है, जहां Chromium शुरू करना धीमा और झंझट भरा होता है।
क्या किसी hosted platform पर चल रहा Node app सॉल्वर तक पहुंच सकता है?
हां। connection settings में CapSkip को Server mode पर स्विच करें ताकि वह loopback के बजाय किसी network address पर सुने, फिर host वाले environment variable को उसी address पर लगा दें। container, CI runner, VPS या managed app platform, सब एक ही तरह से, उसी HTTP API के ज़रिए जुड़ते हैं। अगर रास्ता इंटरनेट से होकर जाता है तो static public IP इस्तेमाल करें, और उसे firewall rule से सीमित रखें। इन सब मामलों में सॉल्वर आपके अपने hardware पर ही रहता है, इसलिए लाइसेंस या हल की गिनती में कुछ नहीं बदलता।
क्या async client कई ALTCHA challenges को ज़्यादा तेज़ी से हल करता है?
अपने आप नहीं। Node package में async client सामान्य client का ही एक alias है, कोई दूसरा implementation नहीं, इसलिए उसे import करने से काम के तरीके में कुछ नहीं बदलता। हर method पहले से ही एक promise लौटाती है, इसलिए concurrency तब आती है जब आप कई calls एक साथ चलाकर पूरे सेट का await करें। ऐसा करते वक्त हर fetch को उसी के solve के साथ रखें, क्योंकि challenges अलग-अलग expire होते हैं और पहले से इकट्ठा किया गया batch तब तक बासी हो जाता है जब तक शुरुआती कुछ hash ही हो रहे होते हैं।
क्या SDK इस्तेमाल करने के लिए TypeScript ज़रूरी है?
नहीं। definitions package के अंदर ही आती हैं, इसलिए अगर आपका project उन्हें पढ़ता है तो वे मौजूद हैं और नहीं पढ़ता तो दिखती भी नहीं। सादा CommonJS ठीक वैसे ही चलता है जैसे पहले sample में दिखाया गया है, और फ़र्क बस इतना है कि optional token अब compiler की ज़िद नहीं, बल्कि चलते वक्त की ऐसी जांच बन जाता है जो आप खुद लिखते हैं। यह जांच दोनों हालात में लिखने लायक है, क्योंकि undefined token इस बात का सबसे साफ़ संकेत है कि गलत method कॉल हुई थी।
संक्षेप में
challenge endpoint को widget से पढ़ें, उसे page URL के साथ उस इकलौती ALTCHA method को दें, और token को बिना छुए altcha नाम वाले field में वापस भेज दें। typed project में solve के बाद token को एक बार narrow कर लें, क्योंकि एक ही result type हर कैप्चा टाइप को कवर करता है और उस पर ALTCHA के fields optional हैं। fetch, solve और submit को एक ही block में रखें, क्योंकि challenge की खिड़की दो मिनट के अंदर बंद हो सकती है और expire हो चुका challenge बिल्कुल गलत जवाब जैसा दिखता है। जिस पल Node process सॉल्वर वाली मशीन से अलग हो जाए, उसी पल Server mode पर स्विच कर दें।
- challenge क्या है और यह टाइप कैसे काम करता है: ALTCHA सॉल्वर पेज.
- Node package जो बाकी सारी methods देता है, वे यहां: Node.js सॉल्वर पेज.
आखिरी बात, जो आपके retry के डिज़ाइन को बदल देती है। क्योंकि एक असीमित कैप्चा सॉल्वर proof of work उसी मशीन पर करता है जो पहले से आपकी अपनी है, इसलिए समाप्त हो चुके challenge को दोबारा आज़माने में आपके अपने CPU के कुछ मिलीसेकंड के अलावा कुछ खर्च नहीं होता, इसलिए आप बासी challenge को सँभालने के बजाय नया challenge लाने की छूट रख सकते हैं।
