مستندات و شروع اتصال

کلید محرمانه فقط در بک‌اند، بررسی نتیجه با قرارداد سایت

کلید محرمانه فقط در بک‌اند، بررسی نتیجه با قرارداد سایت

کلید محرمانه فقط در بک‌اند، بررسی نتیجه با قرارداد سایت

کلید محرمانه فقط در بک‌اند، بررسی نتیجه با قرارداد سایت

کلید محرمانه فقط در بک‌اند، بررسی نتیجه با قرارداد سایت

کلید محرمانه فقط در بک‌اند، بررسی نتیجه با قرارداد سایت

Captchas.irمرکز توسعه‌دهندگان

از آماده‌سازی سرویس تا اولین تأیید سمت سرور با راهنمای روشن برای کلیدها، ویجت و مرزهای امنیتی

فهرست این صفحه در این صفحه

معرفی

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 نیاز دارید. پنل از قبل ساخته شده و داخل باینری قرار می‌گیرد

shell
# 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_USER
DIGICAPTCHA_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_ENDPOINT
DIGICAPTCHA_CAP_ADMIN_KEY
آدرس Cap Standalone محلی (فقط http یا https، بدون مسیر) و کلید مدیریت آن (حداقل 12 نویسه). پنل سلامت Cap را بررسی می‌کند و سایت‌ها بدون واردکردن دوبارهٔ کلید به آن وصل می‌شوند
DIGICAPTCHA_SMTP_HOST
DIGICAPTCHA_SMTP_PORT
DIGICAPTCHA_SMTP_USER
DIGICAPTCHA_SMTP_PASSWORD
DIGICAPTCHA_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 برسد

/etc/nginx/sites-available/digicaptcha.conf
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 باید به آن دسترسی داشته باشد

سایت و کلید در پنل

  1. ورود و رمز دوم در /panel/ وارد شوید و از بخش امنیت یک اپ TOTP یا ایمیل (در صورت تنظیم SMTP) را فعال کنید
  2. سایت تازه نام، محیط (مثلاً production) و مبدأهای مجاز را وارد کنید. هر مبدأ کامل است، مثل https://shop.example.com
  3. کلید عمومی در صفحهٔ سایت یک کلید عمومی بسازید. این مقدار در مرورگر و در هدر X-DigiCaptcha-Site-Key استفاده می‌شود و راز نیست
  4. اعتبار بک‌اند اعتباری با scopeهای intents:create و receipts:verify بسازید. مقدار کامل به شکل dck_<prefix>_<secret> فقط همین یک بار نمایش داده می‌شود؛ پس از آن فقط پیشوند هشت‌نویسه‌ای دیده می‌شود
  5. اتصال Cap سایت را به Cap وصل کنید. اگر DIGICAPTCHA_CAP_ENDPOINT تنظیم شده باشد نیازی به واردکردن نشانی و کلید نیست. Captchas.ir یک site key در Cap می‌سازد با instrumentation روشن، سختی پیش‌فرض 1000000 و سطح مبهم‌سازی 3
  6. آزمایش از صفحهٔ سایت به /playground/ بروید تا کل جریان در مرورگر اجرا شود و یک siteverify واقعی ببینید

اعتبار بک‌اند و رمز نمونهٔ Cap هر دو قابل چرخش‌اند. اعتبار تازه همان scopeها را می‌گیرد و مقدار جدید باز هم فقط یک بار نمایش داده می‌شود

یک سایت برای هر دامنه

برای هر دامنه‌ای که کپچا می‌خواهد یک سایت جدا بسازید، مثلاً سایت Pulse با مبدأ https://pulse.digitalvps.ir و سایت BGP Map با مبدأ https://bgpmap.digitalvps.ir. هر سایت کلید عمومی، کلید سرور و نمونهٔ Cap خودش را دارد و کلید یک سایت روی مبدأ سایت دیگر کار نمی‌کند

  1. در /panel/sites/new نام، محیط و مبدأ دامنه را وارد کنید، بدون مسیر و بدون اسلش پایانی
  2. در صفحهٔ سایت، بخش «راه‌اندازی این سایت» چهار گام را نشان می‌دهد: مبدأ، کلید عمومی، کلید سرور و نمونهٔ Cap
  3. کلید عمومی را در HTML صفحه و کلید سرور را فقط در تنظیم بک‌اند همان دامنه بگذارید
  4. دکمهٔ «آزمون در مرورگر» از خود پنل اجرا می‌شود و لازم نیست نشانی پنل را به مبدأهای سایت اضافه کنید

مدیریت کلید و سایت

  • به هر کلید عمومی و کلید سرور می‌شود برچسب داد، مثلاً «فرم ثبت‌نام»
  • هر کلید را می‌شود جدا باطل کرد، بدون این‌که کلید تازه‌ای جایش صادر شود
  • دسترسی‌های کلید سرور قابل ویرایش است و دسترسی کم‌شده از درخواست بعدی اعمال می‌شود، خود راز فقط با چرخش عوض می‌شود
  • راز تازه پوشیده نمایش داده می‌شود، با دکمهٔ چشم دیده می‌شود و کادر آن پس از دو دقیقه بسته می‌شود
  • حذف سایت نیاز به نوشتن دقیق نام سایت دارد، همهٔ کلیدها و نمونه‌های آن همان لحظه از کار می‌افتند و سابقهٔ ممیزی می‌ماند

جریان تأیید

sequence
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

signup.html
<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>
signup.js
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 گرفته شده، کافی است:

Content-Security-Policy
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 را در نمونهٔ زیر استفاده می‌کند، کلید محرمانه و اطلاعات ثبت موتور باید فقط روی سرور بمانند

JavaScript
<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

shell
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. مصرف رسید

shell
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

مرجع در صفحهٔ مستقل با جزئیات درخواست، نشست و مصرف رسید قرار دارد

خطاها

همهٔ خطاها یک شکل دارند:

json
{
  "error": {
    "code": "ALREADY_CONSUMED",
    "message": "This receipt was already consumed.",
    "request_id": "5f0c2a9e8b7d4c3e1a2b3c4d5e6f7a8b",
    "retryable": false
  }
}
HTTPcodeمعنی
400INVALID_BODY · INVALID_REQUESTJSON نامعتبر، فیلد ناشناخته یا اعتبارسنجی ناموفق، از جمله نبود Idempotency-Key
401UNAUTHENTICATEDاعتبار، کلید یا نشست وجود ندارد یا معتبر نیست
403FORBIDDENscope ناکافی، مبدأ غیرمجاز یا CSRF نادرست
403BINDING_MISMATCHرسید یا intent به سایت، action یا نصب دیگری تعلق دارد
404NOT_FOUNDمنبع در این tenant نیست
409ALREADY_CONSUMEDرسید قبلاً مصرف شده است
409IDEMPOTENCY_CONFLICTهمان کلید با بدنهٔ متفاوت
409OPERATION_IN_PROGRESSهمان عملیات هنوز در جریان است؛ retryable: true
409CONFLICTمنبع در وضعیتی نیست که این عملیات را بپذیرد
410SESSION_EXPIREDintent، نشست یا رسید منقضی شده است
413BODY_TOO_LARGEبدنه بیش از 64 KiB
400CAPTCHA_REQUIREDکپچای ورود پنل روشن است و رسید آن فرستاده نشده یا پذیرفته نشد
422INVALID_PROOFCap توکن را رد کرد یا توکن مال سایت Cap دیگری است
502UPSTREAM_REJECTEDCap درخواست مدیریتی را رد کرد
503UPSTREAM_UNAVAILABLECap برای challenge یا redeem در دسترس نبود؛ retryable: true
503UPSTREAM_RESULT_UNKNOWNنتیجهٔ تأیید نامعلوم است؛ رسیدی صادر نشده و نباید با همان proof تکرار شود
503NO_ELIGIBLE_PROVIDERهیچ موتور واجد شرایط و فعالی برای این سایت در دسترس نیست
503STORAGE_UNAVAILABLEپایگاه داده در دسترس نیست؛ retryable: true
429RATE_LIMITEDدرخواست ورود یا نشست مرورگر از یک نشانی زیاد است، یا سقف نشست سایت برای هر IP پر شده؛ به اندازهٔ Retry-After ثانیه صبر کنید
503MAIL_NOT_CONFIGUREDSMTP تنظیم نشده است
503ADAPTERS_NOT_IMPLEMENTEDحالت foundation، بدون پایگاه
500INTERNAL_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 تنظیم شده است
capstate برابر 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

نسخهٔ ویجت و روش بررسی

V1 فقط Sum، نسخهٔ V2 فقط PoW قابل مشاهده، نسخهٔ V3 فقط Marks، نسخهٔ V4 فقط PoW نامرئی و نسخهٔ V5 فقط Marks پیشرفته است تنظیم‌ها در پنل هر سایت ذخیره می‌شوند و نشست قبلی با تنظیم زمان صدورش ادامه می‌یابد

نسخهٔ V3 امتیاز رفتاری ندارد و V4 فقط PoW نامرئی با نشانگر و اجرای هنگام عملیات است محاسبهٔ مرورگر اثبات انسان‌بودن نیست

Digital Captcha Lab