معرفی
Captchas.ir یک کنترلپلین کپچای خودمیزبان است یک باینری Go، API عمومی و مدیریت، ویجتها و پنل Vue را سرو میکند مسیر عمومی سایت از پنل /panel/ جداست
بکاند intent میسازد، مرورگر چالش موتور انتخابشده را حل میکند و بکاند رسید را با /v1/siteverify مصرف میکند نمونههای این راهنما برای Cap هستند، موتورهای دیگر renderer و قرارداد مستقل دارند
داراییهای وب خودمیزبان هستند تماس با موتور به پیکربندی همان موتور وابسته است PostgreSQL 18 و SQLite شواهد آزمون محلی دارند؛ ماتریس تازهٔ PostgreSQL 16 و همهٔ شیوههای استقرار هنوز کامل نیست
وضعیت و محدوده
| بخش | وضعیت | مرز ادعا |
|---|---|---|
| جریان intent تا مصرف رسید | پیادهسازیشده | اعتبار بکاند، اتصال به درخواست و مصرف اتمیک |
| موتورها و مسیریابی | وابسته به محیط | پیادهسازی، پیکربندی، سلامت و آزمون واقعی وضعیتهای جدا هستند |
| مدیریت و آمار | پیادهسازیشده | نشست مدیر، TOTP و کنترل CSRF؛ ایمیل به SMTP نیاز دارد |
| تجارت و پلنها | تکمیلنشده | پرداخت، سهمیه، صورتحساب و تعهد SLA محصول کامل نیستند |
| افزونهها و SDK | بهزودی | نمونهٔ اتصال، SDK رسمی یا افزونهٔ منتشرشده نیست |
نسخهٔ طرح دادهٔ فعلی 11 است وضعیت موتورهای همین محیط را از پنل و /v1/capabilities بخوانید؛ وجود پیادهسازی یا health موفق، آزمون حل واقعی نیست
شروع سریع
برای ساخت به Go مطابق go.mod نیاز دارید. پنل از قبل ساخته شده و داخل باینری قرار میگیرد
# 1. ساخت
go build -o digicaptcha ./cmd/digicaptcha
# 2. کلید داده: دقیقاً 32 بایت با base64 استاندارد، یک بار بسازید و نگه دارید
install -d -m 700 /etc/digicaptcha /var/lib/digicaptcha
umask 077
if [ ! -f /etc/digicaptcha/data.key ]; then
openssl rand -base64 32 > /etc/digicaptcha/data.key
fi
# 3. پیکربندی
export DIGICAPTCHA_DB=/var/lib/digicaptcha/digicaptcha.db
export DIGICAPTCHA_DATA_KEY="$(cat /etc/digicaptcha/data.key)"
export DIGICAPTCHA_ADMIN_USER=owner
read -r -s -p "Admin password: " DIGICAPTCHA_ADMIN_PASSWORD
export DIGICAPTCHA_ADMIN_PASSWORD
# 4. اجرا (پیشفرض 127.0.0.1:8088)
./digicaptcha -listen 127.0.0.1:8088
# 5. بررسی
curl -s 127.0.0.1:8088/livez # {"stage":"m1","status":"live"}
curl -s 127.0.0.1:8088/readyz # {"stage":"m1-sqlite","status":"ready"}
سپس 127.0.0.1:8088/panel/ را باز کنید و با همان نام کاربری و گذرواژه وارد شوید. مسیرهای /panel و /dashboard هم به پنل هدایت میشوند
کلید داده را گم نکنید رمزهای ذخیرهشده و پاسخهای idempotency با AES-GCM و همین کلید رمز میشوند. کلید تازه یعنی دادههای قبلی قابل خواندن نیستند
بدون DIGICAPTCHA_DB فرایند در حالت foundation بالا میآید: /readyz و همهٔ POSTهای عمومی خطای 503 با کد ADAPTERS_NOT_IMPLEMENTED میدهند و هیچ تأییدی ساخته نمیشود
متغیرهای محیطی
همهٔ تنظیمات از متغیر محیطی خوانده میشوند. تنها پرچم خط فرمان -listen است با مقدار پیشفرض 127.0.0.1:8088
| متغیر | کاربرد |
|---|---|
DIGICAPTCHA_DB | مقدار شروعشونده با postgres:// یا postgresql:// یعنی PostgreSQL؛ هر مقدار دیگر مسیر فایل SQLite است. خالی یعنی حالت foundation |
DIGICAPTCHA_DATA_KEY | الزامی همراه با پایگاه. base64 استاندارد برای دقیقاً 32 بایت؛ در غیر این صورت فرایند اجرا نمیشود |
DIGICAPTCHA_ADMIN_USERDIGICAPTCHA_ADMIN_PASSWORD | حساب مالک را هنگام شروع میسازد یا گذرواژهاش را بهروز میکند (argon2id). اگر گذرواژه از پنل عوض شده باشد، گذرواژهٔ پنل در شروعهای بعدی میماند |
DIGICAPTCHA_PASSWORD_RESET | مقدار 1 در /etc/digicaptcha/env گذرواژهٔ DIGICAPTCHA_ADMIN_PASSWORD را حتی پس از تغییر از پنل دوباره اعمال میکند، قفل ورود را برمیدارد و نشستها را میبندد. متغیر پوسته دیده نمیشود. پس از بازیابی حذفش کنید. رمز دوم با این پرچم پاک نمیشود |
DIGICAPTCHA_BOOTSTRAP_TOKEN | اختیاری. توکن یکبارمصرف برای POST /admin/v1/session/bootstrap وقتی حساب مدیر با گذرواژه ندارید |
DIGICAPTCHA_COOKIE_SECURE | مقدار true پرچم Secure را روی کوکیهای نشست و CSRF میگذارد. پشت HTTPS الزامی است |
DIGICAPTCHA_MFA_RESET | مقدار 1 همراه با DIGICAPTCHA_ADMIN_USER رمز دوم مالک را هنگام شروع پاک میکند. پس از بازیابی حذفش کنید |
DIGICAPTCHA_CAP_ENDPOINTDIGICAPTCHA_CAP_ADMIN_KEY | آدرس Cap Standalone محلی (فقط http یا https، بدون مسیر) و کلید مدیریت آن (حداقل 12 نویسه). پنل سلامت Cap را بررسی میکند و سایتها بدون واردکردن دوبارهٔ کلید به آن وصل میشوند |
DIGICAPTCHA_SMTP_HOSTDIGICAPTCHA_SMTP_PORTDIGICAPTCHA_SMTP_USERDIGICAPTCHA_SMTP_PASSWORDDIGICAPTCHA_SMTP_FROM | ارسال کد ایمیلی رمز دوم. فقط وقتی HOST و FROM هر دو تنظیم شوند فعال است؛ درگاه پیشفرض 587 |
DIGICAPTCHA_MCP_LISTEN | اختیاری. نشانی IP:port که IP آن باید loopback باشد، مثل 127.0.0.1:8090. نامهایی مثل localhost پذیرفته نمیشوند. خالی یعنی خاموش |
DIGICAPTCHA_MCP_TOKEN | با پایگاه داده اختیاری است چون توکن را میشود در پنل ساخت، بدون پایگاه الزامی است؛ 16 تا 128 نویسه بدون فاصله. کلاینت آن را در Authorization: Bearer میفرستد |
پشت nginx
Captchas.ir را روی loopback نگه دارید و فقط nginx را در اینترنت قرار دهید. Captchas.ir هدر X-Forwarded-For را فقط وقتی میپذیرد که درخواست از loopback آمده باشد، پس nginx باید آن را با $remote_addr بازنویسی کند تا نشانی واقعی بازدیدکننده به Cap برسد
server {
listen 443 ssl;
http2 on;
server_name captcha.example.com;
ssl_certificate /etc/ssl/captcha.example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/captcha.example.com/privkey.pem;
# Captchas.ir bodies are capped at 64 KiB anyway
client_max_body_size 64k;
location / {
proxy_pass http://127.0.0.1:8088;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_read_timeout 20s;
}
}
server {
listen 80;
server_name captcha.example.com;
return 301 https://captcha.example.com$request_uri;
}
DIGICAPTCHA_COOKIE_SECURE=trueرا تنظیم کنید- هدر
Originمرورگر باید دستنخورده به Captchas.ir برسد؛ nginx بهطور پیشفرض همین کار را میکند - Cap Standalone را منتشر نکنید. فقط Captchas.ir باید به آن دسترسی داشته باشد
سایت و کلید در پنل
- ورود و رمز دوم در
/panel/وارد شوید و از بخش امنیت یک اپ TOTP یا ایمیل (در صورت تنظیم SMTP) را فعال کنید - سایت تازه نام، محیط (مثلاً
production) و مبدأهای مجاز را وارد کنید. هر مبدأ کامل است، مثلhttps://shop.example.com - کلید عمومی در صفحهٔ سایت یک کلید عمومی بسازید. این مقدار در مرورگر و در هدر
X-DigiCaptcha-Site-Keyاستفاده میشود و راز نیست - اعتبار بکاند اعتباری با scopeهای
intents:createوreceipts:verifyبسازید. مقدار کامل به شکلdck_<prefix>_<secret>فقط همین یک بار نمایش داده میشود؛ پس از آن فقط پیشوند هشتنویسهای دیده میشود - اتصال Cap سایت را به Cap وصل کنید. اگر
DIGICAPTCHA_CAP_ENDPOINTتنظیم شده باشد نیازی به واردکردن نشانی و کلید نیست. Captchas.ir یک site key در Cap میسازد با instrumentation روشن، سختی پیشفرض1000000و سطح مبهمسازی 3 - آزمایش از صفحهٔ سایت به
/playground/بروید تا کل جریان در مرورگر اجرا شود و یک siteverify واقعی ببینید
اعتبار بکاند و رمز نمونهٔ Cap هر دو قابل چرخشاند. اعتبار تازه همان scopeها را میگیرد و مقدار جدید باز هم فقط یک بار نمایش داده میشود
یک سایت برای هر دامنه
برای هر دامنهای که کپچا میخواهد یک سایت جدا بسازید، مثلاً سایت Pulse با مبدأ https://pulse.digitalvps.ir و سایت BGP Map با مبدأ https://bgpmap.digitalvps.ir. هر سایت کلید عمومی، کلید سرور و نمونهٔ Cap خودش را دارد و کلید یک سایت روی مبدأ سایت دیگر کار نمیکند
- در
/panel/sites/newنام، محیط و مبدأ دامنه را وارد کنید، بدون مسیر و بدون اسلش پایانی - در صفحهٔ سایت، بخش «راهاندازی این سایت» چهار گام را نشان میدهد: مبدأ، کلید عمومی، کلید سرور و نمونهٔ Cap
- کلید عمومی را در HTML صفحه و کلید سرور را فقط در تنظیم بکاند همان دامنه بگذارید
- دکمهٔ «آزمون در مرورگر» از خود پنل اجرا میشود و لازم نیست نشانی پنل را به مبدأهای سایت اضافه کنید
مدیریت کلید و سایت
- به هر کلید عمومی و کلید سرور میشود برچسب داد، مثلاً «فرم ثبتنام»
- هر کلید را میشود جدا باطل کرد، بدون اینکه کلید تازهای جایش صادر شود
- دسترسیهای کلید سرور قابل ویرایش است و دسترسی کمشده از درخواست بعدی اعمال میشود، خود راز فقط با چرخش عوض میشود
- راز تازه پوشیده نمایش داده میشود، با دکمهٔ چشم دیده میشود و کادر آن پس از دو دقیقه بسته میشود
- حذف سایت نیاز به نوشتن دقیق نام سایت دارد، همهٔ کلیدها و نمونههای آن همان لحظه از کار میافتند و سابقهٔ ممیزی میماند
جریان تأیید
Backend Browser Captchas.ir Cap (loopback)
| | | |
|-- POST /v1/intents (Bearer) ----------------->| |
|<-------------------------------- intent_id ---| |
|-- intent_id ---->| | |
| |-- POST /v1/sessions ------>| (X-DigiCaptcha-Site-Key) |
| |<- session_id, completion --| |
| |-- .../cap/challenge ------>|-- challenge --------------->|
| |<---------------------------|<---------------------------|
| | (solve proof-of-work) | |
| |-- .../cap/redeem --------->|-- redeem ------------------>|
| |-- .../complete ----------->|-- siteverify (secret) ----->|
| |<---------------- receipt --| |
|<-- receipt ------| | |
|-- POST /v1/siteverify (Bearer) -------------->| consume once, atomically |
|<------------------------------ success:true --| |
- intent بین 30 تا 600 ثانیه عمر دارد (پیشفرض 300)
- نشست حداکثر 5 دقیقه و هرگز بیشتر از عمر باقیماندهٔ intent است
- رسید 2 دقیقه اعتبار دارد و فقط یک بار مصرف میشود
- پاسخ challenge و redeem موفقیت نیستند؛ فقط complete رسید میدهد و فقط siteverify آن را تأیید میکند
ویجت مرورگر
Captchas.ir دو فایل برای مرورگر سرو میکند: /widget/cap.js که جریان را مدیریت میکند و نسخهٔ رسمی ویجت Cap در /widget/vendor/cap.min.js همراه با فایلهای WASM آن. ترتیب بارگذاری مهم است و cap.js باید اول بیاید تا Cap فایلهای WASM را از همین مبدأ بگیرد، نه از CDN
<form id="signup" method="post" action="/signup">
<input name="email" type="email" required>
<div id="captcha"></div>
<input type="hidden" name="dc_intent">
<input type="hidden" name="dc_receipt">
<button>ثبتنام</button>
</form>
<script src="https://captcha.example.com/widget/cap.js" nonce="RANDOM"></script>
<script src="https://captcha.example.com/widget/vendor/cap.min.js" nonce="RANDOM"></script>
<script src="/js/signup.js" nonce="RANDOM"></script>
const API = "https://captcha.example.com";
const SITE_KEY = "PUBLIC_SITE_KEY_FROM_PANEL";
const form = document.getElementById("signup");
async function startCaptcha() {
// 1. Your backend creates the intent; it holds the credential.
const intent = await fetch("/captcha/intent", { method: "POST" }).then((r) => r.json());
// 2. Open a Captchas.ir session with the public site key.
const res = await fetch(API + "/v1/sessions", {
method: "POST",
credentials: "omit",
headers: {
"content-type": "application/json",
"x-digicaptcha-site-key": SITE_KEY,
"idempotency-key": "sess-" + crypto.randomUUID(),
},
body: JSON.stringify({ intent_id: intent.intent_id, locale: "fa" }),
});
const session = await res.json();
if (!res.ok) throw new Error(session.error.code);
// 3. Render Cap; resolves with the Captchas.ir receipt.
const done = await DigiCaptcha.mountCap({
apiBase: API,
siteKey: SITE_KEY,
sessionId: session.session_id,
completionToken: session.completion_token,
container: document.getElementById("captcha"),
lang: "fa",
});
form.elements.dc_intent.value = done.intent_id;
form.elements.dc_receipt.value = done.receipt;
}
startCaptcha().catch((err) => console.error("captcha", err));
DigiCaptcha.mountCap(options)
ویجت رسمی Cap را داخل container میسازد، درخواستهای challenge و redeem آن را از مسیر نشست Captchas.ir عبور میدهد و پس از حل، /complete را صدا میزند. Promise آن با پاسخ complete یعنی {receipt, receipt_expires_at, intent_id, session_id} حل میشود
siteKey- کلید عمومی سایت، الزامی
sessionId- مقدار
session_idازPOST /v1/sessions، الزامی completionToken- مقدار
completion_tokenهمان پاسخ، الزامی container- عنصر DOM که ویجت داخلش قرار میگیرد، الزامی
apiBase- مبدأ Captchas.ir؛ پیشفرض مبدأ همان فایل
cap.js lang- مقدار
"fa"برچسبهای فارسی ویجت را فعال میکند labels- بازنویسی برچسبها با کلیدهای Cap مثل
initial-state autoSolve- نادیده گرفته میشود. حل فقط بعد از کلیک خود کاربر روی ویجت شروع میشود
DigiCaptcha.runCap(options)
برای حلکنندهٔ اختصاصی یا آزمون، بدون ویجت رسمی. گزینهها: apiBase، siteKey، sessionId، completionToken، idempotency: {challenge, redeem, complete} و تابع solve(challenge) که باید {token, solutions} برگرداند. خروجی همان پاسخ complete است. با تعریف globalThis.DigiCaptchaTransport میتوانید fetch را جایگزین کنید
CSP صفحهٔ میزبان
ویجت Cap برای proof-of-work از WebAssembly و Web Worker استفاده میکند و استایل خودش را با nonce تزریق میکند cap.js مقدار nonce تگ اسکریپت خودش را به Cap میدهد. سیاستی شبیه این، که از صفحهٔ playground خود Captchas.ir گرفته شده، کافی است:
script-src 'self' https://captcha.example.com 'nonce-RANDOM' 'wasm-unsafe-eval' 'unsafe-eval';
style-src 'self' 'nonce-RANDOM';
worker-src 'self' blob:;
connect-src 'self' https://captcha.example.com;
frame-src 'self'
اگر Captchas.ir را روی همان مبدأ سایت (مثلاً با یک مسیر در nginx) سرو کنید، ارجاع به captcha.example.com لازم نیست. در مرورگرهایی که DecompressionStream ندارند، Cap نمیتواند instrumentation را باز کند و چالش عمداً شکست میخورد؛ هیچ کتابخانهای از CDN بارگذاری نمیشود
Prosopo OSS
در پنل، یک سایت با موتور Prosopo و یکی از حالتهای pow، puzzle، iconOrder یا image بسازید و مبدا دقیق سایت مشتری را ثبت کنید
بکاند شما intent را میسازد و مرورگر پاسخ عادی ایجاد session را در نمونهٔ زیر استفاده میکند، کلید محرمانه و اطلاعات ثبت موتور باید فقط روی سرور بمانند
<script src="https://captchas.ir/widget/engines.js"></script>
const controller = new AbortController()
const result = await DigiCaptcha.mountProsopo({
apiBase: 'https://captchas.ir',
siteKey: YOUR_PUBLIC_SITE_KEY,
prosopoSiteKey: session.challenge.public_config.prosopo_site_key,
sessionId: session.session_id,
completionToken: session.completion_token,
container: document.getElementById('captcha'),
lang: 'fa',
signal: controller.signal
})
// Send result.receipt to your backend for /v1/siteverify
// controller.abort() cancels this widget onlyفقط پس از تایید و مصرف یکبارهٔ رسید در بکاند، عملیات محافظتشده را انجام دهید، خطای شبکه به معنی موفقیت نیست و یک نشست نباید به موتور دیگری منتقل شود
سه حالت تصویری، جایگزین صوتی با ارقام گفتاری انگلیسی دارند، صدای فارسی و تشخیص رفتاری تجاری Prosopo در این اتصال ارائه نمیشوند، عنوان برخی تصاویر اختصاصی هنوز انگلیسی است
باندل مرورگر و فایلهای WASM از همین سرویس بارگیری میشوند، آداپتور استایل از nonce اسکریپت معتبر صفحه استفاده میکند، برای صوت محلی media-src را به self و data محدود کنید
تأیید در بکاند
بکاند دو درخواست میزند و هر دو با اعتبار بکاند و Idempotency-Key. اعتبار هرگز نباید به مرورگر برسد
1. ساخت intent
curl -sS https://captcha.example.com/v1/intents \
-H "Authorization: Bearer $DIGICAPTCHA_CREDENTIAL" \
-H "Idempotency-Key: intent-$(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"site_id": "SITE_ID",
"action": "signup",
"request_hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"binding_version": "jcs-v1",
"expires_in_seconds": 300
}'
# 201 Created
{"intent_id":"4b7c0d1e2f3a4b5c6d7e8f9a0b1c2d3e","action":"signup","expires_at":"2026-10-25T08:05:00Z"}
request_hash را بکاند شما محاسبه میکند و Captchas.ir فقط آن را مقایسه میکند. قالب آن sha256: و 64 نویسهٔ hex کوچک است binding_version فعلاً فقط jcs-v1 است که به شکل canonical در RFC 8785 اشاره دارد؛ مهم این است که هنگام ساخت intent و هنگام siteverify دقیقاً همان بایتها را hash کنید
2. مصرف رسید
curl -sS https://captcha.example.com/v1/siteverify \
-H "Authorization: Bearer $DIGICAPTCHA_CREDENTIAL" \
-H "Idempotency-Key: verify-4b7c0d1e2f3a4b5c6d7e8f9a0b1c2d3e" \
-H "Content-Type: application/json" \
-d '{
"receipt": "RECEIPT_FROM_FORM",
"intent_id": "4b7c0d1e2f3a4b5c6d7e8f9a0b1c2d3e",
"action": "signup",
"request_hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}'
# 200 OK
{"success":true,"verification_id":"…","intent_id":"4b7c…","site_id":"…","action":"signup",
"request_hash":"sha256:9f86…","verified_at":"2026-10-25T08:01:12Z"}
قاعدهٔ طلایی فقط وقتی ادامه دهید که پاسخ 200 باشد و success برابر true. هر وضعیت دیگر، از جمله timeout و خطای شبکه، یعنی رد
نمونه برای PHP، Python، Node و Go در صفحهٔ یکپارچهسازی آمده است
مرجع HTTP API
مرجع در صفحهٔ مستقل با جزئیات درخواست، نشست و مصرف رسید قرار دارد
خطاها
همهٔ خطاها یک شکل دارند:
{
"error": {
"code": "ALREADY_CONSUMED",
"message": "This receipt was already consumed.",
"request_id": "5f0c2a9e8b7d4c3e1a2b3c4d5e6f7a8b",
"retryable": false
}
}
| HTTP | code | معنی |
|---|---|---|
| 400 | INVALID_BODY · INVALID_REQUEST | JSON نامعتبر، فیلد ناشناخته یا اعتبارسنجی ناموفق، از جمله نبود Idempotency-Key |
| 401 | UNAUTHENTICATED | اعتبار، کلید یا نشست وجود ندارد یا معتبر نیست |
| 403 | FORBIDDEN | scope ناکافی، مبدأ غیرمجاز یا CSRF نادرست |
| 403 | BINDING_MISMATCH | رسید یا intent به سایت، action یا نصب دیگری تعلق دارد |
| 404 | NOT_FOUND | منبع در این tenant نیست |
| 409 | ALREADY_CONSUMED | رسید قبلاً مصرف شده است |
| 409 | IDEMPOTENCY_CONFLICT | همان کلید با بدنهٔ متفاوت |
| 409 | OPERATION_IN_PROGRESS | همان عملیات هنوز در جریان است؛ retryable: true |
| 409 | CONFLICT | منبع در وضعیتی نیست که این عملیات را بپذیرد |
| 410 | SESSION_EXPIRED | intent، نشست یا رسید منقضی شده است |
| 413 | BODY_TOO_LARGE | بدنه بیش از 64 KiB |
| 400 | CAPTCHA_REQUIRED | کپچای ورود پنل روشن است و رسید آن فرستاده نشده یا پذیرفته نشد |
| 422 | INVALID_PROOF | Cap توکن را رد کرد یا توکن مال سایت Cap دیگری است |
| 502 | UPSTREAM_REJECTED | Cap درخواست مدیریتی را رد کرد |
| 503 | UPSTREAM_UNAVAILABLE | Cap برای challenge یا redeem در دسترس نبود؛ retryable: true |
| 503 | UPSTREAM_RESULT_UNKNOWN | نتیجهٔ تأیید نامعلوم است؛ رسیدی صادر نشده و نباید با همان proof تکرار شود |
| 503 | NO_ELIGIBLE_PROVIDER | هیچ موتور واجد شرایط و فعالی برای این سایت در دسترس نیست |
| 503 | STORAGE_UNAVAILABLE | پایگاه داده در دسترس نیست؛ retryable: true |
| 429 | RATE_LIMITED | درخواست ورود یا نشست مرورگر از یک نشانی زیاد است، یا سقف نشست سایت برای هر IP پر شده؛ به اندازهٔ Retry-After ثانیه صبر کنید |
| 503 | MAIL_NOT_CONFIGURED | SMTP تنظیم نشده است |
| 503 | ADAPTERS_NOT_IMPLEMENTED | حالت foundation، بدون پایگاه |
| 500 | INTERNAL_ERROR | خطای داخلی؛ عملیات بسته شکست خورد |
Idempotency
- برای هر عملیات منطقی یک کلید تصادفی بسازید و در تلاشهای مجدد همان عملیات، همان کلید را بفرستید
- همان کلید با همان بدنه، پاسخ ذخیرهشده را برمیگرداند. این مصرف دوم رسید نیست و موفقیت تازه نمیسازد
- همان کلید با بدنهٔ متفاوت
409 IDEMPOTENCY_CONFLICTمیگیرد - اگر عملیات قبلی هنوز در جریان باشد
409 OPERATION_IN_PROGRESSبرمیگردد - پس از
UPSTREAM_RESULT_UNKNOWNکور تلاش نکنید. یک intent و نشست تازه بسازید
پایش و MCP
صفحهٔ پایش پنل از GET /admin/v1/monitor تغذیه میشود:
| فیلد | معنی |
|---|---|
engine · schema · ping_ms | نوع پایگاه، نسخهٔ طرحواره و تأخیر ping |
pool | اتصالهای open، in_use و idle |
counts | تعداد sites، users و receipts |
mail_configured | آیا SMTP تنظیم شده است |
cap | state برابر running، unreachable یا contract_tested_not_live_cap، بهعلاوهٔ latency_ms، version و commit |
local | میزبان پایگاه، captcha_upstreams: 0، cap_loopback، panel_assets: "origin" و وضعیت mcp |
شنوندهٔ MCP اختیاری است و جدا از درگاه اصلی روی همان نشانی loopback که در DIGICAPTCHA_MCP_LISTEN دادهاید باز میشود. دو ابزار فقطخواندنی digicaptcha_status و digicaptcha_traffic (شمارش امروز هر سایت، کشورهای هفت روز و بیستوچهار ساعت اخیر) دارد (Streamable HTTP). هر درخواست باید Authorization: Bearer با توکنی داشته باشد که در پنل بخش تنظیمات ساخته شده، یا DIGICAPTCHA_MCP_TOKEN از فایل تنظیم. این شنونده SQL، shell یا درخواست بیرونی ندارد
نکات امنیتی
- اعتبار بکاند (
dck_…) را فقط در سرور نگه دارید. اگر لو رفت، از پنل بچرخانید - هر سایت فقط مبدأهای دقیق خودش را داشته باشد. درخواست مرورگر بدون Origin یا با Origin دیگر رد میشود
- Cap Standalone و درگاه MCP را فقط روی loopback نگه دارید
- پشت TLS مقدار
DIGICAPTCHA_COOKIE_SECURE=trueرا فراموش نکنید - رمز دوم را برای حساب مالک فعال کنید و
DIGICAPTCHA_MFA_RESETرا پس از استفاده حذف کنید - کلید داده و پشتیبان پایگاه را جدا از هم نگه دارید
- در بکاند، timeout یا خطای شبکه به siteverify را هرگز قبول تلقی نکنید
- رسیدها فقط بهصورت hash ذخیره میشوند و ویجت با
credentials: "omit"درخواست میدهد
عیبیابی
| نشانه | علت محتمل |
|---|---|
فرایند با پیام DIGICAPTCHA_DATA_KEY must be standard base64… خارج میشود | کلید 32 بایتی نیست یا base64 استاندارد نیست. از openssl rand -base64 32 استفاده کنید |
/readyz خطای 503 میدهد | بدون DIGICAPTCHA_DB در حالت foundation هستید یا پایگاه در دسترس نیست |
| پس از ورود دوباره به صفحهٔ ورود برمیگردید | DIGICAPTCHA_COOKIE_SECURE=true روی HTTP ساده؛ مرورگر کوکی Secure را ذخیره نمیکند. آن را فقط پشت HTTPS روشن کنید |
هر تغییر در API مدیریت 403 FORBIDDEN میگیرد | هدر X-CSRF-Token با مقدار کوکی digicaptcha_csrf همین نشست فرستاده نشده است |
POST /v1/sessions خطای 403 میدهد | Origin صفحه در مبدأهای مجاز سایت نیست، یا کلید عمومی مال سایت دیگری است |
خطای NO_ELIGIBLE_PROVIDER | سایت هنوز به نمونهٔ Cap وصل نشده است |
| وضعیت Cap در پایش «unreachable» است | Cap روی DIGICAPTCHA_CAP_ENDPOINT پاسخ health نمیدهد |
| ویجت بارگذاری نمیشود یا WASM خطا میدهد | ترتیب اسکریپتها یا CSP صفحه cap.js باید قبل از cap.min.js بیاید |
siteverify خطای BINDING_MISMATCH میدهد | action، intent یا request_hash با زمان ساخت intent یکی نیست، یا اعتبار مال سایت دیگری است |
| کد ایمیلی نمیرسد | DIGICAPTCHA_SMTP_HOST یا DIGICAPTCHA_SMTP_FROM تنظیم نشده است |
رمز پنل فراموش شده و DIGICAPTCHA_PASSWORD_RESET=1 اثری ندارد | خط را در /etc/digicaptcha/env بگذارید و systemctl restart digicaptcha بزنید. گزارش باید admin password reset from environment باشد. رمز همان DIGICAPTCHA_ADMIN_PASSWORD است |
| دسترسی به رمز دوم از دست رفته است | یک بار DIGICAPTCHA_MFA_RESET=1 را هم در /etc/digicaptcha/env بگذارید و سرویس را راه بیندازید، بعد حذفش کنید |
مجوزها
- رابط پنل از GateCHA (MIT) مشتق شده است
- ویجت و فایلهای WASM موتور Cap از Cap Standalone (Apache-2.0) بدون تغییر بستهبندی شدهاند
- فونت وزیرمتن تحت SIL Open Font License 1.1 است
- پرچمها از flag-icons نسخهٔ 7.6.0 با مجوز MIT هستند
- نقشهٔ جهان از world-atlas 2.0.2 (Natural Earth 1:110m، مالکیت عمومی؛ بسته با مجوز ISC) ساخته شده است
- یافتن کشور از دادهٔ IP2Location LITE داخل باینری است و درخواستی بیرون نمیرود. This site or product includes IP2Location LITE data available from https://lite.ip2location.com