API — Порты и трафик
Открывайте, закрывайте, перечисляйте и настраивайте прокси-порты, а также тестируйте вышестоящий прокси (upstream) перед его использованием. Это эндпоинты, которые вы будете применять чаще всего при интеграции BlankTrail Proxy.
Открыть порт
Один запрос поднимает локальный прокси-порт и сразу задаёт всё его поведение. Полей много, обязательное одно — port; остальные берут значения по умолчанию, а поменять их потом можно на живом порте.
/api/v1/ports/openТребуется авторизацияПоднимает прокси-порт с заданной личностью и поведением.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
port | int | Да | Номер порта, 1–65535. |
protocol | string | Нет | http (по умолчанию), socks5 или mtproto. |
upstream | string | Нет | Выходной прокси; пусто — напрямую. |
mode | string | Нет | Как выбирается личность: random, db, auto, specific, custom. |
browser | string | Нет | Фильтр браузера; несовместим с mode=random. |
os | string | Нет | Фильтр ОС; несовместим с mode=random. |
{
"port": 20134,
"protocol": "socks5",
"mode": "db",
"browser": "chrome",
"os": "windows",
"upstream": "socks5://user:pass@host:1080"
}curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"port":20134,"protocol":"socks5","mode":"db","browser":"chrome","os":"windows"}' \
http://127.0.0.1:8891/api/v1/ports/open{
"port": 20134,
"protocol": "socks5",
"status": "opened",
"current_profile": {
"name": "chrome_152_windows",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) …",
"browser": "chrome",
"os": "windows"
}
}- 400 «port is required» — поля нет или оно 0; 400 «invalid port number: N (must be 1-65535)».
- 400 на недопустимом значении mode, browser, os, vdns_mode, resolver_strategy или intercept_scope — текст отказа перечисляет допустимые.
- 400, если mode=random сочетается с фильтром browser или os; 400 «require_udp_dns requires vdns_mode to be on_leak or forced»; 400, если запрошен шлюз, а менеджер шлюзов не включён; 400 «gateway config … not found — upload it first via POST /api/v1/ovpn», если upstream_gateway или chain_gateway называет шлюз, которого нет в сохранённом списке — опечатка в имени даёт тот же отказ, что и незагруженный шлюз, поэтому сверьтесь с GET /api/v1/ovpn прежде чем винить выключенный менеджер.
- 403 «the debug fingerprint port requires a Pro license» на debug_capture без Pro.
- 409, если порт уже открыт или занят другой программой — текст приходит от менеджера портов.
- При protocol=mtproto ответ несёт ещё два поля: tg_link (ссылка tg://proxy…, которую отдают клиенту Telegram) и mtproto_secret (канонический секрет ee…, в том числе сгенерированный).
Выход в сеть
| Поле | Тип | По умолчанию | Что делает |
|---|---|---|---|
upstream | string | — | Выходной прокси: scheme://[user:pass@]host:port, схемы socks5, socks5h, http. Пусто — выход напрямую. |
chain_proxy | string | — | Первое звено цепочки перед выходным прокси. |
upstream_gateway | string | — | Имя сохранённого шлюза; его локальный SOCKS5 становится выходом. |
chain_gateway | string | — | Имя сохранённого шлюза для первого звена цепочки. |
ovpn_config | string | — | Устаревший алиас upstream_gateway; оставлен ради прежних клиентов. |
upstream_tls_insecure | bool | false | Снять проверку сертификата у https-прокси. Только для своего прокси с самоподписанным сертификатом. |
allow_mitm_upstream | bool | false | Разрешить выход, который сам вскрывает TLS. По умолчанию запрещено: такой выход стирает отпечаток. |
egress_force_ipv4 | bool | true | Выходить только по IPv4 — защита от утечки через IPv6. |
block_private_targets | bool | true | Отказывать в соединениях на частные, локальные и CGNAT-адреса: иначе прокси становится картой локальной сети. |
Личность и протокол
| Поле | Тип | По умолчанию | Что делает |
|---|---|---|---|
mode | string | random | random, db (алиас database), auto, specific или custom. С фильтрами browser/os режим random несовместим. |
browser | string | — | Фильтр браузера: chrome, firefox, safari, edge, random; можно с версией — chrome_145. |
os | string | — | Фильтр ОС: windows, macos, linux, ios, android, random. |
specific_profile | string | — | Имя профиля для mode=specific, например chrome_152_windows. |
custom_tls | object | — | Снятый отпечаток целиком для mode=custom; та же форма, что у PUT /port/{port}/custom_tls. |
auto_profile_from_ua | bool | false | Выбирать профиль по User-Agent запроса. |
h2_spoofing | bool | — | Подменять параметры HTTP/2 под профиль браузера. |
spoof_user_agent | bool | — | Подменять User-Agent. Выключено — заголовок клиента идёт байт в байт. |
spoof_headers | bool | — | Приводить набор и порядок заголовков к браузерному. |
tls_passthrough | bool | false | Пропускать TLS насквозь без вскрытия: отпечаток клиента сохраняется, содержимое не читается. |
tls_mirror | bool | false | Вскрывать TLS, но повторять наружу снятый отпечаток самого клиента. |
session_resumption | bool | — | Разрешать возобновление сессий TLS (тикеты). |
enable_http3 | bool | false | Переоткрывать соединение по HTTP/3 там, где сайт предлагает h3 и выход умеет UDP. |
decompress | bool | — | Распаковывать br/gzip/zstd перед отдачей клиенту. Режим решателя челленджей включает принудительно. |
Нагрузка и тайм-ауты
| Поле | Тип | По умолчанию | Что делает |
|---|---|---|---|
max_concurrent | int | — | Предел одновременных запросов; 0 — без предела. |
sem_timeout | int | — | Сколько секунд запрос ждёт места в этом пределе. 🔴 Здесь поле называется sem_timeout — в отличие от отдельной ручки, где оно timeout_seconds. |
skip_retry | bool | false | Не повторять запрос после сетевой ошибки. |
retry_delay_ms | int | — | Пауза между повторами, миллисекунды. |
timeout_seconds | int | — | Тайм-аут простоя СОЕДИНЕНИЯ (не порта), секунды. |
connect_timeout_seconds | int | 5 | Потолок ОДНОЙ попытки набора. |
request_timeout_seconds | int | 30 | Потолок всей фазы установления вместе с повторами; 0 — без ограничения. |
idle_seconds | int | null | null | Тайм-аут простоя ПОРТА, перекрывающий общий (по умолчанию 30 минут); 0 — не закрывать никогда. |
Кэш, журнал и захват
| Поле | Тип | По умолчанию | Что делает |
|---|---|---|---|
cache_enabled | bool | false | Включить кэш ответов на порту. |
cache_mode | string | normal | normal, hard, hard-media или hard-autowarm. |
cache_ignore_no_cache | bool | false | Кэшировать вопреки заголовку no-cache. |
traffic_log | bool | false | Писать журнал запросов порта в data/traffic_port_<порт>.jsonl. |
debug_capture | bool | false | Открыть порт захвата отпечатков. 🔴 Требует лицензии Pro: иначе 403. |
debug_capture_n | int | 500 | Размер кольца снимков на порту захвата. |
Челленджи и сессии
| Поле | Тип | По умолчанию | Что делает |
|---|---|---|---|
js_solver | bool | false | Решатель челленджей: запросы, упёршиеся в проверку, уходят в пул решателя. Требует вскрытия TLS (несовместимо с tls_passthrough). |
keep_sessions | bool | false | Порт держит свою банку кук по доменам: впитывает Set-Cookie, подставляет куки и идёт по перенаправлениям. От js_solver не зависит. |
DNS и защита от утечек
| Поле | Тип | По умолчанию | Что делает |
|---|---|---|---|
leak_guard | string | — | Предстартовая проверка утечек DNS и IPv6 на выходе: off, warn или enforce. |
vdns_mode | string | off | Виртуальный DNS: off, on_leak (включать при обнаруженной утечке) или forced. Тарифы Standard и выше: на Lite поле принимается, порт открывается с выключенным vdns, а vdns_path_reason у него — plan. |
resolver_strategy | string | auto | auto или custom — откуда брать резолверы. |
custom_resolvers | array | — | Список host:port для resolver_strategy=custom. |
ecs_enabled | bool | true | Передавать подсеть клиента в запросе DNS (EDNS Client Subnet). |
vdns_strict_bypass | bool | false | Строгий обход: наружу уходит только IP-литерал, имя хоста не покидает машину. |
require_udp_dns | bool | false | Открывать порт ТОЛЬКО если выход доказал, что умеет проносить UDP для DNS. Требует vdns_mode on_leak или forced, иначе 400. |
Перехват системного трафика
| Поле | Тип | По умолчанию | Что делает |
|---|---|---|---|
intercept | bool | false | Уводить в порт системный трафик без настройки прокси в приложении. |
intercept_scope | string | system | 🔴 system — ВСЯ машина (значение по умолчанию), process — только перечисленные программы. |
intercept_apps | array | — | Пути к exe для scope=process. |
MTProto (прокси для Telegram)
Поля ниже имеют смысл только при protocol=mtproto. Порт в этом режиме говорит на протоколе Telegram, а не на HTTP или SOCKS5, и подключается к нему клиент Telegram по ссылке из ответа.
| Поле | Тип | По умолчанию | Что делает |
|---|---|---|---|
mtproto_secret | string | — | Каноническая строка секрета вида ee…; пусто — сгенерировать новый. |
mtproto_camouflage_domain | string | www.google.com | Домен маскировки — он же SNI, который предъявляет клиент Telegram. |
mtproto_fallback_real | bool | true | Отправлять сканеров и клиентов с неверным секретом на настоящий сайт маскировки. |
mtproto_egress | string | auto | auto, obfuscated или faketls — как идти к вышестоящему прокси MTProto. |
mtproto_faketls_upstream | string | — | host:port вышестоящего прокси MTProto для egress=faketls. |
mtproto_faketls_secret | string | — | Секрет ee… этого вышестоящего прокси. |
Закрыть порт
/api/v1/ports/closeТребуется авторизацияЗакрывает открытый порт и рвёт все идущие через него соединения.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
port | int | Да | Порт, который нужно закрыть. |
{ "port": 20134 }curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"port":20134}' \
http://127.0.0.1:8891/api/v1/ports/close{
"port": 20134,
"status": "closed"
}- 400 «port is required», если поля нет или оно 0; 404, если порт не открыт.
- Порт с перехватом при закрытии снимает и своё правило перехвата — трафик возвращается на обычный маршрут.
Список открытых портов
/api/v1/portsТребуется авторизацияВозвращает все открытые порты с их полной конфигурацией и состоянием.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/ports{
"ports": [
{
"port": 20134,
"protocol": "socks5",
"created_at": "2026-09-06T09:12:44Z",
"last_activity": "2026-09-06T11:03:01Z",
"current_profile": "chrome_152_windows",
"mode": "db",
"upstream": "socks5://host:1080",
"chain_proxy": "",
"browser_filter": "chrome",
"os_filter": "windows",
"h2_spoofing": true,
"spoof_user_agent": true,
"spoof_headers": true,
"decompress": true,
"max_concurrent": 0,
"skip_retry": false,
"retry_delay_ms": 0,
"cache_enabled": false,
"cache_mode": "",
"cache_normalize_ids": false,
"debug_capture": false,
"capture_count": 0,
"auto_profile_from_ua": false,
"effective_idle_seconds": 1800,
"js_solver": false,
"keep_sessions": false,
"captcha_action": "rotate_retry",
"captcha_max_attempts": 3,
"intercept": false,
"vdns_active": false
}
],
"total_open": 1,
"max_ports": 1000
}- max_ports — действующий потолок одновременно открытых портов: меньшее из настроенного максимума (portmanager.max_ports) и лимита вашей лицензии. Это ровно то же число, что отдаёт GET /api/v1/status, — сверять две ручки между собой больше не нужно.
- 🔴 В сборках до этого выпуска поле здесь отдавало константу 1000 независимо от тарифа и настроек, тогда как у /status в том же поле уже стоял настоящий потолок. Увидели ровно 1000 при заведомо меньшем лимите — обновите клиента, а пул до тех пор планируйте по max_ports из /status.
- Поля status в элементе списка нет: открытый порт на то и открыт. Только когда им есть что сказать, появляются leak_report, last_ua, last_auto_profile, idle_override_seconds, leak_guard, upstream_gateway, chain_gateway, intercept_scope, intercept_apps, intercept_state, intercept_reason, vdns_mode, vdns_transport и vdns_udp_reason, а у порта с protocol=mtproto — ещё tg_link и mtproto_secret (те же два поля, что отдаёт открытие порта). Все прочие поля приходят в КАЖДОМ элементе, даже пустыми, — в том числе cache_normalize_ids, captcha_action, captcha_max_attempts и vdns_active, показанные в примере выше.
- effective_idle_seconds — итоговый порог простоя после наложения переопределения порта на общий (по умолчанию 30 минут); 0 означает «не закрывать».
- vdns_active показывает, переписывает ли виртуальный DNS подключения ПРЯМО СЕЙЧАС, — в отличие от vdns_mode, который лишь заявляет режим. vdns_transport — транспорт, которым ФАКТИЧЕСКИ прошло последнее удачное разрешение: udp, tcp, dot или doh; пусто — удачных ещё не было. vdns_udp_reason появляется, когда VDNS работает, но не по udp, и называет причину: refused, accepted_but_silent, control_error, chain_unsupported или not_probed.
Подобрать свободный порт
/api/v1/ports/suggestТребуется авторизацияВозвращает наименьший номер в диапазоне 20000–29999, который и не занят менеджером, и реально поддаётся привязке в системе.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/ports/suggest{
"port": 20134
}- 503 «no_free_port», если во всём диапазоне свободного не нашлось.
- Между подсказкой и открытием порт может занять кто-то ещё — это нормально: открытие в таком случае отвечает 409, и надо просто спросить следующий.
Проверить выход до открытия порта
Проверка собирает цепочку выхода на время запроса и гоняет по ней выбранные пробы, ничего не открывая. Так узнают, что прокси жив, проносит UDP для DNS и не течёт, — до того, как на нём построят работу.
/api/v1/upstream/testТребуется авторизацияПроверяет доступность выхода, поддержку UDP через SOCKS5 и утечки DNS и IPv6.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
checks | array | Да | Любые из http, udp, leak. |
protocol | string | Нет | socks5 или http — протокол будущего порта. |
upstream | string | Нет | Выходной прокси; пусто — проверять прямое подключение. |
chain_proxy | string | Нет | Первое звено цепочки. |
upstream_gateway | string | Нет | Имя сохранённого шлюза вместо адреса выхода. |
chain_gateway | string | Нет | Имя сохранённого шлюза для первого звена. |
upstream_tls_insecure | bool | Нет | Зеркалит одноимённую настройку порта: без неё проверялась бы не та конфигурация, которую вы собираетесь открыть. |
{
"checks": ["http", "udp", "leak"],
"protocol": "socks5",
"upstream": "socks5://user:pass@host:1080"
}curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"checks":["http","leak"],"upstream":"socks5://user:pass@host:1080"}' \
http://127.0.0.1:8891/api/v1/upstream/test{
"http": { "ok": true, "detail": "200 in 45ms" },
"udp": { "ok": false, "detail": "UDP ASSOCIATE granted but nothing came back",
"code": "accepted_but_silent" },
"leak": { "ok": true, "detail": "no DNS/IPv6 leak — exit 203.0.113.45" }
}- У каждой пробы, кроме ok и detail, могут прийти skipped (проба не выполнялась) и code — машиночитаемая причина, по которой удобно ветвиться.
- 403, если лицензия неактивна: эта ручка за лицензией. 400 «invalid JSON body»; 405 на любом методе, кроме POST. Тело ограничено 64 КиБ.
Состояние и конфигурация порта
Три ручки об одном и том же с разной подробностью: /status — сводка о живом порте, /config — полный снимок конфигурации, PUT /config — единственный способ поменять то, у чего нет своей ручки.
/api/v1/port/{port}/statusТребуется авторизацияСводка о живом порте: личность, поведение, выход, счётчики отказов и время последней активности.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/status{
"port": 20134,
"protocol": "socks5",
"mode": "db",
"browser_filter": "chrome",
"os_filter": "windows",
"h2_spoofing": true,
"spoof_user_agent": true,
"spoof_headers": true,
"session_resumption": true,
"max_concurrent": 0,
"sem_timeout_seconds": 30,
"connect_timeout_seconds": 5,
"request_timeout_seconds": 30,
"skip_retry": false,
"retry_delay_ms": 0,
"cache_enabled": false,
"cache_mode": "",
"traffic_log": false,
"tls_passthrough": false,
"tls_mirror": false,
"cache_ignore_no_cache": false,
"allow_mitm_upstream": false,
"mitm_blocked": 0,
"current_profile": { "name": "chrome_152_windows", "user_agent": "Mozilla/5.0 …",
"browser": "chrome", "os": "windows" },
"upstream": "socks5://host:1080",
"chain_proxy": "",
"created_at": "2026-09-06T09:12:44Z",
"last_activity": "2026-09-06T11:03:01Z"
}- mitm_blocked и mitm_last_issuer берутся одним снимком: они меняются вместе, и раздельное чтение показало бы число от одного отказа рядом с виновником от другого.
- last_activity двигает не только трафик, но и любое обращение к ручкам этого порта.
/api/v1/port/{port}/configТребуется авторизацияПолный снимок конфигурации порта — все примерно шестьдесят ключей, включая те, у которых нет своей ручки. Ключи без значения в снимок не попадают.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/config{
"port": 20134,
"protocol": "socks5",
"upstream": "socks5://host:1080",
"mode": "db",
"browser": "chrome",
"os": "windows",
"timeout_seconds": 0,
"connect_timeout_seconds": 5,
"request_timeout_seconds": 30,
"egress_force_ipv4": true,
"block_private_targets": true,
"js_solver": false,
"keep_sessions": false,
"sessions_per_port": 1,
"captcha_action": "rotate_retry",
"captcha_max_attempts": 3,
"ecs_enabled": true
}- Ответ обрезан для примера, но показанные ключи порт действительно отдаёт. Ключи без значения в ответ НЕ попадают вовсе: leak_guard, vdns_mode и resolver_strategy при значении по умолчанию отсутствуют, а не приходят пустой строкой; idle_seconds есть только у порта со СВОИМ порогом простоя — при наследовании общего ключа нет, и null не приходит никогда; intercept и intercept_scope есть только у порта, открытого с перехватом, причём intercept тогда всегда true, а intercept_scope — system или process. Поэтому cfg.intercept === false и cfg.idle_seconds === null дадут undefined: проверяйте наличие ключа, например "intercept" in cfg.
- Булевы и числовые настройки порта, наоборот, приходят ВСЕГДА — в том числе нулём и false: js_solver, keep_sessions, timeout_seconds, egress_force_ipv4 и прочие. Имена ключей совпадают с полями тела POST /api/v1/ports/open — по ним же идёт слияние в PUT /config.
/api/v1/port/{port}/configТребуется авторизацияМеняет конфигурацию живого порта. Присланные поля НАКЛАДЫВАЮТСЯ на текущий снимок: то, чего в теле нет, остаётся как было.
{ "mode": "auto", "browser": "firefox", "os": "macos" }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"mode":"auto","browser":"firefox","os":"macos"}' \
http://127.0.0.1:8891/api/v1/port/20134/config{
"port": 20134,
"protocol": "socks5",
"status": "reconfigured",
"current_profile": { "name": "firefox_152_macos", "user_agent": "Mozilla/5.0 …",
"browser": "firefox", "os": "macos" }
}- 🔴 Поле status в ответе — не состояние порта, а имя выполненного действия: у этой ручки это ровно строка reconfigured, у POST /api/v1/ports/open — opened. Сверять ответ со строкой open нельзя ни там, ни там.
- Принимает те же поля и те же значения, что POST /api/v1/ports/open, и отказывает теми же 400 — включая запрет сочетать mode=random с фильтрами.
- 500 «cannot read the port's current configuration», если снимок не удалось прочитать — слияние тогда невозможно, и порт остаётся нетронутым.
- Это единственный путь поменять js_solver, keep_sessions, leak_guard, vdns_mode, resolver_strategy, custom_resolvers, ecs_enabled, timeout_seconds, connect_timeout_seconds, request_timeout_seconds, intercept и остальные ключи без собственной ручки.
- 400, если тело меняет перехват у уже открытого порта: включить, выключить или перенастроить его на живом порте нельзя — закройте порт и откройте заново. Тело, которое перехват не упоминает, проходит: сравниваются состояния, а не наличие ключа.
Настройки отдельного порта
Каждая настройка живёт по своему адресу вида /api/v1/port/{port}/имя. GET читает текущее значение, PUT меняет его на живом порте: порт не закрывается и не перезапускается.
Отказы, общие для всех ручек порта: 400 «invalid port number», если {port} не число; 404 «port N is not open», если порт закрыт; 400 «invalid JSON body», если тело не разбирается. 404 на такой ручке означает именно закрытый порт, а не отсутствующий адрес. У неизвестного имени настройки ответ зависит от метода: GET уходит в катч-олл дашборда и даёт 404 строкой «404 page not found», а PUT, POST и DELETE — 405. Тем же 405 отвечает известное имя с неподдерживаемым методом, но только если этот метод не GET (например PUT /profile): GET на ручку, у которой GET не зарегистрирован, снова уходит в катч-олл и даёт 404.
| Адрес | Методы | Поле тела | Тип | Значения и оговорки |
|---|---|---|---|---|
/mode | GET, PUT | mode | string | random, db (алиас database), auto, specific |
/browser | GET, PUT | browser | string | пусто, random, chrome, firefox, safari, edge; можно с версией: chrome_145 |
/os | GET, PUT | os | string | пусто, random, windows, macos, linux, ios, android |
/profile | GET | — | — | только чтение; закрепить профиль — через /mode или /config |
/profile/view | GET | — | — | только чтение: состав текущего профиля |
/rotate | POST | — | — | тела нет; выдаёт новый профиль немедленно |
/custom_tls | PUT | ja3, ja4, … | object | снятый отпечаток целиком; переводит порт в режим custom |
/upstream | GET, PUT | upstream | string | адрес выходного прокси; пустая строка — выход напрямую |
/chain_proxy | GET, PUT | chain_proxy | string | первое звено цепочки перед выходным прокси |
/allow_mitm_upstream | GET, PUT | allow_mitm_upstream | bool | поле обязательно: без него 400, а не false |
/h2_spoofing | GET, PUT | enabled | bool | true или false |
/spoof_user_agent | GET, PUT | enabled | bool | true или false |
/spoof_headers | GET, PUT | enabled | bool | true или false |
/tls_passthrough | GET, PUT | enabled | bool | true или false |
/tls_mirror | GET, PUT | enabled | bool | true или false |
/http3 | GET, PUT | enabled | bool | true или false |
/session_resumption | GET, PUT | enabled | bool | true или false |
/decompress | GET, PUT | enabled | bool | true или false |
/auto_profile_from_ua | GET, PUT | enabled | bool | GET отдаёт ещё last_ua |
/cache_ignore_no_cache | GET, PUT | enabled | bool | true или false |
/max_concurrent | GET, PUT | max_concurrent | int | 0 и больше; 0 — без предела |
/sem_timeout | GET, PUT | timeout_seconds | int | 1 и больше — имя поля НЕ совпадает с адресом |
/skip_retry | GET, PUT | skip_retry | bool | true или false |
/retry_delay | GET, PUT | retry_delay_ms | int | 0 и больше — имя поля НЕ совпадает с адресом |
/idle | PUT | seconds | int | null | null — вернуться к общему тайм-ауту, 0 — не закрывать; GET нет |
/cache | GET, PUT, DELETE | enabled, mode | bool, string | см. раздел «Кэш ответов» |
/traffic_log | GET, PUT, DELETE | enabled | bool | см. раздел «Журнал запросов порта» |
/captures | GET, DELETE | — | — | только на порте, открытом с debug_capture |
Остальные ключи конфигурации порта отдельных ручек НЕ имеют: timeout_seconds, connect_timeout_seconds, request_timeout_seconds, js_solver, keep_sessions, sessions_per_port, captcha_action, captcha_max_attempts, leak_guard, egress_force_ipv4, block_private_targets, h3_profile, cache_normalize_ids, vdns_mode, resolver_strategy, custom_resolvers, ecs_enabled, require_udp_dns, intercept, intercept_scope, intercept_apps, debug_capture, debug_capture_n и поля mtproto_*. Они читаются через GET /api/v1/port/{port}/config и меняются через PUT /api/v1/port/{port}/config; своего адреса у них нет. GET на /api/v1/port/{port}/js_solver вернёт 404 обычной строкой «404 page not found» — её отдаёт катч-олл дашборда, — а PUT, POST и DELETE на тот же адрес вернут 405 с заголовком Allow: GET, HEAD и телом «Method Not Allowed». Ни тот, ни другой ответ не JSON с полем error.
🔴 Семь ключей из перечисленных PUT /api/v1/port/{port}/config НЕ применяет, хотя и отвечает 200 «reconfigured»: sessions_per_port, captcha_action, captcha_max_attempts, cache_normalize_ids, h3_profile, debug_capture и debug_capture_n. Обработчик не переносит их в конфигурацию, которую отдаёт менеджеру портов, поэтому отказа не будет, а порт останется прежним. debug_capture и debug_capture_n задаются при ОТКРЫТИИ порта, в теле POST /api/v1/ports/open; чтобы их поменять, порт надо закрыть и открыть заново. Видно это по тому, что GET /api/v1/port/{port}/captures на порте, которому debug_capture досылали через PUT, так и отвечает 400 «port is not a debug fingerprint-capture port». Остальные пять тело открытия не принимает вовсе: captcha_action и captcha_max_attempts настраиваются только у пула портов (см. «Пул портов»), sessions_per_port в текущей версии всегда равен 1, а h3_profile и cache_normalize_ids не задаются ни одной ручкой — h3_profile к тому же всегда пуст, и в ответе GET /config его нет.
Поля mtproto_* PUT /config применяет по общему правилу: чего в теле нет — остаётся как было, что названо — применяется. Секрет и домен-прикрытие переживают правку любой другой настройки, поэтому розданная ссылка tg://proxy продолжает работать; а прислать mtproto_secret явно — законный способ сменить секрет, не закрывая порт. Уже открытые соединения смена не рвёт, новые идут по новому секрету.
🔴 Присланный секрет, который не разобрался, ручка НЕ отвергает: она молча выдаёт свежий, и ответ всё равно 200 «reconfigured». Сверяйте секрет в ответе GET /api/v1/port/{port}/config с тем, что отправляли.
🔴 В сборках до этого выпуска было наоборот: правка любой мелочи на mtproto-порту выдавала новый секрет и сбрасывала домен-прикрытие к стандартному, убивая уже розданную ссылку. Если клиент ещё не обновлён — не правьте mtproto-порт через PUT /config, а если правили, перечитайте секрет и раздайте ссылку заново.
Личность порта
Кем порт представляется сайту: как выбирается профиль, чем ограничен выбор и как подать свой отпечаток.
/api/v1/port/{port}/modeТребуется авторизацияВозвращает режим выбора профиля.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/mode{
"mode": "db"
}/api/v1/port/{port}/modeТребуется авторизацияМеняет режим выбора профиля на живом порте.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
mode | string | Да | random — синтетический отпечаток; db (алиас database) — настоящий профиль из базы; auto — сгенерированный под запрошенные браузер и ОС; specific — закреплённый по имени. |
specific_profile | string | Нет | Имя профиля для mode=specific, например chrome_152_windows. |
{ "mode": "specific", "specific_profile": "chrome_152_windows" }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"mode":"specific","specific_profile":"chrome_152_windows"}' \
http://127.0.0.1:8891/api/v1/port/20134/mode{
"mode": "specific"
}- Режим custom эта ручка НЕ принимает, хотя текст отказа его называет: «mode must be one of: random, db, auto, specific, custom». Порт переходит в custom только через PUT /custom_tls или PUT /config.
- 400, если на порте задан фильтр browser или os, а режим переключают в random: синтетический отпечаток не принадлежит настоящему браузеру и не может их соблюсти. Сначала снимите фильтры пустой строкой.
- 400 с текстом от движка, если specific_profile не найден — но режим к этому моменту УЖЕ переключён. После такой ошибки перечитайте GET /api/v1/port/{port}/config: порт остался в specific со старым именем.
- Новое значение действует со следующего соединения: уже открытые соединения keep-alive не рвутся.
/api/v1/port/{port}/browserТребуется авторизацияВозвращает фильтр браузера. Пустая строка — фильтра нет.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/browser{
"browser": "chrome"
}/api/v1/port/{port}/browserТребуется авторизацияСужает выбор профиля до одного браузера или снимает сужение.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
browser | string | Да | chrome, firefox, safari, edge, random или пустая строка (снять фильтр). Можно закрепить версию суффиксом: chrome_145. |
{ "browser": "chrome_145" }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"browser":"chrome_145"}' \
http://127.0.0.1:8891/api/v1/port/20134/browser{
"browser": "chrome_145"
}- 400 «browser must be one of: "", random, chrome, firefox, safari, edge (optionally with version: chrome_145)» на любом другом значении.
- 400, если порт в режиме random: он строит синтетический отпечаток и соблюсти фильтр не может. Снять фильтр пустой строкой на random-порте по-прежнему разрешено.
- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/osТребуется авторизацияВозвращает фильтр операционной системы.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/os{
"os": "windows"
}/api/v1/port/{port}/osТребуется авторизацияСужает выбор профиля до одной операционной системы или снимает сужение.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
os | string | Да | windows, macos, linux, ios, android, random или пустая строка (снять фильтр). |
{ "os": "macos" }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"os":"macos"}' \
http://127.0.0.1:8891/api/v1/port/20134/os{
"os": "macos"
}- 400 «os must be one of: "", random, windows, macos, linux, ios, android» на любом другом значении.
- 400 на порте в режиме random — по той же причине, что и у фильтра браузера.
- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/profileТребуется авторизацияВозвращает профиль, которым порт представляется сейчас. Только чтение: PUT на этот адрес даёт 405.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/profile{
"name": "chrome_152_windows",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) …",
"browser": "chrome",
"os": "windows"
}- Закрепить профиль по имени можно через PUT /api/v1/port/{port}/mode с телом {"mode":"specific","specific_profile":"…"} или через PUT /api/v1/port/{port}/config.
- Сменить личность порта немедленноPOST /api/v1/port/{port}/rotate выдаёт новый профиль, не дожидаясь ротации, а GET /api/v1/port/{port}/profile/view показывает состав текущего — обе описаны в справочнике профилей.
/api/v1/port/{port}/custom_tlsТребуется авторизацияПодаёт порту снятый снаружи отпечаток целиком и переводит его в режим custom. Так порт получает форму, снятую с настоящего браузера — например через ChromeApi или tls.peet.ws.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
ja3 | string | Нет | Строка JA3 целиком. |
ja3_hash | string | Нет | Хеш JA3, если он снят. |
ja4 | string | Нет | Строка JA4. |
ciphers | array | Да | Список шифров в порядке ClientHello. Единственное обязательное поле. Имена разбираются по фиксированной таблице; начинающиеся на TLS_GREASE занимают своё место как GREASE, незнакомые молча отбрасываются. Если после разбора не осталось ни одного шифра — 400. Из ja3 шифры НЕ достаются: эта строка нужна только для порядка расширений. |
extensions | array<object> | Нет | Список расширений в порядке ClientHello. Элемент — объект: обязательное name, необязательные supported_groups, signature_algorithms, versions, protocols. |
supportedGroups | array | Нет | Группы кривых. |
signatureAlgorithms | array | Нет | Алгоритмы подписи. |
alpn | array | Нет | Список ALPN. |
h2 | object | Нет | Параметры HTTP/2: settings, windowUpdate, akamai_fingerprint, headerOrder. |
userAgent | string | Нет | User-Agent, которому соответствует отпечаток. |
curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
--data-binary @captured.json \
http://127.0.0.1:8891/api/v1/port/20134/custom_tls{
"ok": true,
"mode": "custom",
"profile": {
"name": "custom_1757116800123",
"ja3_hash": "cd08e31494f9531f560d64c695473da9",
"ja4": "t13d1516h2_8daaf6152771_b0da82dd1658",
"userAgent": "Mozilla/5.0 …"
}
}- Побочное действие: h2_spoofing и spoof_user_agent принудительно выключаются — подмена HTTP/2 поверх поданной формы рвала переподключение. Нужны — включите их отдельными ручками ПОСЛЕ этого запроса.
- Второе побочное действие: прежние custom-профили порта удаляются, а существующие соединения закрываются — иначе часть трафика продолжала бы идти со старой формой.
- Третье побочное действие: решённые челленджи порта гасятся — новая форма TLS и новый User-Agent обесценивают клиренс, добытый под прежней личностью, и следующий запрос к защищённому сайту снова пройдёт через проверку. То же самое делает ФАКТИЧЕСКАЯ смена адреса в PUT /api/v1/port/{port}/upstream.
- 400 «invalid JSON: …», если тело не разбирается, и 400 «failed to set custom TLS: …», если движок не принял форму. Самый частый случай второго — тело без ciphers или с именами вне таблицы: 400 «failed to set custom TLS: building custom profile: building ClientHelloSpec: no recognized cipher suites». Сокращённого дампа из одних ja3, ja4 и userAgent недостаточно; готовое тело нужной формы отдаёт POST /api/v1/fingerprint/parse.
- GET на этот адрес не зарегистрирован, и запрос забирает катч-олл дашборда: придёт 404 обычной строкой «404 page not found», а не JSON, и этот 404 НЕ означает закрытый порт — не переоткрывайте порт по такому ответу. Текущий состав читается через /profile/view; остальные методы (POST, DELETE) дают 405.
- 🔴 Имя профиля в ответе — не custom: сервер собирает его заново на каждый запрос как custom_<время в миллисекундах> (например custom_1757116800123) и отдаёт то же имя в GET /api/v1/port/{port}/profile и GET /api/v1/port/{port}/profile/view. Сверять его с постоянной строкой нельзя; закрепить такой профиль через mode=specific со specific_profile=custom тоже нельзя — вернётся 400 «fingerprint: profile "custom" not found».
Выход в сеть
Через что порт выходит наружу и что делать, если выход подменяет TLS.
/api/v1/port/{port}/upstreamТребуется авторизацияВозвращает выходной прокси порта. Пустые значения — выход напрямую.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/upstream{
"socks5_addr": "socks5://user:pass@host:1080",
"upstream": "socks5://user:pass@host:1080"
}- Поле socks5_addr — устаревшее имя, оставленное ради прежних клиентов; оно всегда повторяет upstream.
/api/v1/port/{port}/upstreamТребуется авторизацияМеняет выходной прокси на живом порте — так вращают прокси, не закрывая порт.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
upstream | string | Нет | Адрес вида scheme://[user:pass@]host:port; схемы socks5, socks5h, http. Пустое тело или пустая строка возвращают порт на прямой выход. |
socks5_addr | string | Нет | Устаревший алиас того же поля; читается, только если upstream пуст. |
{ "upstream": "socks5://user:pass@host:1080" }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"upstream":"socks5://user:pass@host:1080"}' \
http://127.0.0.1:8891/api/v1/port/20134/upstream{
"socks5_addr": "socks5://user:pass@host:1080",
"upstream": "socks5://user:pass@host:1080"
}- Побочное действие при ФАКТИЧЕСКОЙ смене адреса: решённые челленджи порта гасятся, потому что клиренс выдан прежнему выходному адресу и с нового не работает. Следующий запрос к защищённому сайту снова пройдёт через проверку.
- Кэшированное соединение сбрасывается, простаивающие соединения к прежнему выходу закрываются.
/api/v1/port/{port}/chain_proxyТребуется авторизацияВозвращает промежуточный прокси — первое звено цепочки перед выходным.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/chain_proxy{
"chain_proxy": "http://10.0.0.5:3128"
}/api/v1/port/{port}/chain_proxyТребуется авторизацияСтавит или снимает промежуточный прокси: трафик идёт клиент → цепочка → выход → сайт.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
chain_proxy | string | Да | Адрес того же вида, что у upstream. Пустая строка снимает звено. |
{ "chain_proxy": "http://10.0.0.5:3128" }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"chain_proxy":"http://10.0.0.5:3128"}' \
http://127.0.0.1:8891/api/v1/port/20134/chain_proxy{
"chain_proxy": "http://10.0.0.5:3128"
}- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/allow_mitm_upstreamТребуется авторизацияСообщает, разрешён ли выход, подменяющий TLS, и сколько соединений уже отвергнуто по этой причине.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/allow_mitm_upstream{
"allow_mitm_upstream": false,
"mitm_blocked": 17,
"mitm_last_issuer": "CN=Corporate Proxy CA"
}- mitm_blocked и mitm_last_issuer — готовый ответ на вопрос «почему отпечаток не доезжает»: издатель последнего подменённого сертификата назван прямо.
/api/v1/port/{port}/allow_mitm_upstreamТребуется авторизацияРазрешает или запрещает работу через выход, который вскрывает и пересобирает TLS.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
allow_mitm_upstream | bool | Да | true — работать даже через такой выход; false — отвергать соединения через него. Выключено по умолчанию: такой выход стирает отпечаток, и порт молча перестаёт делать то, ради чего открыт. |
{ "allow_mitm_upstream": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"allow_mitm_upstream":true}' \
http://127.0.0.1:8891/api/v1/port/20134/allow_mitm_upstream{
"allow_mitm_upstream": true,
"mitm_blocked": 17,
"mitm_last_issuer": "CN=Corporate Proxy CA"
}- 400 «allow_mitm_upstream is required», если поля в теле нет. Это единственная булева ручка порта, которая отличает «не прислали» от false и отказывает вместо тихого выключения.
Поведение протокола
Что порт делает с TLS, HTTP/2 и заголовками. Все ручки этой группы устроены одинаково: поле тела называется enabled, ответ повторяет применённое значение.
/api/v1/port/{port}/h2_spoofingТребуется авторизацияСообщает, подменяются ли параметры HTTP/2 под выбранный браузер.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/h2_spoofing{
"enabled": true
}/api/v1/port/{port}/h2_spoofingТребуется авторизацияВключает или выключает подмену параметров HTTP/2.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
enabled | bool | Да | true — кадр SETTINGS, приоритеты и порядок псевдозаголовков берутся у профиля браузера. |
{ "enabled": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
http://127.0.0.1:8891/api/v1/port/20134/h2_spoofing{
"enabled": true
}- PUT /custom_tls выключает эту настройку принудительно — включайте её ПОСЛЕ подачи своего отпечатка.
- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/spoof_user_agentТребуется авторизацияСообщает, подменяется ли User-Agent запроса.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/spoof_user_agent{
"enabled": true
}/api/v1/port/{port}/spoof_user_agentТребуется авторизацияВключает или выключает подмену User-Agent.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
enabled | bool | Да | true — заголовок берётся у профиля; false — заголовок клиента идёт байт в байт. |
{ "enabled": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
http://127.0.0.1:8891/api/v1/port/20134/spoof_user_agent{
"enabled": true
}- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/spoof_headersТребуется авторизацияСообщает, приводится ли набор и порядок заголовков к браузерному.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/spoof_headers{
"enabled": true
}/api/v1/port/{port}/spoof_headersТребуется авторизацияВключает или выключает приведение заголовков к браузерному виду.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
enabled | bool | Да | true — набор, регистр и порядок заголовков совпадают с профилем браузера. |
{ "enabled": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
http://127.0.0.1:8891/api/v1/port/20134/spoof_headers{
"enabled": true
}- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/tls_passthroughТребуется авторизацияСообщает, пропускается ли TLS без вскрытия.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/tls_passthrough{
"enabled": true
}/api/v1/port/{port}/tls_passthroughТребуется авторизацияВключает или выключает сквозной пропуск TLS.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
enabled | bool | Да | true — соединение проходит насквозь: отпечаток клиента сохраняется, содержимое не читается, кэш и журнал запросов на таком порте бесполезны. |
{ "enabled": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
http://127.0.0.1:8891/api/v1/port/20134/tls_passthrough{
"enabled": true
}- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/tls_mirrorТребуется авторизацияСообщает, отражаются ли параметры TLS клиента вместо профиля.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/tls_mirror{
"enabled": true
}/api/v1/port/{port}/tls_mirrorТребуется авторизацияВключает или выключает отражение параметров TLS клиента.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
enabled | bool | Да | true — наружу уходит форма, снятая с самого клиента, а не из профиля. |
{ "enabled": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
http://127.0.0.1:8891/api/v1/port/20134/tls_mirror{
"enabled": true
}- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/http3Требуется авторизацияСообщает, разрешён ли HTTP/3 (QUIC) на порте.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/http3{
"enabled": true
}/api/v1/port/{port}/http3Требуется авторизацияРазрешает или запрещает HTTP/3 (QUIC).
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
enabled | bool | Да | true — там, где сайт предлагает HTTP/3, порт им пользуется. |
{ "enabled": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
http://127.0.0.1:8891/api/v1/port/20134/http3{
"enabled": true
}- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/session_resumptionТребуется авторизацияСообщает, разрешено ли возобновление сессий TLS.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/session_resumption{
"enabled": true
}/api/v1/port/{port}/session_resumptionТребуется авторизацияРазрешает или запрещает возобновление сессий TLS (тикеты).
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
enabled | bool | Да | true — сессионные тикеты принимаются и переиспользуются; false — каждое соединение начинается с полного рукопожатия. |
{ "enabled": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
http://127.0.0.1:8891/api/v1/port/20134/session_resumption{
"enabled": true
}- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/decompressТребуется авторизацияСообщает, распаковывается ли сжатое тело ответа для клиента.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/decompress{
"enabled": true
}/api/v1/port/{port}/decompressТребуется авторизацияВключает или выключает распаковку тела ответа.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
enabled | bool | Да | true — br, gzip и zstd распаковываются в обычные байты перед отдачей клиенту. |
{ "enabled": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
http://127.0.0.1:8891/api/v1/port/20134/decompress{
"enabled": true
}- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/cache_ignore_no_cacheТребуется авторизацияСообщает, кэшируются ли ответы вопреки заголовку no-cache.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/cache_ignore_no_cache{
"enabled": true
}/api/v1/port/{port}/cache_ignore_no_cacheТребуется авторизацияВключает или выключает кэширование вопреки no-cache.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
enabled | bool | Да | true — ответ кладётся в кэш, даже если сайт просил его не кэшировать. |
{ "enabled": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
http://127.0.0.1:8891/api/v1/port/20134/cache_ignore_no_cache{
"enabled": true
}- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/auto_profile_from_uaТребуется авторизацияСообщает, выбирается ли профиль по User-Agent запроса, и показывает последний такой заголовок.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/auto_profile_from_ua{
"enabled": true,
"last_ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) …"
}/api/v1/port/{port}/auto_profile_from_uaТребуется авторизацияВключает или выключает выбор профиля по User-Agent запроса.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
enabled | bool | Да | true — приложение само называет, кем притворяться: порт подбирает профиль под присланный User-Agent. |
{ "enabled": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
http://127.0.0.1:8891/api/v1/port/20134/auto_profile_from_ua{
"enabled": true
}- В ответе PUT поля last_ua нет — оно появляется только в ответе GET, и только после того, как порт увидел первый запрос. Это единственный способ проверить, что режим срабатывает на живом трафике.
- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
Нагрузка, повторы и простой
Сколько запросов порт держит разом, что делает после сетевой ошибки и когда закрывается сам.
/api/v1/port/{port}/max_concurrentТребуется авторизацияВозвращает предел одновременных запросов на порте.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/max_concurrent{
"max_concurrent": 32
}/api/v1/port/{port}/max_concurrentТребуется авторизацияМеняет предел одновременных запросов.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
max_concurrent | int | Да | 0 и больше; 0 — без предела. |
{ "max_concurrent": 32 }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"max_concurrent":32}' \
http://127.0.0.1:8891/api/v1/port/20134/max_concurrent{
"max_concurrent": 32
}- 400 «max_concurrent must be >= 0 (0 = unlimited)» на отрицательном значении.
- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/sem_timeoutТребуется авторизацияВозвращает, сколько секунд запрос ждёт свободного места в пределе одновременных.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/sem_timeout{
"timeout_seconds": 30
}/api/v1/port/{port}/sem_timeoutТребуется авторизацияМеняет ожидание свободного места. 🔴 Поле называется timeout_seconds, а не sem_timeout.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
timeout_seconds | int | Да | 1 и больше — секунды ожидания. |
{ "timeout_seconds": 30 }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"timeout_seconds":30}' \
http://127.0.0.1:8891/api/v1/port/20134/sem_timeout{
"timeout_seconds": 30
}- 400 «timeout_seconds must be >= 1» на нуле и отрицательных.
- Тело {"sem_timeout": 30} — по имени адреса, а не поля — молча ставит 0 и получает отказ 400.
/api/v1/port/{port}/skip_retryТребуется авторизацияСообщает, повторяется ли запрос после сетевой ошибки.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/skip_retry{
"skip_retry": false
}/api/v1/port/{port}/skip_retryТребуется авторизацияВключает или выключает отказ от повторов.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
skip_retry | bool | Да | true — не повторять запрос после сетевой ошибки, отдать ошибку клиенту сразу. |
{ "skip_retry": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"skip_retry":true}' \
http://127.0.0.1:8891/api/v1/port/20134/skip_retry{
"skip_retry": true
}- Поле обязано называться именно так. Неизвестные имена сервер отбрасывает молча, и запрос применяет значение по умолчанию: false у булевых, пустую строку у строковых — с ответом 200.
/api/v1/port/{port}/retry_delayТребуется авторизацияВозвращает паузу между повторами в миллисекундах.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/retry_delay{
"retry_delay_ms": 500
}/api/v1/port/{port}/retry_delayТребуется авторизацияМеняет паузу между повторами. 🔴 Поле называется retry_delay_ms, а не retry_delay.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
retry_delay_ms | int | Да | 0 и больше — миллисекунды. |
{ "retry_delay_ms": 500 }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"retry_delay_ms":500}' \
http://127.0.0.1:8891/api/v1/port/20134/retry_delay{
"retry_delay_ms": 500
}- 400 «retry_delay_ms must be >= 0» на отрицательном значении.
- Тело {"retry_delay": 500} проходит проверку как 0 и УСТАНАВЛИВАЕТ паузу 0 мс с ответом 200.
/api/v1/port/{port}/idleТребуется авторизацияЗадаёт тайм-аут простоя ЭТОГО порта, перекрывая общий. Ручки GET у него нет — текущее значение видно как idle_seconds в ответе GET /api/v1/port/{port}/config.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
seconds | int | null | Да | Секунды простоя до автозакрытия. null снимает переопределение и возвращает порт к общему тайм-ауту; 0 — не закрывать никогда. |
{ "seconds": 1800 }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"seconds":1800}' \
http://127.0.0.1:8891/api/v1/port/20134/idle{
"status": "ok"
}- Ответ НЕ содержит установленного значения — только status. Проверять — через GET /config.
- 400 «seconds must be >= 0» на отрицательном значении; 404, если порт закрыт.
- Это единственная ручка порта, которая НЕ засчитывает обращение как активность: она разбирает номер порта сама, минуя общий помощник.
Отладочный захват отпечатков
Порт, открытый с debug_capture, складывает в кольцевой буфер отпечатки всех, кто к нему подключился. Так проверяют, каким видит клиента сама сеть. Порт захвата требует лицензии Pro: без неё POST /api/v1/ports/open с debug_capture отвечает 403 «the debug fingerprint port requires a Pro license».
/api/v1/port/{port}/capturesТребуется авторизацияВозвращает отпечатки, снятые отладочным портом захвата, от старых к новым.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
since | int | Нет | Вернуть только снимки новее указанного порядкового номера. |
limit | int | Нет | Сколько снимков вернуть максимум; 0 или отсутствие — все. |
curl -H "X-API-Key: YOUR_API_KEY" \
"http://127.0.0.1:8891/api/v1/port/20134/captures?since=0&limit=50"{
"count": 128,
"captures": [
{
"seq": 1,
"time": "2026-09-06T12:34:56.789+03:00",
"domain": "example.com",
"client_addr": "127.0.0.1:54321",
"alpn": "h2",
"method": "GET",
"path": "/",
"ua": "Mozilla/5.0 …",
"status": "ok",
"tls": {
"ja3": "771,4865-4866-4867-…",
"ja3_hash": "cd08e31494f9531f560d64c695473da9",
"ja4": "t13d1516h2_8daaf6152771_b0da82dd1658",
"client_hello_hex": "16030103…"
},
"h2": {
"available": true,
"akamai": "1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p",
"settings": [ { "id": 1, "val": 65536 } ],
"window_update": 15663105,
"pseudo_order": ["m", "a", "s", "p"],
"header_order": ["accept", "user-agent", "accept-encoding"]
}
}
]
}- 🔴 Отпечаток лежит ВНУТРИ снимка: TLS — в captures[].tls (ja3, ja3_hash, ja4, client_hello_hex), HTTP/2 — в captures[].h2 (available, akamai, settings, window_update, pseudo_order, header_order), а User-Agent — это captures[].ua. Полей ja3_hash, ja4 и user_agent на верхнем уровне снимка НЕТ.
- status — ok, когда сняты и TLS, и HTTP/2; tls-only, когда клиент не завершил h2-рукопожатие (закреплённый сертификат или обычный HTTP/1.1): тогда captures[].h2.available равно false, а остальные поля блока h2 отсутствуют.
- method, path, ua, tls.client_hello_hex и поля блока h2, кроме available, необязательны: если значение не снято, ключа в ответе просто нет.
- count — сколько снимков в буфере ВСЕГО, независимо от since и limit.
- 400 «port is not a debug fingerprint-capture port», если порт открыт без debug_capture.
- Размер кольца задаётся при открытии порта полем debug_capture_n, по умолчанию 500 снимков; переполнение вытесняет самые старые.
/api/v1/port/{port}/capturesТребуется авторизацияОчищает кольцо снимков отладочного порта захвата.
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/captures{
"count": 0,
"captures": []
}- 400 «port is not a debug fingerprint-capture port» на обычном порте.
Кэш ответов
Кэш включается на порт и работает в одном из четырёх режимов. Обычный и жёсткий — разные хранилища, а не разные настройки одного.
| Режим | Что это значит |
|---|---|
normal | Обычный кэш: живёт, пока работает приложение, слушается заголовков ответа и перепроверяет устаревшее у сервера. |
hard | Жёсткий кэш: переживает перезапуск (хранилище SQLite) и НИКОГДА не перепроверяет — поэтому изменчивые ответы в него не пускаются вовсе. |
hard-media | Жёсткий кэш только для картинок, шрифтов, аудио и видео: всё остальное проходит мимо. |
hard-autowarm | Жёсткий кэш, который принимает адрес только после трёх подряд одинаковых тел ответа — то есть когда содержимое доказало, что оно статическое. |
/api/v1/port/{port}/cacheТребуется авторизацияСостояние кэша порта: включён ли, в каком режиме и сколько уже сэкономил.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/cache{
"enabled": true,
"mode": "hard",
"hits": 18420,
"misses": 2210,
"entries": 1842,
"used_bytes": 268435456,
"max_bytes": 2147483648,
"saved_bytes": 913000000
}- mode пуст, когда кэш выключен. saved_bytes — сколько байтов не пришлось скачать заново.
/api/v1/port/{port}/cacheТребуется авторизацияВключает, выключает или переключает режим кэша на живом порте.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
mode | string | Нет | normal, hard, hard-media, hard-autowarm или пусто. |
enabled | bool | Нет | Читается ТОЛЬКО когда режим не жёсткий. false выключает кэш; при жёстком режиме поле игнорируется. |
{ "mode": "hard-media" }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"mode":"hard-media"}' \
http://127.0.0.1:8891/api/v1/port/20134/cache{
"enabled": true,
"mode": "hard-media",
"hits": 18420,
"misses": 2210,
"entries": 1842,
"used_bytes": 268435456,
"max_bytes": 2147483648,
"saved_bytes": 913000000
}- 🔴 Выключить кэш можно ровно одним телом: {"enabled": false} без mode. Любое другое тело — включая пустое {} — кэш ВКЛЮЧАЕТ в обычном режиме.
- 400 «invalid cache mode: must be one of normal, hard, hard-media, hard-autowarm» на любом другом режиме.
- 🔴 Ответ отдаёт ТОЧНОЕ имя режима: hard-media и hard-autowarm не схлопываются до hard. Проверять применение надо сравнением mode с ОТПРАВЛЕННЫМ значением, а не со строкой hard. Та же строка приходит в GET /api/v1/port/{port}/cache и в поле cache_mode у /status и /ports.
- Ответ — то же состояние, что отдаёт GET: обработчик отвечает им же.
/api/v1/port/{port}/cacheТребуется авторизацияОчищает кэш этого порта, не трогая остальные.
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/cache{
"status": "cleared"
}- Порт в жёстком режиме чистит ОБЩЕЕ жёсткое хранилище — то же, что чистит DELETE /api/v1/cache/hard.
/api/v1/cache/hardТребуется авторизацияСостояние жёсткого кэша целиком: сколько записей, сколько места и сколько сэкономлено.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/cache/hard{
"enabled": true,
"mode": "hard",
"hits": 18420,
"misses": 2210,
"entries": 1842,
"used_bytes": 268435456,
"max_bytes": 2147483648,
"saved_bytes": 913000000
}/api/v1/cache/hardТребуется авторизацияОчищает жёсткий кэш целиком — у всех портов сразу.
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/cache/hard{
"status": "hard cache cleared"
}/api/v1/cache/hard/evictТребуется авторизацияВыбрасывает из жёсткого кэша записи по списку образцов — точечно, вместо полной очистки.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
patterns | array | Да | Домены или полные адреса. Список обязателен. |
{ "patterns": ["example.com", "https://cdn.example.net/img/logo.png"] }curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"patterns":["example.com"]}' \
http://127.0.0.1:8891/api/v1/cache/hard/evict{
"evicted": 37
}- 400 «patterns list is required» на пустом списке. Поля domain у этой ручки нет.
/api/v1/cache/evictТребуется авторизацияТо же для обычного кэша: выбрасывает записи по списку образцов.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
patterns | array | Да | Домены или полные адреса. Список обязателен. |
{ "patterns": ["example.com"] }curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"patterns":["example.com"]}' \
http://127.0.0.1:8891/api/v1/cache/evict{
"evicted": 12
}- 400 «patterns list is required» на пустом списке.
/api/v1/cache/hard/saveТребуется авторизацияОставлена ради прежних клиентов: жёсткий кэш лежит в SQLite и пишется на диск при каждой записи, так что сбрасывать его отдельно не нужно. Ручка просто отдаёт состояние.
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/cache/hard/save{
"status": "persisted",
"entries": 1842,
"info": "SQLite-backed cache is already persistent"
}/api/v1/cache/hard/exclusionsТребуется авторизацияДомены и адреса, которые жёсткий кэш обходит стороной.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/cache/hard/exclusions{
"exclusions": ["api.example.com", "*.example.net/checkout"]
}/api/v1/cache/hard/exclusionsТребуется авторизацияДОПИСЫВАЕТ присланные образцы к списку исключений жёсткого кэша, а не заменяет его.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
exclusions | array | Да | Домены или маски адресов, которые кэшировать не надо. |
{ "exclusions": ["api.example.com"] }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"exclusions":["api.example.com"]}' \
http://127.0.0.1:8891/api/v1/cache/hard/exclusions{
"exclusions": ["api.example.com", "*.example.net/checkout"]
}- 🔴 Убрать домен из исключений укороченным списком через PUT НЕЛЬЗЯ: список только растёт, и присланное сливается с прежним без повторов. Для удаления есть DELETE.
- Ответ — полный список после слияния.
/api/v1/cache/hard/exclusionsТребуется авторизацияУдаляет ПЕРЕЧИСЛЕННЫЕ образцы из списка исключений жёсткого кэша. Тело обязательно.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
exclusions | array | Да | Что удалить из списка. |
{ "exclusions": ["api.example.com"] }curl -X DELETE -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"exclusions":["api.example.com"]}' \
http://127.0.0.1:8891/api/v1/cache/hard/exclusions{
"exclusions": ["*.example.net/checkout"]
}- 🔴 Это НЕ «очистить список». DELETE без тела отвечает 400 «invalid JSON body». Чтобы опустошить список, перечислите в теле всё, что вернул GET.
- 🔴 Если перечислить всё и список опустеет, ответ придёт как {"exclusions": null}, а не с пустым массивом — ровно как у обычного кэша.
/api/v1/cache/exclusionsТребуется авторизацияТо же для обычного кэша: список исключений.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/cache/exclusions{
"exclusions": ["login.example.com"]
}/api/v1/cache/exclusionsТребуется авторизацияДОПИСЫВАЕТ образцы к списку исключений обычного кэша.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
exclusions | array | Да | Домены или маски адресов, которые кэшировать не надо. |
{ "exclusions": ["login.example.com"] }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"exclusions":["login.example.com"]}' \
http://127.0.0.1:8891/api/v1/cache/exclusions{
"exclusions": ["login.example.com"]
}- 🔴 Ответ этой ручки показывает только ПРИСЛАННОЕ, а не полный список после слияния — в отличие от жёсткого кэша. Полный список отдаёт GET.
/api/v1/cache/exclusionsТребуется авторизацияУдаляет перечисленные образцы из списка исключений обычного кэша. Тело обязательно.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
exclusions | array | Да | Что удалить из списка. |
{ "exclusions": ["login.example.com"] }curl -X DELETE -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"exclusions":["login.example.com"]}' \
http://127.0.0.1:8891/api/v1/cache/exclusions{
"exclusions": null
}- 🔴 Когда после удаления не остаётся ни одного образца, поле приходит как null, а НЕ как []. Пустой массив эта ручка не отдаёт никогда, поэтому клиент, написанный как resp.exclusions.map(…) или for … of, падает именно на успешном полном очищении — проверяйте на null. GET того же списка в том же состоянии вернёт []: формы ответа у GET и DELETE РАЗНЫЕ.
- DELETE без тела — 400 «invalid JSON body».
Журнал запросов порта
Журнал пишет по строке JSON на каждый запрос, прошедший через порт: время, метод, хост, путь, код ответа, тип и размер тела, заголовки кэширования и вердикт кэша. Им разбирают, почему кэш не срабатывает и куда уходит трафик.
/api/v1/port/{port}/traffic_logТребуется авторизацияСообщает, включён ли журнал, где лежит файл и сколько в нём записей.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/traffic_log{
"enabled": true,
"path": "data/traffic_port_20134.jsonl",
"entries": 4821
}- 🔴 entries — ЧИСЛО записей, а не сами записи. Записи отдаёт /traffic_log/download.
- Счётчик описывает ФАЙЛ, а не сеанс: после перезапуска он показывает всё, что уже лежит на диске.
- Когда журнал выключен, ответ — {"enabled": false, "entries": 0}: path отсутствует, а entries приходит ВСЕГДА и равен 0. Отличать «выключен» от «включён и пуст» надо по enabled, а не по наличию ключа entries.
/api/v1/port/{port}/traffic_logТребуется авторизацияВключает или выключает журнал на живом порте. При включении файл создаётся, если его ещё нет.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
enabled | bool | Да | true — начать писать; false — закрыть файл и перестать. |
{ "enabled": true }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
http://127.0.0.1:8891/api/v1/port/20134/traffic_log{
"enabled": true,
"path": "data/traffic_port_20134.jsonl",
"entries": 4821
}- Ответ — то же состояние, что отдаёт GET.
- 500 «failed to create traffic log: …», если файл не удалось создать — например, каталог data недоступен на запись.
- Поле обязано называться enabled: неизвестное имя даёт false, то есть ВЫКЛЮЧАЕТ журнал с ответом 200.
/api/v1/port/{port}/traffic_log/downloadТребуется авторизацияОтдаёт файл журнала целиком — по строке JSON на запрос (формат NDJSON).
curl -H "X-API-Key: YOUR_API_KEY" -o traffic.jsonl \
http://127.0.0.1:8891/api/v1/port/20134/traffic_log/download{"ts":"2026-09-06T11:22:33Z","method":"GET","scheme":"https","host":"example.com",
"path":"/static/app.js","status":200,"content_type":"application/javascript",
"content_len":184320,"cache_control":"max-age=31536000","cache":"hit","proto":"h2","port":20134}- Content-Type — application/x-ndjson, имя вложения traffic_port_<порт>.jsonl. Поле cache принимает значения hit, miss, stale, excluded и skip.
- 400 «traffic logging is not enabled», если журнал выключен.
/api/v1/port/{port}/traffic_logТребуется авторизацияОпустошает файл журнала, оставляя запись включённой.
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/traffic_log{
"status": "cleared"
}- 400 «traffic logging is not enabled», если журнал выключен: очистить файл выключенного журнала этой ручкой нельзя — сначала включите его.
Перехват системного трафика и аудит утечек
Перехват уводит на порт трафик программы или всей машины без настройки прокси в самом приложении. Аудит утечек отвечает на обратный вопрос: не ходит ли подопытная программа МИМО перехвата.
/api/v1/system/interceptТребуется авторизацияДоступна ли привилегированная служба перехвата прямо сейчас. Спрашивать её нужно ДО открытия порта: иначе отказ придёт уже после заполнения формы.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/system/intercept{
"available": false,
"code": "not_installed",
"reason": "привилегированная служба перехвата не установлена — установите её из установщика BlankTrail"
}- Поля code и reason не дублируют друг друга: по code ветвится программа, reason читает человек. Оба поля есть всегда — при available=true они пусты.
- Значения code: off — перехват недоступен в этой сборке или на этой платформе; not_installed — служба не установлена; unreachable — служба не отвечает; busy — перехватом уже управляет другая копия приложения, служба обслуживает одного клиента за раз.
/api/v1/system/processesТребуется авторизацияСписок приложений, из которого выбирают цели попроцессного перехвата.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/system/processes[
{ "name": "chrome.exe", "path": "C:\\Program Files\\Google\\Chrome\\chrome.exe",
"count": 7 },
{ "name": "curl.exe", "path": "C:\\Windows\\System32\\curl.exe", "count": 1 }
]- Строка списка — ИСПОЛНЯЕМЫЙ ФАЙЛ, а не процесс: правило перехвата цепляется к exe, поэтому семь окон браузера дают одну строку с count = 7. Путь нормализован — именно его и надо класть в intercept_apps.
- 501 на платформе, которая перечислять процессы не умеет: пустой массив был бы неотличим от «процессов нет», и человек увидел бы пустую таблицу выбора вместо объяснения.
/api/v1/system/leak-auditТребуется авторизацияНачинает сеанс наблюдения: смотрит, ходит ли выбранная программа мимо перехвата.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
exe_paths | array | Да | Пути к исполняемым файлам, за которыми наблюдать. 🔴 Поле называется exe_paths, а не paths. |
policy | string | Да | observe — только наблюдать; block — ещё и обрывать то, что пошло мимо. |
{ "exe_paths": ["C:\\Program Files\\App\\app.exe"], "policy": "observe" }curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"exe_paths":["C:\\Program Files\\App\\app.exe"],"policy":"observe"}' \
http://127.0.0.1:8891/api/v1/system/leak-audit{
"started": true,
"code": "",
"reason": ""
}- 🔴 Отказ старта приходит с кодом 200 и started=false: код 200 здесь НЕ означает, что наблюдение началось. Проверять надо поле started, а не статус ответа.
- Коды отказа при 200: ipv6_present — у машины живой глобальный IPv6, захват маршрута его не покрывает; session_active — сеанс уже идёт; rule_conflict — правило конфликтует с текущим перехватом; service_outdated и service_version_unknown — служба старая или её версию не удалось определить.
- 400 с кодом no_paths, если список пуст, и с кодом invalid_request, если тело не разбирается или политика не observe и не block. 503, если менеджер портов не поднят.
/api/v1/system/leak-auditТребуется авторизацияСостояние живого сеанса: вердикт, агрегаты, границы честности и свежий хвост событий.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/system/leak-audit{
"exe_paths": ["C:\\Program Files\\App\\app.exe"],
"policy": "observe",
"started_at": "2026-09-06T11:00:00Z",
"verdict": "leaking",
"confirmed": true,
"by_class": { "dns_direct": 12, "ip_direct": 3 },
"top_targets": [ { "addr": "203.0.113.7:443", "count": 9 } ],
"dropped": 0,
"udp_exhausted": 0,
"client_trimmed": 0,
"connection_interrupted": false,
"audit_unavailable": false,
"stopped_by": "",
"limits": ["attribution_by_exe"],
"recent_events": []
}- verdict принимает три значения: clean, leaking и inconclusive. Последнее — честное «не знаем»: например, когда журнал аудита недоступен или события терялись.
- limits перечисляет границы честности этого прогона: attribution_by_exe (правило цепляется к exe, а не к процессу), start_window (окно между запросом правила и его применением), events_dropped, udp_exhausted, connection_interrupted, audit_unavailable, stopped_by_watchdog и другие. Читать вердикт без них нельзя.
- 404 «сеанс аудита утечек не запущен», пока сеанс не начат: пустой отчёт был бы не честнее молчания.
/api/v1/system/leak-audit/reportТребуется авторизацияОтчёт без ленты событий — им выгружают результат. Если живого сеанса нет, отдаёт отчёт последнего завершённого.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/system/leak-audit/report- 404 «аудит утечек ни разу не запускался», если завершённого отчёта тоже нет.
/api/v1/system/leak-auditТребуется авторизацияОстанавливает сеанс и отдаёт итоговый отчёт в той же форме, что GET.
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/system/leak-audit- 404, если сеанса нет. У остановленного сеанса поле stopped_by говорит, кто его закончил — человек или сторож, который сам обрывает затянувшееся наблюдение.
Проверки порта
Две проверки на живом порте и одна вспомогательная. Полная проверка гоняет запрос НАСКВОЗЬ через порт и сверяет, каким его увидели снаружи, с тем, кем он собирался притворяться.
/api/v1/port/{port}/testТребуется авторизацияПолная проверка настройки порта: утечки на выходе, сквозной отпечаток и поддержка UDP.
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/test{
"leak": {
"exit_ip": "203.0.113.45",
"egress_resolver": "203.0.113.53",
"host_resolver": "192.0.2.1",
"dns": "pass",
"ipv6": "pass",
"latency_ms": 214,
"checked_at": "2026-09-06T11:03:01Z"
},
"leak_skipped": false,
"fingerprint": {
"ran": true,
"skipped": false,
"observed_ja3": "cd08e31494f9531f560d64c695473da9",
"observed_ja4": "t13d1516h2_8daaf6152771_b0da82dd1658",
"observed_ua": "Mozilla/5.0 …",
"expected_ja4": "t13d1516h2_8daaf6152771_b0da82dd1658",
"expected_ua": "Mozilla/5.0 …",
"match": true
},
"udp": { "checked": true, "supported": true, "detail": "DNS round-trip via UDP ASSOCIATE",
"verdict": "relays" },
"ok": true,
"messages": [],
"checked_at": "2026-09-06T11:03:01Z"
}- Проверка отпечатка ходит на внешнюю службу снятия отпечатков через сам порт: match=false означает, что наружу уехало не то, что порт обещал.
- leak_skipped=true с leak_skip_reason различает две вещи: disabled — проверка выключена в настройках порта, direct — выход прямой и проверять нечего. Это НЕ «утечек нет».
- fingerprint.skipped с причиной passthrough — не сбой: при сквозном пропуске TLS подмены нет, и наблюдать нечего.
- udp.supported=true означает завершённый круг DNS через выход. Разрешённый UDP ASSOCIATE без ответа поддержкой НЕ считается — вердикт accepted_but_silent.
- Проверка ограничена 15 секундами.
/api/v1/port/{port}/leakcheckТребуется авторизацияТолько проверка утечек на выходе: сравнивает, чей резолвер отвечает и чей IPv6 виден.
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/port/20134/leakcheck{
"report": {
"exit_ip": "203.0.113.45",
"egress_resolver": "203.0.113.53",
"host_resolver": "192.0.2.1",
"dns": "pass",
"ipv6": "leak",
"ipv6_addr": "2001:db8::1",
"host_ipv6": "2001:db8::1",
"latency_ms": 214,
"checked_at": "2026-09-06T11:03:01Z"
}
}- Поля dns и ipv6 принимают три значения: pass, leak и inconclusive. Полей webrtc и verdict у этой ручки нет.
- 🔴 На порте с прямым выходом ответ — {"skipped": true} БЕЗ отчёта, с кодом 200. Это значит «не проверяли», а не «утечек нет».
- ipv6_addr рядом с host_ipv6 — это и есть доказательство: совпали значит наружу видно адрес самой машины, то есть туннель обошли. Проверка ограничена 12 секундами.
/api/v1/port/{port}/generateТребуется авторизацияСобирает профиль для запрошенного браузера и версии и ставит его на порт.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
browser | string | Да | chrome, firefox, safari или edge. |
version | int | Нет | Версия браузера; 0 — версия по умолчанию. |
os | string | Нет | windows, macos, linux, ios или android; пусто — windows. |
save | bool | Нет | Сохранить профиль в базе для повторного использования. |
{ "browser": "chrome", "version": 152, "os": "windows", "save": true }curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"browser":"chrome","version":152,"os":"windows"}' \
http://127.0.0.1:8891/api/v1/port/20134/generate- 400 «browser is required (chrome, firefox, safari, edge)», 400 «browser must be one of: chrome, firefox, safari, edge», 400 «os must be one of: windows, macos, linux, ios, android»; 500 с текстом движка, если профиль собрать не удалось.