Решения
Разработчикам
- 50325
- Порт локального интерфейса
- 11
- Ходов у локального интерфейса
- 9
- Инструментов у сервера MCP
- 7
- Прав у ключа API
- 13
- Событий для подписки
Код ведёт настоящий профиль, а не чистый браузер
Сценарий, открывающий обычный Chromium, начинает с той машины, на которой он идёт. Экран, шрифты, строка видеокарты и часовой пояс принадлежат этому компьютеру, и каждый аккаунт, открытый там, повторяет их. Отдельная папка данных разводит куки, но не даёт десятому аккаунту другое устройство.
Профиль ARMANOS несёт устройство с собой. У каждого свои куки, своё хранилище, свои расширения и свой прокси, а отпечаток берётся из четвёртого поколения генератора и строится на одном из пяти образцов: macos-chrome, windows-chrome, linux-chrome, android-chrome и android-tablet. На движке ARMANOS Browser версии 153.0.7978.0 значения подставляются внутри движка, а не поверх страницы сценарием, который страница умеет найти.
Подключение обычное, как к любому Chromium: по протоколу отладки, на петле. Открытый порт отладки обычно ставит navigator.webdriver в true, и это первая строка почти любой проверки на робота. Движок запускается с ключом --disable-blink-features=AutomationControlled, значение остаётся false, а проверка поднимает настоящий движок и читает его на настоящей странице.
| Что нужно | Окно инкогнито | Расширение подмены | Виртуальная машина | Профиль ARMANOS |
|---|---|---|---|---|
| Куки и хранилище врозь у каждого аккаунта | – | – | Да | Да |
| Отпечаток меняется ниже страничного JavaScript | – | – | Да | Да |
| Свой прокси, а не общий на машину | – | на весь браузер | Да | Да |
| Десять аккаунтов разом на ноутбуке | – | – | сколько хватит памяти | Да |
| Код подключается ровно к одному из них | – | – | Да | Да |
| Открывается примерно за секунду | Да | Да | – | Да |
Локальный интерфейс живёт на вашей машине и отвечает только программам
Включите локальный интерфейс в программе: настройки, раздел API и MCP. Сервер слушает 127.0.0.1 и больше ничего. Порт по умолчанию 50325, а если он уже занят, программа берёт свободный порт у системы и показывает тот, что достался.
До вашего включения он выключен. При первом запуске программа пишет local-api.json в свою папку данных: пропуск из 24 случайных байтов и выключатель в положении «нет». Файл доступен на чтение только вам. Пропуск отправляется как параметр token, как заголовок x-api-token или как Authorization: Bearer. Смена пропуска действует сразу, перезапуск не нужен.
Петля сама по себе не граница. Страницы, которые открывает человек, идут на этой же машине, а имя, переуказанное на 127.0.0.1, приходит к местному серверу как своя же область. Поэтому запрос с заголовком Origin, с Sec-Fetch-Site не равным none или с чужим Host получает 403 и ничего больше. Эта проверка стоит перед /status, потому что /status называет продукт и его точную версию, а знать это странице незачем.
Ответы приходят в одной обёртке: код 0 и объект data при удаче, отрицательный код и сообщение при отказе. Тело запроса больше мегабайта отбрасывается. Нет пропуска или он неверный, это 401, неизвестный ход, это 404, сбой обработчика, это 500 с причиной.
| Ход | Метод | Что делает |
|---|---|---|
| /status | GET | Единственный ход без пропуска. Отвечает готовностью и версией программы. |
| /api/v1/profile/list | GET | Все профили: задача, группа, образец отпечатка, краткое описание прокси без паролей и признак «открыт сейчас». Тот же ответ отдаётся на /api/v1/browser/list. |
| /api/v1/browser/start | POST | Открывает профиль той же дорогой, что и кнопка в окне. Возвращает точку отладки. Принимает headless на один прогон. |
| /api/v1/browser/stop | POST | Закрывает все окна профиля и говорит, сколько закрыл. |
| /api/v1/browser/active | GET | Номера профилей, у которых прямо сейчас открыто окно. |
| /api/v1/profile/create | POST | Заводит профиль через ту же проверку доступа и нормы, что и программа. Возвращает новый profileId и сам профиль. |
| /api/v1/profile/delete | POST | Переносит профиль в корзину, откуда программа умеет его вернуть. |
| /api/v1/profile/code | GET | Текущий шестизначный код для профиля с сохранённым ключом двухфакторного входа и сколько секунд он ещё годен. |
| /api/v1/proxy/list | GET | Сохранённая библиотека прокси с итогом последней проверки. Паролей в ответе нет никогда. |
| /api/v1/flow/list | GET | Сценарии на этой машине и число шагов у каждого. |
| /api/v1/flow/run | POST | Запускает сценарий по названным профилям, с выбранной вами одновременностью. |
Каждый открытый профиль отдаёт свою собственную точку отладки
POST /api/v1/browser/start идёт той же дорогой, что и кнопка в окне: та же проверка доступа, та же норма профилей. Можно попросить провести именно этот прогон без окна, в теле или в строке запроса, и решение самого профиля при этом не меняется. Строки 0, false и пустая означают «нет», потому что строка запроса приносит текст, а непустой текст это ещё не согласие.
На движке ARMANOS Browser ответ несёт точку отладки, принадлежащую только этому профилю. Движок стартует с --remote-debugging-port=0 на 127.0.0.1, Chromium берёт свободный порт и пишет его вместе с путём сокета в файл DevToolsActivePort внутри папки профиля. Программа читает этот файл, повторяя тридцать раз через 150 миллисекунд, и отдаёт debugPort и адрес вида ws://127.0.0.1. Точка живёт ровно столько, сколько открыт профиль.
На встроенном движке Electron порт отладки один на всю программу и общий для всех профилей: ответ возвращает ws пустым и честно пишет об этом в примечании. Этот порт открывается только тогда, когда работает именно встроенный движок. Незащищённый порт, которым никто не пользуется, это открытая дверь на вашей же машине, поэтому по умолчанию его нет.
- 1
Включите локальный интерфейс
Настройки, раздел API и MCP. Пропуск копируется с того же экрана.
- 2
Попросите профиль
POST /api/v1/browser/start с profileId и пропуском в заголовке x-api-token.
- 3
Возьмите data.ws из ответа
Это порт и путь сокета именно этого профиля и никакого другого.
- 4
Подключитесь и работайте
Отпечаток, куки, расширения и прокси уже стоят на месте до первого перехода.

Puppeteer и Playwright подключаются за два вызова, и кое-что при этом меняется
Два вызова, и вы внутри. Попросите локальный интерфейс открыть профиль, возьмите data.ws из ответа и передайте его в puppeteer.connect как browserWSEndpoint или в Playwright как connect_over_cdp. Примеры на странице автоматизации написаны по-английски и запускаются как есть.
Теперь то, о чём лучше узнать от нас. Оба клиента при подключении сами включают домены Runtime и Page. Эти домены ставят в Chromium наблюдателей, которых страничный сценарий умеет заметить, а проверки на них давно входят в обычный набор защиты от роботов. Это свойство клиента, а не нашего движка, и выключить его с нашей стороны нельзя.
Наш собственный исполнитель не включает ни одного домена. Runtime.evaluate, Page.navigate, Input и Network работают и без включения, а готовность страницы опрашивается через document.readyState, вместо подписки на события жизненного цикла. Поэтому сценарий, который ведёт программа, оставляет тот же след, что и человек с мышью.
В сборщике сценариев шестнадцать видов шага: переход, ожидание, щелчок, ввод, прокрутка, чтение, снимок, клавиша, случайная пауза, повтор, ветвление, обход списка, вкладка, кука, свой JavaScript и ожидание запроса. Повтор, ветвление и обход списка держат вложенные шаги: до четырёх уровней вложенности, до пятисот шагов в списке и до двухсот сценариев на машине. Переход разрешён только на http, https и about:blank, и это проверяется и при сохранении, и заново в момент запуска, потому что адрес шага может собираться из переменной.
Сценарии запускаются и снаружи. GET /api/v1/flow/list отдаёт их вместе с числом шагов, а POST /api/v1/flow/run принимает flowId, номера профилей и одновременность. Незнакомые номера возвращаются списком, чтобы было видно, что править, а прогон, взявший ноль профилей, считается отказом, а не удачей. Последнее важно: раньше ответ «запущено 0» при исправном прогоне заставлял чужую программу повторять вызов и открывать те же профили дважды.
Внешний клиент: Puppeteer, Playwright, Selenium
Ваш язык, ваши библиотеки, ваша обработка ошибок, подключение к одному профилю по его собственной точке. При подключении включает домены Runtime и Page.
Сборщик сценариев внутри программы
Шестнадцать видов шага, без кода и без заметных доменов. Работает на этой машине, запускается руками, по расписанию или ходом /api/v1/flow/run из вашей программы.
Два набора разработчика оборачивают сервер и намеренно ничего сверх того
В хранилище лежат два небольших набора: packages/sdk-js для Node и packages/sdk-python для Python. Питоновский берёт только стандартную библиотеку, потому что пять запросов не должны тянуть в ваш проект дерево зависимостей. Набор для Node пользуется встроенным fetch, то есть нужен Node 18 и новее, и несёт написанные руками описания типов.
Оба оборачивают ходы сервера, а не программу на вашей машине. Адрес по умолчанию https://api.armanos.io и берётся из ARMANOS_API_URL, ключ из ARMANOS_API_KEY. Своей логики в них нет нарочно: набор, который умеет больше сервера, расходится с ним через месяц и начинает врать.
Ключ проверяется до первого запроса. Ключ с лишним пробелом или посторонним знаком из буфера обмена ломает сам заголовок, и отказ читается как «сервер недоступен», хотя сервер жив. Вместо этого вы получаете понятное сообщение. У каждой ошибки есть код ответа и свой признак, который отличает отсутствие ключа, испорченный ключ, потерю связи, отвергнутый ключ и нехватку права.
Запуск профиля отправляет имя машины, по умолчанию sdk и имя хоста. Замок, не дающий открыть профиль дважды, и учёт устройств тарифа одинаково требуют, чтобы машины различались. Одно общее слово на всех тихо ломало и то, и другое.
В обоих наборах есть сверка подписи вызова, и это та половина, которая нужна принимающей стороне. Она считает HMAC-SHA256 по времени, точке и точному телу, сравнивает постоянным временем по байтам и отвергает всё старше 300 секунд. Без проверки времени однажды перехваченный вызов остался бы правильно подписанным навсегда.
- profiles.list() / profiles_list()
- GET /profiles
- profiles.create() / profile_create()
- POST /profiles
- profiles.start() / profile_start()
- POST /profiles/{id}/start
- proxies.update() / proxy_update()
- PATCH /proxies/{id}
- logs.suspicion() / logs_suspicion()
- GET /logs/suspicion
- verifyWebhook() / verify_webhook()
- HMAC-SHA256 по «время.тело»
Каждый путь, который зовут наборы, сверяется с порождённым описанием, поэтому переименованный ход ломает сборку, а не ваш сценарий.
Сервер MCP позволяет ИИ-клиенту открывать и закрывать ваши профили
Сервер MCP это один файл на Node без зависимостей: apps/desktop/src/mcp/armanos-mcp-server.js. Его запускает ваш клиент, а не программа. Он говорит по JSON-RPC 2.0 через стандартный ввод и вывод, читает и построчные кадры, и кадры с Content-Length, а пишет всегда построчно.
Своих секретов у него нет. Две переменные среды указывают на локальный интерфейс: ARMANOS_API_URL со значением по умолчанию http://127.0.0.1:50325 и ARMANOS_API_TOKEN. Программа собирает готовый блок настройки целиком, уже с нужным путём и нужным пропуском, и ставит рядом кнопку копирования.
Инструментов девять, и каждый ведёт ровно к одному ходу, который локальный интерфейс действительно умеет. Инструмент для несуществующего хода был бы хуже отсутствия инструмента: помощник продолжал бы его звать.
Три подробности выросли из настоящих поломок. launch_profile ждёт до девяноста секунд, потому что движок открывает порт отладки в своём темпе, а на пятнадцати секундах помощник получал «нет ответа», пока окно ещё поднималось. Ответ больше 5 МБ заканчивается понятным отказом, а не обещанием, которое не исполнится никогда. Внутри Linux AppImage файл сначала копируется рядом с вашими данными, потому что путь внутри образа меняется при каждом запуске и вставленная настройка молча ломалась бы после перезагрузки программы.
get_status
Проверяет, что локальный интерфейс отвечает, и называет версию программы. Единственный инструмент без пропуска.
list_profiles
Все профили: задача, группа, образец, краткое описание прокси и признак «открыт».
get_active_profiles
Номера профилей, у которых окно открыто прямо сейчас.
launch_profile
Открывает профиль через ту же проверку доступа и нормы, что и программа, и отдаёт порт отладки, когда он есть.
stop_profile
Закрывает все окна профиля и сообщает, сколько закрыл.
create_profile
Заводит профиль, при желании с группой, образцом, метками, заметкой и прокси, включая выход через свой сервер по SSH.
delete_profile
Переносит профиль в корзину, откуда программа умеет его вернуть.
get_one_time_code
Текущий код второго фактора для профиля с сохранённым ключом и остаток его срока в секундах.
list_proxies
Сохранённая библиотека прокси и итог последней проверки, без паролей.
Подписки отдают события наружу, ключи API впускают программы внутрь
Ключ API это дверь в аккаунт без пароля, поэтому и устроен он как дверь. Ключ создаётся в кабинете, права выбираются поштучно, а сам ключ показывается ровно один раз. На сервере лежит только отпечаток SHA-256 и десять первых знаков, показать второй раз физически нечего. Двадцать действующих ключей на аккаунт это предел, а отозванный ключ сохраняет свою строку, иначе в журнале остались бы действия ключа, которого никто не назовёт.
Права проверяются у каждого хода, и по умолчанию закрыто. Ход, у которого право не объявлено, ключу недоступен вовсе, и в описании так и написано: туда проходит только человек с паролем. Сегодня ключам открыто пятнадцать ходов: профили, прокси, сценарии и журналы. Три отказа различаются нарочно: 401 для негодного или отозванного ключа, 402 для настоящего ключа с закончившимся тарифом, 403 для настоящего ключа без нужного права. Сценарий, который их не различает, чинит не то.
Ключи входят в платные тарифы: Professional, Business и Enterprise. На бесплатном сервер отказывает в выдаче. Тариф читается по действующему доступу при каждом запросе, а не по тому, что было записано при выдаче: иначе месяц оплаты давал бы вечный ключ. Ответ держится минуту, потому что программа делает десятки запросов в секунду и два лишних чтения базы на каждый это заметная плата. Блокировка аккаунта закрывает и его ключи в тот же момент.
Подписки идут в обратную сторону, и управляет ими только человек, вошедший паролем. Ключ, которым можно завести подписку, стал бы утечкой, переживающей собственный отзыв. Тринадцать событий, до десяти подписок на аккаунт, а секрет лежит в базе запечатанным, а не хешированным: им подписывается каждое тело.
В каждой отправке идут заголовки X-Armanos-Event, X-Armanos-Timestamp и X-Armanos-Signature, где подпись это sha256= и HMAC-SHA256 по времени, точке и ровно тому телу, которое вы получили. У каждой отправки свой номер, потому что доставка идёт «хотя бы один раз» и повтор надо уметь отбросить. Поля, чьи имена похожи на код, пропуск, секрет, пароль, ссылку или хеш, наружу не уходят: код приглашения это вход, а дорогу до вашего сервера мы не выбираем.
Отправка устроена осторожно. Десять секунд на попытку, три попытки, пауза в секунду и потом в две, повтор только тогда, когда следующий ответ может отличаться, то есть на 5xx и 429. Переадресация не исполняется: один ответ «перейди на 127.0.0.1» обошёл бы всю проверку адреса. Адрес разбирается и проверяется перед каждой отправкой, а не только при сохранении. Пятнадцать неудач подряд выключают подписку с записанной причиной, а в кабинете видны последний код ответа, последняя ошибка и счёт неудач.
profile.create, profile.update, profile.delete
Профиль появился, изменился или уехал в корзину.
profile.start, profile.stop
Профиль открыли или закрыли на какой-то машине пространства.
proxy.create, proxy.update, proxy.delete
Общий пул прокси изменился.
apikey.create, apikey.revoke
Ключ выдан или закрыт.
team.invite, team.remove
Кого-то пригласили в пространство или убрали из него. Сам код приглашения наружу не едет.
plan.change
Сменился тариф или оплаченный срок.
| Право | Что можно ключом с этим правом |
|---|---|
| profiles:read | Смотреть профили с их задачей, группой и образцом отпечатка. |
| profiles:write | Заводить, править и удалять профили. |
| browser:run | Брать и отпускать замок профиля и записывать открытия и закрытия. Окно на сервере при этом не запускается. |
| proxies:read | Смотреть общий пул прокси. Пароли лежат за отдельным ходом, куда ключу дороги нет. |
| proxies:write | Добавлять, править и удалять прокси в пуле. |
| flows:read | Смотреть библиотеку сценариев пространства. Менять её может только человек с ролью не ниже управляющего. |
| logs:read | Читать журнал действий, журнал открытий и закрытий, входы с адресом и страной и правила подозрительной активности. |
Пределы записаны в коде, и описание тоже
Один предел покрывает все ходы сервера: 300 запросов за 60 секунд. Вход и пароли ограничены строже, тридцатью в минуту, а два хода там стоят на десяти и трёх. Письма в поддержку ограничены восемью за десять минут. Счётчик живёт в памяти самого сервера.
Пределов по тарифу нет, и делать вид, что они есть, мы не будем. Тариф решает, сколько у вас профилей, устройств и мест в команде, а не как часто можно звать. Если вы ведёте сотни профилей, темп своего цикла держите сами: поток запросов упрётся в те же 300, что и у всех.
У локального интерфейса на вашей машине предела частоты нет вовсе. Он отвечает программам, идущим под вашей же учётной записью, и единственный потолок это тело запроса в один мегабайт. Сервер MCP добавляет свой: ответ больше 5 МБ отклоняется с объяснением, и это лучше, чем вызов, зависший навсегда.
Описание не пишется руками, оно снимается с живого приложения. Работающий сервер отдаёт его на /docs, собирая из тех же контроллеров, которые обслуживают запросы, поэтому обещать несуществующий ход оно не может. Пометка о праве у каждого хода читается из настоящей пометки в коде, а не из второго списка, и ход без пометки прямо говорит, что ключам он закрыт. В описании есть и три дополнительных поля: список прав, список событий и все пути, куда ключ может дойти.
- Все ходы сервера
- 300 запросов / 60 секунд
- Вход и работа с паролем
- 30 в минуту, два хода по 10 и 3
- Письма в поддержку
- 8 за 10 минут
- Локальный интерфейс на машине
- без предела, тело до 1 МБ
- Размер ответа MCP
- 5 МБ, дальше понятный отказ
- Ключей API на аккаунт
- 20 действующих
- Подписок на события
- 10 на аккаунт
- Сценариев на одной машине
- 200, до 500 шагов в списке
Числа сняты с apps/server/src/app.module.ts, apps/desktop/src/lib/local-api.js, apps/desktop/src/lib/rpa.js и двух служб сервера.
Чего это не делает
- Облака, которое откроет вам браузер, нет. Профили работают на вашей машине, через программу. Сервер заводит, правит, делит и запирает профили, но никогда не запускает браузер.
- Ключи API это платная возможность. На бесплатном тарифе сервер отказывает в выдаче, а ключ с закончившимся тарифом получает 402, а не молчание.
- Оба набора разработчика имеют версию 0.1.0 и лежат в хранилище проекта. Ни один пока не опубликован в npm или PyPI, поэтому сегодня файл копируется в ваш проект.
- Помешать внешнему Puppeteer или Playwright включить домены Runtime и Page при подключении мы не можем. Если площадка, с которой вы работаете, ищет именно это, собирайте последовательность в программе.
- Пределов частоты по тарифу нет. Общий потолок в 300 запросов в минуту одинаков для всех, и держать темп большого парка профилей придётся вам самим.
- Ключ не управляет подписками, не меняет библиотеку сценариев и не читает пароль прокси. У этих ходов право не объявлено, и туда проходит только человек с паролем.
Как проверить
Каждое утверждение выше лежит в файле, который можно открыть, и почти каждое держит стенд, исполняющий настоящий код.
- Локальный интерфейс отвечает программам и отказывает страницам
- apps/desktop/src/lib/local-api.js · apps/desktop/test/local-api-gate.js
- Открытый порт отладки не ставит navigator.webdriver
- apps/desktop/src/lib/forkEngine.js · apps/desktop/test/webdriver-flag.js, который поднимает настоящий движок и читает значение на настоящей странице
- Наш исполнитель не включает домены Runtime и Page
- apps/desktop/src/lib/cdp.js, примечание DETECTION NOTE в начале файла
- Ключи хранятся отпечатком, ограничены правами и закрыты на бесплатном тарифе
- apps/server/src/api-keys/api-keys.service.ts · apps/server/src/auth/api-key.guard.ts · apps/server/test/api-keys.js
- Описание сходится с живым сервером, и оба набора идут против него
- apps/server/src/openapi/openapi.ts · apps/server/test/sdk-and-openapi.js
- Вызовы подписаны, повторяются и видны, когда не доходят
- apps/server/src/webhooks/webhook-dispatch.service.ts · apps/server/test/webhooks.js
Читать дальше
Автоматизация
Готовые примеры на Puppeteer и Playwright и оговорка целиком.
Что умеет программа
Шестнадцать видов шага в сборщике и всё остальное окно.
Движок
Что ARMANOS Browser 153.0.7978.0 меняет внутри Chromium.
Цены
В каких тарифах есть ключи API, места и устройства.
Доказательства
Как проверяются утверждения этого сайта и чем.
Соберите на этом уже сегодня
Программа бесплатна на два профиля, а локальный интерфейс входит в любой тариф, включая бесплатный.