Справочник по API

BlankTrail Proxy предоставляет локальный HTTP API, с помощью которого вы можете открывать порты, настраивать идентичности и автоматизировать всё, что делает дашборд. На этой странице описаны базовый URL, аутентификация и соглашения.

Базовый URL

Все эндпоинты находятся под префиксом /api/v1 на том же хосте и порту, что и дашборд:

http://127.0.0.1:8891/api/v1

API и дашборд используют один и тот же источник (origin), поэтому вызовы из ваших собственных скриптов и инструментов работают сразу и без дополнительной настройки.

Аутентификация

API работает по принципу «запрещено по умолчанию»: почти каждый эндпоинт требует аутентификации. Есть два способа аутентификации:

  • API-ключ — передавайте его в заголовке X-API-Key. Лучший вариант для скриптов и автоматизации.
  • Сессионная cookie — получается при входе в систему; используется веб-дашбордом.

Найти и заменить свой API-ключ можно в диалоге «Настройки» дашборда. Передавайте его в каждом запросе:

curl -H "X-API-Key: YOUR_API_KEY" http://127.0.0.1:8891/api/v1/status
ВажноAPI-ключ даёт полный контроль над вашими портами. Храните его в секрете и ограничьте сетевой доступ к порту дашборда (привяжите его к localhost или используйте SSH-туннель на серверах).

Соглашения

  • Запросы и ответы — в формате JSON. В запросах с телом отправляйте Content-Type: application/json.
  • Конфигурация всегда передаётся в теле JSON, а не в строке запроса.
  • API версионируется под префиксом /api/v1. Несовместимые изменения будут вынесены в новый префикс версии.

Коды состояния

Тело ошибки всегда одно и то же: объект с полем error и текстом на английском; у части отказов рядом стоит поле code — машиночитаемая причина. Тексты приведены в описаниях ручек: по ним отличают одну причину 400 от другой. Исключение — 405: его отдаёт маршрутизатор обычной строкой, без JSON.

КодЗначение
200 OKУспех.
202 AcceptedПринято и выполняется в фоне — так отвечает запуск обновления.
400 Bad RequestПлохой ввод: тело не разбирается, поля нет, значение недопустимо.
401 UnauthorizedКлюч API не прислан или неверен. Проверьте имя заголовка: X-API-Key.
403 ForbiddenДействие запрещено не ключом, а состоянием: лицензия неактивна (подписка кончилась или сутки не было связи с сервером авторизации) либо возможность требует тарифа Pro.
404 Not FoundТакого нет: путь не зарегистрирован, порт не открыт, узел не найден.
405 Method Not AllowedПуть есть, а метод не тот — например PUT на ручку только для чтения. Этот код отдаёт сам маршрутизатор, а не обработчик.
409 ConflictСостояние мешает: порт уже занят, имя уже есть, шлюз используется.
429 Too Many RequestsСлишком часто — например, повторная отправка отчёта о проблеме. Снизьте частоту и повторите.
500 Internal Server ErrorНепредвиденная ошибка внутри приложения.
501 Not ImplementedУзел не собран или не проведён в этой сборке — так отвечает вход в дашборд, когда авторизация не настроена.
502 Bad GatewayНе удалось сходить наружу: Кабинет недоступен, заход по адресу не состоялся.
503 Service UnavailableНечем ответить прямо сейчас: свободного порта нет, служба не поднята, отладочные крючки не проведены.
Примечание403 и 401 путать не надо: 401 — про ключ, 403 — про право. Клиент, работавший месяцами, получает 403 на POST /api/v1/system/leak-audit, POST /api/v1/upstream/test, POST /api/v1/scraper/probe, POST /api/v1/ovpn/ping и POST /api/v1/gateway/subs/{name}/refresh после суток без связи с сервером авторизации — ключ при этом верен.

Справочник по темам

  • Порты и трафик — открытие, закрытие, получение списка и настройка портов; тестирование вышестоящего прокси.
  • Профили и маршрутизация — идентичности, пресеты, правила доменов и шлюзы.
  • Пул портов — открытие пулов портов из списка прокси и управление ими.
  • Лицензия и доступ — статус лицензии, аутентификация и CA-сертификат.

Служебные ручки

Эти ручки нужны не в сценарии, а рядом с ним: убедиться, что приложение живо, поднять подробность журнала на время разбирательства, узнать про обновление, задать общее правило маршрутизации.

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

Текущая подробность журнала.

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

Поднимает подробность на время разбирательства: debug пишет заметно больше и держится до перезапуска.

ПараметрТипОбязательныйОписание
levelstringДаdebug, info, warn или error.
Тело запроса
{ "level": "debug" }
Пример (curl)
curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"level":"debug"}' \
  http://127.0.0.1:8891/api/v1/log_level
Ответ
{
  "level": "debug"
}
  • 400 «level must be one of: debug, info, warn, error» на любом другом значении.
GET/api/v1/update/statusТребуется авторизация

Какая версия стоит и есть ли новая: available пуст, пока обновления нет; state показывает, чем занят обновлятор сейчас.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/update/status
Ответ
{
  "current": "1.3.2",
  "available": "",
  "state": "idle",
  "last_error": "",
  "download_url": ""
}
  • 404 «self-update not available» в сборке, где самообновление не собрано.
POST/api/v1/updateТребуется авторизация

Ставит обновление, о котором сообщил /update/status. Приложение перезапускается само.

Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/update
Ответ
{
  "status": "started"
}
  • 🔴 Ответ — 202 Accepted, а не 200: обновление принято и идёт в фоне, а не закончилось к моменту ответа. Дожидаться его нужно опросом /update/status.
  • 409 «no update available», если ставить нечего; 404 «self-update not available» в сборке без самообновления.
GET/api/v1/domain_routingТребуется авторизация

Простое правило маршрутизации: список доменов и общий для них выходной прокси. Пустой список — правило выключено. Подробные правила с именами живут в /domain_rules.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/domain_routing
Ответ
{
  "domains": ["example.com", "*.example.net"],
  "proxy": "socks5://203.0.113.10:1080"
}
PUT/api/v1/domain_routingТребуется авторизация

Задаёт простое правило целиком: домены и прокси. Пустой список доменов его выключает.

ПараметрТипОбязательныйОписание
domainsarrayДаДомены и маски вида *.example.net.
proxystringДаВыходной прокси для этих доменов.
Тело запроса
{ "domains": ["example.com"], "proxy": "socks5://203.0.113.10:1080" }
Пример (curl)
curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"domains":["example.com"],"proxy":"socks5://203.0.113.10:1080"}' \
  http://127.0.0.1:8891/api/v1/domain_routing
Ответ
{
  "domains": ["example.com"],
  "proxy": "socks5://203.0.113.10:1080"
}
  • 400 с объяснением, если домен или адрес прокси не той формы.
PUT/api/v1/idle_timeoutТребуется авторизация

Общий тайм-аут простоя для портов, у которых нет своего. 0 — не закрывать.

ПараметрТипОбязательныйОписание
secondsintДаСекунды простоя до автозакрытия порта; 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/idle_timeout
Ответ
{
  "idle_timeout_seconds": 1800
}
  • 400 «seconds must be >= 0» на отрицательном значении. Порт со своим переопределением (PUT /api/v1/port/{port}/idle) это значение не берёт.

Отчёт об ошибке

Отчёт собирает журналы и состояние портов и отправляет их в поддержку. Перед отправкой его можно посмотреть целиком — это тот же файл, который уйдёт.

GET/api/v1/bug-report/statusТребуется авторизация

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

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/bug-report/status
Ответ
{
  "enabled": true,
  "unclean_previous_exit": false
}
  • enabled=false означает, что отправка недоступна без активной лицензии.
POST/api/v1/bug-report/previewТребуется авторизация

Собирает отчёт и показывает его, ничего не отправляя.

Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/bug-report/preview
Ответ
{
  "report": { "version": 1, "ports": [ … ], "log": [ … ] },
  "notes_included": false
}
  • 404 «bug report not available», если сбор отчётов не поднят; 500 «failed to build report».
POST/api/v1/bug-report/sendТребуется авторизация

Отправляет отчёт в поддержку вместе с вашим описанием.

ПараметрТипОбязательныйОписание
descriptionstringДаЧто случилось. Поле обязательно: пустое после обрезки пробелов отвергается.
Тело запроса
{ "description": "После смены выхода порт перестал отдавать 200" }
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"description":"port stopped returning 200 after an egress change"}' \
  http://127.0.0.1:8891/api/v1/bug-report/send
Ответ
{
  "status": "sent",
  "bug_report_id": "br-7f3a1c9d"
}
  • 400 «description required» без описания; 403 «activate your license to send a bug report»; 404 «bug report not available»; 429 при частой отправке; 502, если сервер поддержки недоступен.
  • Тело ограничено 64 КиБ.
POST/api/v1/bug-report/dismissТребуется авторизация

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

Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/bug-report/dismiss
Ответ
{
  "status": "dismissed"
}
  • Отметка живёт в памяти: настоящее новое аварийное завершение поставит её снова на следующем запуске.

Диагностика

Эти пути нужны, когда приложение ведёт себя не так и к обращению в поддержку надо приложить цифры, а не описание. Продуктовой поверхностью они не являются: их формат задан средой выполнения и может измениться между версиями.

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

Снимок самого процесса: горутины, системные потоки, дескрипторы, память и время работы. Это первое, что стоит приложить к обращению, если приложение стало тяжёлым.

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/debug
Ответ
{
  "goroutines": 142,
  "threads": 21,
  "handles": 380,
  "open_ports": 2,
  "uptime": "3h14m22s",
  "memory": {
    "alloc_mb": 96,
    "total_alloc_mb": 8140,
    "sys_mb": 310,
    "heap_objects": 410000,
    "heap_inuse_mb": 120,
    "heap_released_mb": 64,
    "gc_cycles": 1830,
    "stack_inuse_mb": 6
  }
}
  • Поле handles приходит всегда, но осмысленно только на Windows: там его считает сама система, а на прочих платформах оно всегда 0.

Профилирование (pprof)

Одиннадцать стандартных путей Go, открытых по обычному ключу API. Отдают они внутреннее устройство процесса, а не данные пользователя, но за ключом стоят так же, как остальной API.

ПутьЧто отдаёт
/debug/pprof/Оглавление: список доступных профилей.
/debug/pprof/heapСнимок кучи — с чего начинать, если растёт память.
/debug/pprof/goroutineВсе горутины со стеками — с чего начинать, если что-то зависло.
/debug/pprof/allocsВсе выделения памяти за время работы.
/debug/pprof/profileПрофиль процессора. 🔴 Держит соединение 30 секунд — это не зависание.
/debug/pprof/traceТрассировка выполнения.
/debug/pprof/blockГде горутины ждали на блокировках.
/debug/pprof/mutexСостязание за мьютексы.
/debug/pprof/threadcreateСоздание системных потоков.
/debug/pprof/cmdlineКомандная строка процесса.
/debug/pprof/symbolРазрешение адресов в имена функций.

Снять кучу и горутины для отчёта: curl -H "X-API-Key: YOUR_API_KEY" -o heap.pprof http://127.0.0.1:8891/debug/pprof/heap и то же с goroutine. Открывать их удобно командой go tool pprof heap.pprof.

Отладочный контур примет солвера

ВниманиеЧетыре ручки ниже существуют, только если приложение запущено с переменной среды BLANKTRAIL_SOLVER_DEBUG_API=1. Сравнение строгое: значение 0 или true их не включает. Переменная читается ОДИН раз при старте — выставленная позже, она на работающий процесс не действует. Без неё маршруты не зарегистрированы вовсе, и запрос получает 404: отсутствующая ручка не должна сообщать о своём существовании.

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

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

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

Пример (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/debug/signatures
Ответ
{
  "debug_active": true,
  "debug_vendors": 3
}
  • 503 «solver debug hooks not wired», если рубильник выставлен, но крючки солвера в этой сборке не проведены. Это НЕ 404: сам маршрут существует, и различить два состояния важно.
POST/api/v1/debug/signaturesТребуется авторизация

Ставит присланный набор примет ОТДЕЛЬНЫМ слоем поверх паковой базы — без пересборки пака, подписи и выкладки.

Тело запроса
{ "Version": 7, "Vendors": [ { "…": "…" } ], "NavHints": [ ] }
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  --data-binary @signatures.json \
  http://127.0.0.1:8891/api/v1/debug/signatures
Ответ
{
  "installed": 3,
  "dropped": 1
}
  • Тело — база примет в том же формате, что и паковая. installed — сколько вендоров принято, dropped — сколько ПРИСЛАННЫХ условий забраковано.
  • Слой переживает любое число обновлений пака и снимается только явным DELETE. Установка пишется в журнал предупреждением — забытый слой не должен остаться незаметным.
  • 503 «solver debug hooks not wired»; 400 «invalid JSON body».
DELETE/api/v1/debug/signaturesТребуется авторизация

Снимает отладочный слой примет и возвращает солвер к паковой базе.

Пример (curl)
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/debug/signatures
Ответ
{
  "cleared": true
}
  • 503 «solver debug hooks not wired», если крючки не проведены.
POST/api/v1/port/{port}/solve_probeТребуется авторизация

Приводит солвер на указанный адрес ЧЕРЕЗ конкретный порт и возвращает, что он там увидел — независимо от того, совпала примета или нет.

ПараметрТипОбязательныйОписание
urlstringДаАдрес, на который вести солвер.
solveboolНетfalse (по умолчанию) — только распознать вендора и характер страницы, около секунды. true — ещё и принудительно решить.
Тело запроса
{ "url": "https://example.com/", "solve": true }
Пример (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -H "Accept-Language: ru-RU,ru;q=0.9" \
  -d '{"url":"https://example.com/","solve":true}' \
  http://127.0.0.1:8891/api/v1/port/20134/solve_probe
  • 🔴 При solve=true искусственного тайм-аута здесь НЕТ намеренно: принудительное решение честно занимает десятки секунд, а у некоторых проверок — до полутора минут. Запрос не завис.
  • Заголовок Accept-Language ЭТОГО запроса прокидывается в задачу дословно: язык запуска влияет на исход, и без него солв уехал бы с языком выходного узла вместо запрошенного.
  • 503 «solver debug hooks not wired»; 400 «url is required»; 502 с текстом ошибки, если сам заход не удался.