API — Порты и трафик

Открывайте, закрывайте, перечисляйте и настраивайте прокси-порты, а также тестируйте вышестоящий прокси (upstream) перед его использованием. Это эндпоинты, которые вы будете применять чаще всего при интеграции BlankTrail Proxy.

Открыть порт

Один запрос поднимает локальный прокси-порт и сразу задаёт всё его поведение. Полей много, обязательное одно — port; остальные берут значения по умолчанию, а поменять их потом можно на живом порте.

POST/api/v1/ports/openТребуется авторизация

Поднимает прокси-порт с заданной личностью и поведением.

ПараметрТипОбязательныйОписание
portintДаНомер порта, 1–65535.
protocolstringНетhttp (по умолчанию), socks5 или mtproto.
upstreamstringНетВыходной прокси; пусто — напрямую.
modestringНетКак выбирается личность: random, db, auto, specific, custom.
browserstringНетФильтр браузера; несовместим с mode=random.
osstringНетФильтр ОС; несовместим с mode=random.
Тело запроса
{
  "port": 20134,
  "protocol": "socks5",
  "mode": "db",
  "browser": "chrome",
  "os": "windows",
  "upstream": "socks5://user:pass@host:1080"
}
Пример (curl)
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…, в том числе сгенерированный).

Выход в сеть

ПолеТипПо умолчаниюЧто делает
upstreamstring—Выходной прокси: scheme://[user:pass@]host:port, схемы socks5, socks5h, http. Пусто — выход напрямую.
chain_proxystring—Первое звено цепочки перед выходным прокси.
upstream_gatewaystring—Имя сохранённого шлюза; его локальный SOCKS5 становится выходом.
chain_gatewaystring—Имя сохранённого шлюза для первого звена цепочки.
ovpn_configstring—Устаревший алиас upstream_gateway; оставлен ради прежних клиентов.
upstream_tls_insecureboolfalseСнять проверку сертификата у https-прокси. Только для своего прокси с самоподписанным сертификатом.
allow_mitm_upstreamboolfalseРазрешить выход, который сам вскрывает TLS. По умолчанию запрещено: такой выход стирает отпечаток.
egress_force_ipv4booltrueВыходить только по IPv4 — защита от утечки через IPv6.
block_private_targetsbooltrueОтказывать в соединениях на частные, локальные и CGNAT-адреса: иначе прокси становится картой локальной сети.

Личность и протокол

ПолеТипПо умолчаниюЧто делает
modestringrandomrandom, db (алиас database), auto, specific или custom. С фильтрами browser/os режим random несовместим.
browserstring—Фильтр браузера: chrome, firefox, safari, edge, random; можно с версией — chrome_145.
osstring—Фильтр ОС: windows, macos, linux, ios, android, random.
specific_profilestring—Имя профиля для mode=specific, например chrome_152_windows.
custom_tlsobject—Снятый отпечаток целиком для mode=custom; та же форма, что у PUT /port/{port}/custom_tls.
auto_profile_from_uaboolfalseВыбирать профиль по User-Agent запроса.
h2_spoofingbool—Подменять параметры HTTP/2 под профиль браузера.
spoof_user_agentbool—Подменять User-Agent. Выключено — заголовок клиента идёт байт в байт.
spoof_headersbool—Приводить набор и порядок заголовков к браузерному.
tls_passthroughboolfalseПропускать TLS насквозь без вскрытия: отпечаток клиента сохраняется, содержимое не читается.
tls_mirrorboolfalseВскрывать TLS, но повторять наружу снятый отпечаток самого клиента.
session_resumptionbool—Разрешать возобновление сессий TLS (тикеты).
enable_http3boolfalseПереоткрывать соединение по HTTP/3 там, где сайт предлагает h3 и выход умеет UDP.
decompressbool—Распаковывать br/gzip/zstd перед отдачей клиенту. Режим решателя челленджей включает принудительно.

Нагрузка и тайм-ауты

ПолеТипПо умолчаниюЧто делает
max_concurrentint—Предел одновременных запросов; 0 — без предела.
sem_timeoutint—Сколько секунд запрос ждёт места в этом пределе. 🔴 Здесь поле называется sem_timeout — в отличие от отдельной ручки, где оно timeout_seconds.
skip_retryboolfalseНе повторять запрос после сетевой ошибки.
retry_delay_msint—Пауза между повторами, миллисекунды.
timeout_secondsint—Тайм-аут простоя СОЕДИНЕНИЯ (не порта), секунды.
connect_timeout_secondsint5Потолок ОДНОЙ попытки набора.
request_timeout_secondsint30Потолок всей фазы установления вместе с повторами; 0 — без ограничения.
idle_secondsint | nullnullТайм-аут простоя ПОРТА, перекрывающий общий (по умолчанию 30 минут); 0 — не закрывать никогда.

Кэш, журнал и захват

ПолеТипПо умолчаниюЧто делает
cache_enabledboolfalseВключить кэш ответов на порту.
cache_modestringnormalnormal, hard, hard-media или hard-autowarm.
cache_ignore_no_cacheboolfalseКэшировать вопреки заголовку no-cache.
traffic_logboolfalseПисать журнал запросов порта в data/traffic_port_<порт>.jsonl.
debug_captureboolfalseОткрыть порт захвата отпечатков. 🔴 Требует лицензии Pro: иначе 403.
debug_capture_nint500Размер кольца снимков на порту захвата.

Челленджи и сессии

ПолеТипПо умолчаниюЧто делает
js_solverboolfalseРешатель челленджей: запросы, упёршиеся в проверку, уходят в пул решателя. Требует вскрытия TLS (несовместимо с tls_passthrough).
keep_sessionsboolfalseПорт держит свою банку кук по доменам: впитывает Set-Cookie, подставляет куки и идёт по перенаправлениям. От js_solver не зависит.

DNS и защита от утечек

ПолеТипПо умолчаниюЧто делает
leak_guardstring—Предстартовая проверка утечек DNS и IPv6 на выходе: off, warn или enforce.
vdns_modestringoffВиртуальный DNS: off, on_leak (включать при обнаруженной утечке) или forced. Тарифы Standard и выше: на Lite поле принимается, порт открывается с выключенным vdns, а vdns_path_reason у него — plan.
resolver_strategystringautoauto или custom — откуда брать резолверы.
custom_resolversarray—Список host:port для resolver_strategy=custom.
ecs_enabledbooltrueПередавать подсеть клиента в запросе DNS (EDNS Client Subnet).
vdns_strict_bypassboolfalseСтрогий обход: наружу уходит только IP-литерал, имя хоста не покидает машину.
require_udp_dnsboolfalseОткрывать порт ТОЛЬКО если выход доказал, что умеет проносить UDP для DNS. Требует vdns_mode on_leak или forced, иначе 400.

Перехват системного трафика

Вниманиеintercept_scope по умолчанию — system, то есть ВСЯ машина, а не выбранные программы. Порт с перехватом уводит в туннель весь трафик, включая ваше собственное удалённое управление этой машиной. Чтобы вести только выбранные приложения, задайте intercept_scope=process и перечислите их в intercept_apps.
ПолеТипПо умолчаниюЧто делает
interceptboolfalseУводить в порт системный трафик без настройки прокси в приложении.
intercept_scopestringsystem🔴 system — ВСЯ машина (значение по умолчанию), process — только перечисленные программы.
intercept_appsarray—Пути к exe для scope=process.

MTProto (прокси для Telegram)

Поля ниже имеют смысл только при protocol=mtproto. Порт в этом режиме говорит на протоколе Telegram, а не на HTTP или SOCKS5, и подключается к нему клиент Telegram по ссылке из ответа.

ПолеТипПо умолчаниюЧто делает
mtproto_secretstring—Каноническая строка секрета вида ee…; пусто — сгенерировать новый.
mtproto_camouflage_domainstringwww.google.comДомен маскировки — он же SNI, который предъявляет клиент Telegram.
mtproto_fallback_realbooltrueОтправлять сканеров и клиентов с неверным секретом на настоящий сайт маскировки.
mtproto_egressstringautoauto, obfuscated или faketls — как идти к вышестоящему прокси MTProto.
mtproto_faketls_upstreamstring—host:port вышестоящего прокси MTProto для egress=faketls.
mtproto_faketls_secretstring—Секрет ee… этого вышестоящего прокси.

Закрыть порт

POST/api/v1/ports/closeТребуется авторизация

Закрывает открытый порт и рвёт все идущие через него соединения.

ПараметрТипОбязательныйОписание
portintДаПорт, который нужно закрыть.
Тело запроса
{ "port": 20134 }
Пример (curl)
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, если порт не открыт.
  • Порт с перехватом при закрытии снимает и своё правило перехвата — трафик возвращается на обычный маршрут.

Список открытых портов

GET/api/v1/portsТребуется авторизация

Возвращает все открытые порты с их полной конфигурацией и состоянием.

Пример (curl)
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.

Подобрать свободный порт

GET/api/v1/ports/suggestТребуется авторизация

Возвращает наименьший номер в диапазоне 20000–29999, который и не занят менеджером, и реально поддаётся привязке в системе.

Пример (curl)
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 и не течёт, — до того, как на нём построят работу.

POST/api/v1/upstream/testТребуется авторизация

Проверяет доступность выхода, поддержку UDP через SOCKS5 и утечки DNS и IPv6.

ПараметрТипОбязательныйОписание
checksarrayДаЛюбые из http, udp, leak.
protocolstringНетsocks5 или http — протокол будущего порта.
upstreamstringНетВыходной прокси; пусто — проверять прямое подключение.
chain_proxystringНетПервое звено цепочки.
upstream_gatewaystringНетИмя сохранённого шлюза вместо адреса выхода.
chain_gatewaystringНетИмя сохранённого шлюза для первого звена.
upstream_tls_insecureboolНетЗеркалит одноимённую настройку порта: без неё проверялась бы не та конфигурация, которую вы собираетесь открыть.
Тело запроса
{
  "checks": ["http", "udp", "leak"],
  "protocol": "socks5",
  "upstream": "socks5://user:pass@host:1080"
}
Пример (curl)
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 — единственный способ поменять то, у чего нет своей ручки.

GET/api/v1/port/{port}/statusТребуется авторизация

Сводка о живом порте: личность, поведение, выход, счётчики отказов и время последней активности.

Пример (curl)
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 двигает не только трафик, но и любое обращение к ручкам этого порта.
GET/api/v1/port/{port}/configТребуется авторизация

Полный снимок конфигурации порта — все примерно шестьдесят ключей, включая те, у которых нет своей ручки. Ключи без значения в снимок не попадают.

Пример (curl)
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.
PUT/api/v1/port/{port}/configТребуется авторизация

Меняет конфигурацию живого порта. Присланные поля НАКЛАДЫВАЮТСЯ на текущий снимок: то, чего в теле нет, остаётся как было.

Тело запроса
{ "mode": "auto", "browser": "firefox", "os": "macos" }
Пример (curl)
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 меняет его на живом порте: порт не закрывается и не перезапускается.

ВниманиеПоля value не существует ни у одной ручки. У каждой своё имя поля — оно указано в таблице отдельной колонкой, и у трёх ручек (/sem_timeout, /retry_delay, /idle) не совпадает с последним сегментом адреса. Неизвестное поле сервер отбрасывает молча: {"value":true} на булевой настройке отвечает 200 и ВЫКЛЮЧАЕТ её, а на строковой — очищает фильтр.

Отказы, общие для всех ручек порта: 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.

ПримечаниеЛюбое обращение к ручке порта считается активностью и сбрасывает тайм-аут простоя. Мониторинг, раз в минуту опрашивающий GET /status, не даст порту закрыться никогда.
АдресМетодыПоле телаТипЗначения и оговорки
/modeGET, PUTmodestringrandom, db (алиас database), auto, specific
/browserGET, PUTbrowserstringпусто, random, chrome, firefox, safari, edge; можно с версией: chrome_145
/osGET, PUTosstringпусто, random, windows, macos, linux, ios, android
/profileGET——только чтение; закрепить профиль — через /mode или /config
/profile/viewGET——только чтение: состав текущего профиля
/rotatePOST——тела нет; выдаёт новый профиль немедленно
/custom_tlsPUTja3, ja4, …objectснятый отпечаток целиком; переводит порт в режим custom
/upstreamGET, PUTupstreamstringадрес выходного прокси; пустая строка — выход напрямую
/chain_proxyGET, PUTchain_proxystringпервое звено цепочки перед выходным прокси
/allow_mitm_upstreamGET, PUTallow_mitm_upstreamboolполе обязательно: без него 400, а не false
/h2_spoofingGET, PUTenabledbooltrue или false
/spoof_user_agentGET, PUTenabledbooltrue или false
/spoof_headersGET, PUTenabledbooltrue или false
/tls_passthroughGET, PUTenabledbooltrue или false
/tls_mirrorGET, PUTenabledbooltrue или false
/http3GET, PUTenabledbooltrue или false
/session_resumptionGET, PUTenabledbooltrue или false
/decompressGET, PUTenabledbooltrue или false
/auto_profile_from_uaGET, PUTenabledboolGET отдаёт ещё last_ua
/cache_ignore_no_cacheGET, PUTenabledbooltrue или false
/max_concurrentGET, PUTmax_concurrentint0 и больше; 0 — без предела
/sem_timeoutGET, PUTtimeout_secondsint1 и больше — имя поля НЕ совпадает с адресом
/skip_retryGET, PUTskip_retrybooltrue или false
/retry_delayGET, PUTretry_delay_msint0 и больше — имя поля НЕ совпадает с адресом
/idlePUTsecondsint | nullnull — вернуться к общему тайм-ауту, 0 — не закрывать; GET нет
/cacheGET, PUT, DELETEenabled, modebool, stringсм. раздел «Кэш ответов»
/traffic_logGET, PUT, DELETEenabledboolсм. раздел «Журнал запросов порта»
/capturesGET, 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, а если правили, перечитайте секрет и раздайте ссылку заново.

Личность порта

Кем порт представляется сайту: как выбирается профиль, чем ограничен выбор и как подать свой отпечаток.

GET/api/v1/port/{port}/modeТребуется авторизация

Возвращает режим выбора профиля.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/mode
Ответ
{
  "mode": "db"
}
PUT/api/v1/port/{port}/modeТребуется авторизация

Меняет режим выбора профиля на живом порте.

ПараметрТипОбязательныйОписание
modestringДаrandom — синтетический отпечаток; db (алиас database) — настоящий профиль из базы; auto — сгенерированный под запрошенные браузер и ОС; specific — закреплённый по имени.
specific_profilestringНетИмя профиля для mode=specific, например chrome_152_windows.
Тело запроса
{ "mode": "specific", "specific_profile": "chrome_152_windows" }
Пример (curl)
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 не рвутся.
GET/api/v1/port/{port}/browserТребуется авторизация

Возвращает фильтр браузера. Пустая строка — фильтра нет.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/browser
Ответ
{
  "browser": "chrome"
}
PUT/api/v1/port/{port}/browserТребуется авторизация

Сужает выбор профиля до одного браузера или снимает сужение.

ПараметрТипОбязательныйОписание
browserstringДаchrome, firefox, safari, edge, random или пустая строка (снять фильтр). Можно закрепить версию суффиксом: chrome_145.
Тело запроса
{ "browser": "chrome_145" }
Пример (curl)
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.
GET/api/v1/port/{port}/osТребуется авторизация

Возвращает фильтр операционной системы.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/os
Ответ
{
  "os": "windows"
}
PUT/api/v1/port/{port}/osТребуется авторизация

Сужает выбор профиля до одной операционной системы или снимает сужение.

ПараметрТипОбязательныйОписание
osstringДаwindows, macos, linux, ios, android, random или пустая строка (снять фильтр).
Тело запроса
{ "os": "macos" }
Пример (curl)
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.
GET/api/v1/port/{port}/profileТребуется авторизация

Возвращает профиль, которым порт представляется сейчас. Только чтение: PUT на этот адрес даёт 405.

Пример (curl)
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.
PUT/api/v1/port/{port}/custom_tlsТребуется авторизация

Подаёт порту снятый снаружи отпечаток целиком и переводит его в режим custom. Так порт получает форму, снятую с настоящего браузера — например через ChromeApi или tls.peet.ws.

ПараметрТипОбязательныйОписание
ja3stringНетСтрока JA3 целиком.
ja3_hashstringНетХеш JA3, если он снят.
ja4stringНетСтрока JA4.
ciphersarrayДаСписок шифров в порядке ClientHello. Единственное обязательное поле. Имена разбираются по фиксированной таблице; начинающиеся на TLS_GREASE занимают своё место как GREASE, незнакомые молча отбрасываются. Если после разбора не осталось ни одного шифра — 400. Из ja3 шифры НЕ достаются: эта строка нужна только для порядка расширений.
extensionsarray<object>НетСписок расширений в порядке ClientHello. Элемент — объект: обязательное name, необязательные supported_groups, signature_algorithms, versions, protocols.
supportedGroupsarrayНетГруппы кривых.
signatureAlgorithmsarrayНетАлгоритмы подписи.
alpnarrayНетСписок ALPN.
h2objectНетПараметры HTTP/2: settings, windowUpdate, akamai_fingerprint, headerOrder.
userAgentstringНетUser-Agent, которому соответствует отпечаток.
Пример (curl)
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.

GET/api/v1/port/{port}/upstreamТребуется авторизация

Возвращает выходной прокси порта. Пустые значения — выход напрямую.

Пример (curl)
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.
PUT/api/v1/port/{port}/upstreamТребуется авторизация

Меняет выходной прокси на живом порте — так вращают прокси, не закрывая порт.

ПараметрТипОбязательныйОписание
upstreamstringНетАдрес вида scheme://[user:pass@]host:port; схемы socks5, socks5h, http. Пустое тело или пустая строка возвращают порт на прямой выход.
socks5_addrstringНетУстаревший алиас того же поля; читается, только если upstream пуст.
Тело запроса
{ "upstream": "socks5://user:pass@host:1080" }
Пример (curl)
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"
}
  • Побочное действие при ФАКТИЧЕСКОЙ смене адреса: решённые челленджи порта гасятся, потому что клиренс выдан прежнему выходному адресу и с нового не работает. Следующий запрос к защищённому сайту снова пройдёт через проверку.
  • Кэшированное соединение сбрасывается, простаивающие соединения к прежнему выходу закрываются.
GET/api/v1/port/{port}/chain_proxyТребуется авторизация

Возвращает промежуточный прокси — первое звено цепочки перед выходным.

Пример (curl)
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"
}
PUT/api/v1/port/{port}/chain_proxyТребуется авторизация

Ставит или снимает промежуточный прокси: трафик идёт клиент → цепочка → выход → сайт.

ПараметрТипОбязательныйОписание
chain_proxystringДаАдрес того же вида, что у upstream. Пустая строка снимает звено.
Тело запроса
{ "chain_proxy": "http://10.0.0.5:3128" }
Пример (curl)
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.
GET/api/v1/port/{port}/allow_mitm_upstreamТребуется авторизация

Сообщает, разрешён ли выход, подменяющий TLS, и сколько соединений уже отвергнуто по этой причине.

Пример (curl)
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 — готовый ответ на вопрос «почему отпечаток не доезжает»: издатель последнего подменённого сертификата назван прямо.
PUT/api/v1/port/{port}/allow_mitm_upstreamТребуется авторизация

Разрешает или запрещает работу через выход, который вскрывает и пересобирает TLS.

ПараметрТипОбязательныйОписание
allow_mitm_upstreamboolДаtrue — работать даже через такой выход; false — отвергать соединения через него. Выключено по умолчанию: такой выход стирает отпечаток, и порт молча перестаёт делать то, ради чего открыт.
Тело запроса
{ "allow_mitm_upstream": true }
Пример (curl)
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, ответ повторяет применённое значение.

GET/api/v1/port/{port}/h2_spoofingТребуется авторизация

Сообщает, подменяются ли параметры HTTP/2 под выбранный браузер.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/h2_spoofing
Ответ
{
  "enabled": true
}
PUT/api/v1/port/{port}/h2_spoofingТребуется авторизация

Включает или выключает подмену параметров HTTP/2.

ПараметрТипОбязательныйОписание
enabledboolДаtrue — кадр SETTINGS, приоритеты и порядок псевдозаголовков берутся у профиля браузера.
Тело запроса
{ "enabled": true }
Пример (curl)
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.
GET/api/v1/port/{port}/spoof_user_agentТребуется авторизация

Сообщает, подменяется ли User-Agent запроса.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/spoof_user_agent
Ответ
{
  "enabled": true
}
PUT/api/v1/port/{port}/spoof_user_agentТребуется авторизация

Включает или выключает подмену User-Agent.

ПараметрТипОбязательныйОписание
enabledboolДаtrue — заголовок берётся у профиля; false — заголовок клиента идёт байт в байт.
Тело запроса
{ "enabled": true }
Пример (curl)
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.
GET/api/v1/port/{port}/spoof_headersТребуется авторизация

Сообщает, приводится ли набор и порядок заголовков к браузерному.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/spoof_headers
Ответ
{
  "enabled": true
}
PUT/api/v1/port/{port}/spoof_headersТребуется авторизация

Включает или выключает приведение заголовков к браузерному виду.

ПараметрТипОбязательныйОписание
enabledboolДаtrue — набор, регистр и порядок заголовков совпадают с профилем браузера.
Тело запроса
{ "enabled": true }
Пример (curl)
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.
GET/api/v1/port/{port}/tls_passthroughТребуется авторизация

Сообщает, пропускается ли TLS без вскрытия.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/tls_passthrough
Ответ
{
  "enabled": true
}
PUT/api/v1/port/{port}/tls_passthroughТребуется авторизация

Включает или выключает сквозной пропуск TLS.

ПараметрТипОбязательныйОписание
enabledboolДаtrue — соединение проходит насквозь: отпечаток клиента сохраняется, содержимое не читается, кэш и журнал запросов на таком порте бесполезны.
Тело запроса
{ "enabled": true }
Пример (curl)
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.
GET/api/v1/port/{port}/tls_mirrorТребуется авторизация

Сообщает, отражаются ли параметры TLS клиента вместо профиля.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/tls_mirror
Ответ
{
  "enabled": true
}
PUT/api/v1/port/{port}/tls_mirrorТребуется авторизация

Включает или выключает отражение параметров TLS клиента.

ПараметрТипОбязательныйОписание
enabledboolДаtrue — наружу уходит форма, снятая с самого клиента, а не из профиля.
Тело запроса
{ "enabled": true }
Пример (curl)
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.
GET/api/v1/port/{port}/http3Требуется авторизация

Сообщает, разрешён ли HTTP/3 (QUIC) на порте.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/http3
Ответ
{
  "enabled": true
}
PUT/api/v1/port/{port}/http3Требуется авторизация

Разрешает или запрещает HTTP/3 (QUIC).

ПараметрТипОбязательныйОписание
enabledboolДаtrue — там, где сайт предлагает HTTP/3, порт им пользуется.
Тело запроса
{ "enabled": true }
Пример (curl)
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.
GET/api/v1/port/{port}/session_resumptionТребуется авторизация

Сообщает, разрешено ли возобновление сессий TLS.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/session_resumption
Ответ
{
  "enabled": true
}
PUT/api/v1/port/{port}/session_resumptionТребуется авторизация

Разрешает или запрещает возобновление сессий TLS (тикеты).

ПараметрТипОбязательныйОписание
enabledboolДаtrue — сессионные тикеты принимаются и переиспользуются; false — каждое соединение начинается с полного рукопожатия.
Тело запроса
{ "enabled": true }
Пример (curl)
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.
GET/api/v1/port/{port}/decompressТребуется авторизация

Сообщает, распаковывается ли сжатое тело ответа для клиента.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/decompress
Ответ
{
  "enabled": true
}
PUT/api/v1/port/{port}/decompressТребуется авторизация

Включает или выключает распаковку тела ответа.

ПараметрТипОбязательныйОписание
enabledboolДаtrue — br, gzip и zstd распаковываются в обычные байты перед отдачей клиенту.
Тело запроса
{ "enabled": true }
Пример (curl)
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.
GET/api/v1/port/{port}/cache_ignore_no_cacheТребуется авторизация

Сообщает, кэшируются ли ответы вопреки заголовку no-cache.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/cache_ignore_no_cache
Ответ
{
  "enabled": true
}
PUT/api/v1/port/{port}/cache_ignore_no_cacheТребуется авторизация

Включает или выключает кэширование вопреки no-cache.

ПараметрТипОбязательныйОписание
enabledboolДаtrue — ответ кладётся в кэш, даже если сайт просил его не кэшировать.
Тело запроса
{ "enabled": true }
Пример (curl)
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.
GET/api/v1/port/{port}/auto_profile_from_uaТребуется авторизация

Сообщает, выбирается ли профиль по User-Agent запроса, и показывает последний такой заголовок.

Пример (curl)
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) …"
}
PUT/api/v1/port/{port}/auto_profile_from_uaТребуется авторизация

Включает или выключает выбор профиля по User-Agent запроса.

ПараметрТипОбязательныйОписание
enabledboolДаtrue — приложение само называет, кем притворяться: порт подбирает профиль под присланный User-Agent.
Тело запроса
{ "enabled": true }
Пример (curl)
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.

Нагрузка, повторы и простой

Сколько запросов порт держит разом, что делает после сетевой ошибки и когда закрывается сам.

GET/api/v1/port/{port}/max_concurrentТребуется авторизация

Возвращает предел одновременных запросов на порте.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/max_concurrent
Ответ
{
  "max_concurrent": 32
}
PUT/api/v1/port/{port}/max_concurrentТребуется авторизация

Меняет предел одновременных запросов.

ПараметрТипОбязательныйОписание
max_concurrentintДа0 и больше; 0 — без предела.
Тело запроса
{ "max_concurrent": 32 }
Пример (curl)
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.
GET/api/v1/port/{port}/sem_timeoutТребуется авторизация

Возвращает, сколько секунд запрос ждёт свободного места в пределе одновременных.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/sem_timeout
Ответ
{
  "timeout_seconds": 30
}
PUT/api/v1/port/{port}/sem_timeoutТребуется авторизация

Меняет ожидание свободного места. 🔴 Поле называется timeout_seconds, а не sem_timeout.

ПараметрТипОбязательныйОписание
timeout_secondsintДа1 и больше — секунды ожидания.
Тело запроса
{ "timeout_seconds": 30 }
Пример (curl)
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.
GET/api/v1/port/{port}/skip_retryТребуется авторизация

Сообщает, повторяется ли запрос после сетевой ошибки.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/skip_retry
Ответ
{
  "skip_retry": false
}
PUT/api/v1/port/{port}/skip_retryТребуется авторизация

Включает или выключает отказ от повторов.

ПараметрТипОбязательныйОписание
skip_retryboolДаtrue — не повторять запрос после сетевой ошибки, отдать ошибку клиенту сразу.
Тело запроса
{ "skip_retry": true }
Пример (curl)
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.
GET/api/v1/port/{port}/retry_delayТребуется авторизация

Возвращает паузу между повторами в миллисекундах.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/port/20134/retry_delay
Ответ
{
  "retry_delay_ms": 500
}
PUT/api/v1/port/{port}/retry_delayТребуется авторизация

Меняет паузу между повторами. 🔴 Поле называется retry_delay_ms, а не retry_delay.

ПараметрТипОбязательныйОписание
retry_delay_msintДа0 и больше — миллисекунды.
Тело запроса
{ "retry_delay_ms": 500 }
Пример (curl)
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.
PUT/api/v1/port/{port}/idleТребуется авторизация

Задаёт тайм-аут простоя ЭТОГО порта, перекрывая общий. Ручки GET у него нет — текущее значение видно как idle_seconds в ответе GET /api/v1/port/{port}/config.

ПараметрТипОбязательныйОписание
secondsint | nullДаСекунды простоя до автозакрытия. null снимает переопределение и возвращает порт к общему тайм-ауту; 0 — не закрывать никогда.
Тело запроса
{ "seconds": 1800 }
Пример (curl)
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».

GET/api/v1/port/{port}/capturesТребуется авторизация

Возвращает отпечатки, снятые отладочным портом захвата, от старых к новым.

ПараметрТипОбязательныйОписание
sinceintНетВернуть только снимки новее указанного порядкового номера.
limitintНетСколько снимков вернуть максимум; 0 или отсутствие — все.
Пример (curl)
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 снимков; переполнение вытесняет самые старые.
DELETE/api/v1/port/{port}/capturesТребуется авторизация

Очищает кольцо снимков отладочного порта захвата.

Пример (curl)
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Жёсткий кэш, который принимает адрес только после трёх подряд одинаковых тел ответа — то есть когда содержимое доказало, что оно статическое.
ВниманиеЗаписи ОБЩИЕ для всех портов: ключ — это метод, схема, хост и путь с запросом, номера порта в нём нет. Разделения по личностям в хранилище не существует, и защита устроена иначе: в кэш не попадает ничего с Set-Cookie, с Cache-Control: no-store или private, с Vary: *, Vary: Cookie или Vary: Authorization; а у отдаваемого ответа снимаются ETag и Last-Modified, чтобы валидатор, выданный одной личности, не вернулся эхом от другой и не связал их между собой.
GET/api/v1/port/{port}/cacheТребуется авторизация

Состояние кэша порта: включён ли, в каком режиме и сколько уже сэкономил.

Пример (curl)
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 — сколько байтов не пришлось скачать заново.
PUT/api/v1/port/{port}/cacheТребуется авторизация

Включает, выключает или переключает режим кэша на живом порте.

ПараметрТипОбязательныйОписание
modestringНетnormal, hard, hard-media, hard-autowarm или пусто.
enabledboolНетЧитается ТОЛЬКО когда режим не жёсткий. false выключает кэш; при жёстком режиме поле игнорируется.
Тело запроса
{ "mode": "hard-media" }
Пример (curl)
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: обработчик отвечает им же.
DELETE/api/v1/port/{port}/cacheТребуется авторизация

Очищает кэш этого порта, не трогая остальные.

Пример (curl)
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.
GET/api/v1/cache/hardТребуется авторизация

Состояние жёсткого кэша целиком: сколько записей, сколько места и сколько сэкономлено.

Пример (curl)
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
}
DELETE/api/v1/cache/hardТребуется авторизация

Очищает жёсткий кэш целиком — у всех портов сразу.

Пример (curl)
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/cache/hard
Ответ
{
  "status": "hard cache cleared"
}
POST/api/v1/cache/hard/evictТребуется авторизация

Выбрасывает из жёсткого кэша записи по списку образцов — точечно, вместо полной очистки.

ПараметрТипОбязательныйОписание
patternsarrayДаДомены или полные адреса. Список обязателен.
Тело запроса
{ "patterns": ["example.com", "https://cdn.example.net/img/logo.png"] }
Пример (curl)
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 у этой ручки нет.
POST/api/v1/cache/evictТребуется авторизация

То же для обычного кэша: выбрасывает записи по списку образцов.

ПараметрТипОбязательныйОписание
patternsarrayДаДомены или полные адреса. Список обязателен.
Тело запроса
{ "patterns": ["example.com"] }
Пример (curl)
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» на пустом списке.
POST/api/v1/cache/hard/saveТребуется авторизация

Оставлена ради прежних клиентов: жёсткий кэш лежит в SQLite и пишется на диск при каждой записи, так что сбрасывать его отдельно не нужно. Ручка просто отдаёт состояние.

Пример (curl)
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"
}
GET/api/v1/cache/hard/exclusionsТребуется авторизация

Домены и адреса, которые жёсткий кэш обходит стороной.

Пример (curl)
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"]
}
PUT/api/v1/cache/hard/exclusionsТребуется авторизация

ДОПИСЫВАЕТ присланные образцы к списку исключений жёсткого кэша, а не заменяет его.

ПараметрТипОбязательныйОписание
exclusionsarrayДаДомены или маски адресов, которые кэшировать не надо.
Тело запроса
{ "exclusions": ["api.example.com"] }
Пример (curl)
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.
  • Ответ — полный список после слияния.
DELETE/api/v1/cache/hard/exclusionsТребуется авторизация

Удаляет ПЕРЕЧИСЛЕННЫЕ образцы из списка исключений жёсткого кэша. Тело обязательно.

ПараметрТипОбязательныйОписание
exclusionsarrayДаЧто удалить из списка.
Тело запроса
{ "exclusions": ["api.example.com"] }
Пример (curl)
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}, а не с пустым массивом — ровно как у обычного кэша.
GET/api/v1/cache/exclusionsТребуется авторизация

То же для обычного кэша: список исключений.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/cache/exclusions
Ответ
{
  "exclusions": ["login.example.com"]
}
PUT/api/v1/cache/exclusionsТребуется авторизация

ДОПИСЫВАЕТ образцы к списку исключений обычного кэша.

ПараметрТипОбязательныйОписание
exclusionsarrayДаДомены или маски адресов, которые кэшировать не надо.
Тело запроса
{ "exclusions": ["login.example.com"] }
Пример (curl)
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.
DELETE/api/v1/cache/exclusionsТребуется авторизация

Удаляет перечисленные образцы из списка исключений обычного кэша. Тело обязательно.

ПараметрТипОбязательныйОписание
exclusionsarrayДаЧто удалить из списка.
Тело запроса
{ "exclusions": ["login.example.com"] }
Пример (curl)
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 на каждый запрос, прошедший через порт: время, метод, хост, путь, код ответа, тип и размер тела, заголовки кэширования и вердикт кэша. Им разбирают, почему кэш не срабатывает и куда уходит трафик.

ВниманиеЖурнал — это история посещений на диске. Он пишется в data/traffic_port_<порт>.jsonl рядом с приложением, переживает перезапуск (файл ДОПИСЫВАЕТСЯ, а не начинается заново) и растёт до предела, после которого старые записи вытесняются. Выключение журнала закрывает файл, но НЕ удаляет его.
GET/api/v1/port/{port}/traffic_logТребуется авторизация

Сообщает, включён ли журнал, где лежит файл и сколько в нём записей.

Пример (curl)
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.
PUT/api/v1/port/{port}/traffic_logТребуется авторизация

Включает или выключает журнал на живом порте. При включении файл создаётся, если его ещё нет.

ПараметрТипОбязательныйОписание
enabledboolДаtrue — начать писать; false — закрыть файл и перестать.
Тело запроса
{ "enabled": true }
Пример (curl)
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.
GET/api/v1/port/{port}/traffic_log/downloadТребуется авторизация

Отдаёт файл журнала целиком — по строке JSON на запрос (формат NDJSON).

Пример (curl)
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», если журнал выключен.
DELETE/api/v1/port/{port}/traffic_logТребуется авторизация

Опустошает файл журнала, оставляя запись включённой.

Пример (curl)
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», если журнал выключен: очистить файл выключенного журнала этой ручкой нельзя — сначала включите его.

Перехват системного трафика и аудит утечек

Перехват уводит на порт трафик программы или всей машины без настройки прокси в самом приложении. Аудит утечек отвечает на обратный вопрос: не ходит ли подопытная программа МИМО перехвата.

GET/api/v1/system/interceptТребуется авторизация

Доступна ли привилегированная служба перехвата прямо сейчас. Спрашивать её нужно ДО открытия порта: иначе отказ придёт уже после заполнения формы.

Пример (curl)
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 — перехватом уже управляет другая копия приложения, служба обслуживает одного клиента за раз.
GET/api/v1/system/processesТребуется авторизация

Список приложений, из которого выбирают цели попроцессного перехвата.

Пример (curl)
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 на платформе, которая перечислять процессы не умеет: пустой массив был бы неотличим от «процессов нет», и человек увидел бы пустую таблицу выбора вместо объяснения.
POST/api/v1/system/leak-auditТребуется авторизация

Начинает сеанс наблюдения: смотрит, ходит ли выбранная программа мимо перехвата.

ПараметрТипОбязательныйОписание
exe_pathsarrayДаПути к исполняемым файлам, за которыми наблюдать. 🔴 Поле называется exe_paths, а не paths.
policystringДаobserve — только наблюдать; block — ещё и обрывать то, что пошло мимо.
Тело запроса
{ "exe_paths": ["C:\\Program Files\\App\\app.exe"], "policy": "observe" }
Пример (curl)
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, если менеджер портов не поднят.
GET/api/v1/system/leak-auditТребуется авторизация

Состояние живого сеанса: вердикт, агрегаты, границы честности и свежий хвост событий.

Пример (curl)
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 «сеанс аудита утечек не запущен», пока сеанс не начат: пустой отчёт был бы не честнее молчания.
GET/api/v1/system/leak-audit/reportТребуется авторизация

Отчёт без ленты событий — им выгружают результат. Если живого сеанса нет, отдаёт отчёт последнего завершённого.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/system/leak-audit/report
  • 404 «аудит утечек ни разу не запускался», если завершённого отчёта тоже нет.
DELETE/api/v1/system/leak-auditТребуется авторизация

Останавливает сеанс и отдаёт итоговый отчёт в той же форме, что GET.

Пример (curl)
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/system/leak-audit
  • 404, если сеанса нет. У остановленного сеанса поле stopped_by говорит, кто его закончил — человек или сторож, который сам обрывает затянувшееся наблюдение.

Проверки порта

Две проверки на живом порте и одна вспомогательная. Полная проверка гоняет запрос НАСКВОЗЬ через порт и сверяет, каким его увидели снаружи, с тем, кем он собирался притворяться.

POST/api/v1/port/{port}/testТребуется авторизация

Полная проверка настройки порта: утечки на выходе, сквозной отпечаток и поддержка UDP.

Пример (curl)
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 секундами.
POST/api/v1/port/{port}/leakcheckТребуется авторизация

Только проверка утечек на выходе: сравнивает, чей резолвер отвечает и чей IPv6 виден.

Пример (curl)
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 секундами.
POST/api/v1/port/{port}/generateТребуется авторизация

Собирает профиль для запрошенного браузера и версии и ставит его на порт.

ПараметрТипОбязательныйОписание
browserstringДаchrome, firefox, safari или edge.
versionintНетВерсия браузера; 0 — версия по умолчанию.
osstringНетwindows, macos, linux, ios или android; пусто — windows.
saveboolНетСохранить профиль в базе для повторного использования.
Тело запроса
{ "browser": "chrome", "version": 152, "os": "windows", "save": true }
Пример (curl)
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 с текстом движка, если профиль собрать не удалось.