feat: Robotex landing describes architecture v2

- how it works: async POST /v2/tasks + polling, window held between tasks,
  /v1/solve stays synchronous; example uses an http proxy and native Linux UA
- new "Architecture" section (api, RabbitMQ, worker, Redis) via the optional
  engine block; base engine styles moved into landing.css
- features: window between tasks, inner_text; limits: open window takes a slot

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
protokeyandClaude Opus 5.5 committed 2026-10-13 21:13:35 +04:00
1 parent 672467a699
commit d0c0b485c6
3 files changed
+116 -42

No files matched your search

+20
View File
@@ -118,3 +118,23 @@ h3 { font-size: 1.05rem; font-weight: 600; }
.footer { padding: 2rem 0; border-top: 1px solid var(--border); color: var(--muted); font-size: .9rem; }
.footer__row { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .5rem 2rem; }
.footer p { margin: 0; }
/* under the hood (optional "engine" section; themes may restyle it) */
.engine__kicker {
margin: 0 0 .5rem; color: var(--accent);
font: 600 .8rem/1 'JetBrains Mono', monospace; text-transform: uppercase; letter-spacing: .12em;
}
.engine__list {
list-style: none; margin: 2rem 0 0; padding: 0;
display: grid; grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); gap: .75rem 2rem;
}
.engine__list li { position: relative; padding-left: 1.4rem; color: var(--muted); }
.engine__list li::before { content: "›"; position: absolute; left: 0; color: var(--accent); font-weight: 800; }
.engine__list strong { color: var(--text); }
.engine__callout {
margin-top: 2.5rem; padding: 1.25rem 1.5rem;
border: 1px solid rgba(124, 140, 255, .35); border-radius: var(--radius);
background: rgba(124, 140, 255, .06);
}
.engine__callout h3 { color: var(--accent); font-size: 1.1rem; }
.engine__callout p { margin: .5rem 0 0; color: var(--muted); }
+48 -21
View File
@@ -14,6 +14,7 @@ sitemap:
nav:
- { label: "Features", href: "#features" }
- { label: "How it works", href: "#how" }
- { label: "Architecture", href: "#engine" }
- { label: "Community", href: "#community" }
cta:
@@ -33,28 +34,30 @@ features:
title: "Features"
items:
- title: "Scenarios"
text: "More than opening a page. Fill a form, click, move the mouse, wait for an element, grab a piece of HTML."
text: "More than opening a page. Fill a form, click, move the mouse, wait for an element, grab a piece of HTML or text."
- title: "Anti-bot pages"
text: "Detects a Cloudflare check before and during a scenario and gets through it. If it escalates to an image captcha, sends it to a recognition service."
- title: "Captcha"
text: "The `captcha` step solves hCaptcha and other captchas via 2captcha before the form is submitted."
- title: "Profiles"
text: "Cookies, local storage and the browser fingerprint live in one profile. Locale and timezone match the proxy's geo. To the site it's still the same user."
- title: "Real Chrome"
text: "Chrome driven by Patchright, without the CDP leaks anti-bots use to detect automation."
- title: "A window between tasks"
text: "The browser can wait up to two hours for the next step. The next task runs in the same window, without restarting Chrome or passing the check again, right on the open page."
- title: "Any language"
text: "It's a service, not a library. Your bot only needs an HTTP client, whatever it's written in."
how:
title: "How it works"
steps:
- "The bot sends a URL, an action scenario and its proxy to `POST /v1/solve`."
- "Robotex opens the page, passes the checks and runs the scenario. The browser profile is saved and returned in the `session_id` cookie."
- "The bot gets the cookies and User-Agent and keeps browsing on its own, **through the same proxy**. When it needs a browser again, it sends `session_id` and continues in the same profile."
- "The bot sends a URL, an action scenario and its proxy to `POST /v2/tasks`. The task is queued, and the response carries a link to the result and a `session_id` cookie."
- "A free worker opens the page in Chrome, passes the checks and runs the scenario. The bot polls the link until the task is `done`."
- "The bot gets the cookies and User-Agent and keeps browsing on its own, **with the same UA and through the same proxy**."
- "When it needs a browser again, it sends `session_id`. If it asked to keep the window (`browser_idle_timeout`), the task runs there, even without a `url`. If not, Chrome starts with the same profile."
- "Need the answer right away, without polling: `POST /v1/solve` works as before, synchronously."
code:
- caption: "Request"
- caption: "Task"
body: |
POST https://app.robotex.sokolab.xyz/v1/solve
POST https://app.robotex.sokolab.xyz/v2/tasks
{
"url": "https://example.com/login",
@@ -64,28 +67,52 @@ how:
{"type": "click", "selector": "#submit"},
{"type": "wait_for", "selector": ".dashboard", "timeout": 30}
],
"proxy": "socks5://user:pass@host:port"
"proxy": "http://user:pass@host:port",
"browser_idle_timeout": 10
}
- caption: "Response"
→ 202 {"task_id": "0b9c…", "status": "queued", "url": "/v2/tasks/0b9c…"}
- caption: "Result: GET /v2/tasks/0b9c…"
body: |
{
"status": "ok",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...",
"cookies": [
{"name": "sessionid", "value": "...", "domain": ".example.com"}
],
"actions": [
{"type": "fill", "ok": true},
{"type": "fill", "ok": true},
{"type": "click", "ok": true},
{"type": "wait_for", "ok": true}
]
"task_id": "0b9c…",
"status": "done",
"result": {
"status": "ok",
"user_agent": "Mozilla/5.0 (X11; Linux x86_64) ... Chrome/155.0.0.0 ...",
"cookies": [
{"name": "sessionid", "value": "...", "domain": ".example.com"}
],
"actions": [
{"type": "fill", "ok": true},
{"type": "fill", "ok": true},
{"type": "click", "ok": true},
{"type": "wait_for", "ok": true}
]
}
}
engine:
kicker: "Under the hood"
title: "Architecture v2"
lead: "The browser used to live right inside the API, and once the slots ran out, a request got turned away. Now the API only takes tasks and returns results, while Chrome runs in separate workers behind a queue."
items:
- "**api** on FastAPI, no browser: queues tasks, serves status and results"
- "**RabbitMQ**: tasks wait in the queue for a free slot instead of getting a `503`"
- "**worker** on dramatiq: real Chrome under Patchright and Xvfb, no CDP leaks, several windows per worker"
- "**Redis**: task statuses and results are kept for a day, proxies and the 2captcha key never get there"
- "Tasks of a session with an open window go to the personal queue of the worker holding that window"
- "`/v1/solve` runs on top of the same queue, existing clients change nothing"
callout:
title: "The browser is no longer single-use"
text: "A window can stay open for 0 to 120 idle minutes, and a scenario can run in it step by step: log in, hand control to the bot, come back and continue on the same page. Close it early with `DELETE /v2/sessions/current`."
limits:
title: "Where it stands, honestly"
items:
- "**It's an early MVP.** Access is open, no sign-up or keys, but the API may change."
- "An open window takes up a browser slot. Don't keep it longer than you need."
- "For now all workers run on a single server."
- "**It's free.** You only pay for your own proxies and, if needed, 2captcha."
- "Proxy quality matters a lot: no fingerprint will help on a dirty IP."
- "Turnstile doesn't pass on every site yet, I'm working on stability."
+48 -21
View File
@@ -14,6 +14,7 @@ sitemap:
nav:
- { label: "Возможности", href: "#features" }
- { label: "Как работает", href: "#how" }
- { label: "Архитектура", href: "#engine" }
- { label: "Сообщество", href: "#community" }
cta:
@@ -33,28 +34,30 @@ features:
title: "Возможности"
items:
- title: "Сценарии"
text: "Не просто открыть страницу. Заполнить форму, кликнуть, навести мышь, дождаться элемента, забрать кусок HTML."
text: "Не просто открыть страницу. Заполнить форму, кликнуть, навести мышь, дождаться элемента, забрать кусок HTML или текст."
- title: "Анти-бот страницы"
text: "Сам находит проверку Cloudflare до и во время сценария и проходит ее. Если она переросла в капчу с картинками, отправляет в сервис распознавания."
- title: "Капча"
text: "Шаг `captcha` в сценарии решает hCaptcha и другие капчи через 2captcha до отправки формы."
- title: "Профили"
text: "Куки, local storage и отпечаток браузера живут в одном профиле. Локаль и часовой пояс подбираются под гео прокси. Для сайта это все тот же пользователь."
- title: "Настоящий Chrome"
text: "Chrome под управлением Patchright, без утечек CDP, по которым анти-боты узнают автоматизацию."
- title: "Окно между задачами"
text: "Браузер может подождать следующего шага до двух часов. Следующая задача выполнится в том же окне, без перезапуска Chrome и повторной проверки, прямо на открытой странице."
- title: "Любой язык"
text: "Это сервис, а не библиотека. Боту нужен только HTTP клиент, на чем он написан, неважно."
how:
title: "Как это работает"
steps:
- "Бот отправляет в `POST /v1/solve` адрес, сценарий действий и свой прокси."
- "Robotex открывает страницу, проходит проверки и выполняет сценарий. Профиль браузера сохраняется и возвращается в куке `session_id`."
- "Бот получает куки и User-Agent и дальше ходит по сайту сам, **через тот же прокси**. Понадобится браузер снова, передаст `session_id` и продолжит в том же профиле."
- "Бот отправляет в `POST /v2/tasks` адрес, сценарий действий и свой прокси. Задача встает в очередь, в ответ приходит ссылка на результат и кука `session_id`."
- "Свободный воркер открывает страницу в Chrome, проходит проверки и выполняет сценарий. Бот опрашивает ссылку, пока задача не станет `done`."
- "Бот получает куки и User-Agent и дальше ходит по сайту сам, **с тем же UA и через тот же прокси**."
- "Понадобится браузер снова, передаст `session_id`. Если просил подержать окно (`browser_idle_timeout`), задача выполнится в нем, можно даже без `url`. Если нет, Chrome откроется с тем же профилем."
- "Нужен ответ сразу, без опроса: `POST /v1/solve` работает как раньше, синхронно."
code:
- caption: "Запрос"
- caption: "Задача"
body: |
POST https://app.robotex.sokolab.xyz/v1/solve
POST https://app.robotex.sokolab.xyz/v2/tasks
{
"url": "https://example.com/login",
@@ -64,28 +67,52 @@ how:
{"type": "click", "selector": "#submit"},
{"type": "wait_for", "selector": ".dashboard", "timeout": 30}
],
"proxy": "socks5://user:pass@host:port"
"proxy": "http://user:pass@host:port",
"browser_idle_timeout": 10
}
- caption: "Ответ"
→ 202 {"task_id": "0b9c…", "status": "queued", "url": "/v2/tasks/0b9c…"}
- caption: "Результат: GET /v2/tasks/0b9c…"
body: |
{
"status": "ok",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...",
"cookies": [
{"name": "sessionid", "value": "...", "domain": ".example.com"}
],
"actions": [
{"type": "fill", "ok": true},
{"type": "fill", "ok": true},
{"type": "click", "ok": true},
{"type": "wait_for", "ok": true}
]
"task_id": "0b9c…",
"status": "done",
"result": {
"status": "ok",
"user_agent": "Mozilla/5.0 (X11; Linux x86_64) ... Chrome/155.0.0.0 ...",
"cookies": [
{"name": "sessionid", "value": "...", "domain": ".example.com"}
],
"actions": [
{"type": "fill", "ok": true},
{"type": "fill", "ok": true},
{"type": "click", "ok": true},
{"type": "wait_for", "ok": true}
]
}
}
engine:
kicker: "Под капотом"
title: "Архитектура v2"
lead: "Раньше браузер жил прямо в API, и когда слоты кончались, запрос получал отказ. Теперь API только принимает задачи и отдает результаты, а Chrome работает в отдельных воркерах за очередью."
items:
- "**api** на FastAPI без браузера: ставит задачи, отдает статус и результат"
- "**RabbitMQ**: задачи ждут свободного слота в очереди, а не получают `503`"
- "**worker** на dramatiq: настоящий Chrome под Patchright и Xvfb, без утечек CDP, несколько окон на воркер"
- "**Redis**: статусы и результаты задач хранятся сутки, прокси и ключ 2captcha туда не попадают"
- "Задачи сессии с открытым окном идут в личную очередь того воркера, у которого это окно"
- "`/v1/solve` работает поверх той же очереди, старые клиенты ничего не меняют"
callout:
title: "Браузер больше не одноразовый"
text: "Окно можно держать открытым от 0 до 120 минут простоя и вести в нем сценарий по шагам: залогиниться, отдать управление боту, вернуться и продолжить на той же странице. Закрыть окно раньше можно через `DELETE /v2/sessions/current`."
limits:
title: "Честно о текущем состоянии"
items:
- "**Это ранний MVP.** Доступ открыт без регистрации и ключей, но API может меняться."
- "Открытое окно занимает слот браузера. Держите его не дольше, чем нужно."
- "Пока все воркеры работают на одном сервере."
- "**Бесплатно.** Платить нужно только за свои прокси и, если нужна, капчу в 2captcha."
- "Прокси решает многое: на грязном IP не поможет никакой отпечаток."
- "Turnstile проходит не на каждом сайте, над стабильностью работаю."