ARMANOS

Решения

Разработчикам

Ваш код открывает настоящие профили: у каждого свой отпечаток, свои куки, своё хранилище и свой прокси. Локальный сервер на 127.0.0.1 открывает профиль и отдаёт точку отладки, принадлежащую только ему, поэтому Puppeteer, Playwright или ваш собственный клиент подключаются как к обычному Chromium. Всё, что написано ниже, работает в программе сегодня, включая ту оговорку, о которой лучше узнать от нас, а не от площадки.
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 с причиной.

ХодМетодЧто делает
/statusGETЕдинственный ход без пропуска. Отвечает готовностью и версией программы.
/api/v1/profile/listGETВсе профили: задача, группа, образец отпечатка, краткое описание прокси без паролей и признак «открыт сейчас». Тот же ответ отдаётся на /api/v1/browser/list.
/api/v1/browser/startPOSTОткрывает профиль той же дорогой, что и кнопка в окне. Возвращает точку отладки. Принимает headless на один прогон.
/api/v1/browser/stopPOSTЗакрывает все окна профиля и говорит, сколько закрыл.
/api/v1/browser/activeGETНомера профилей, у которых прямо сейчас открыто окно.
/api/v1/profile/createPOSTЗаводит профиль через ту же проверку доступа и нормы, что и программа. Возвращает новый profileId и сам профиль.
/api/v1/profile/deletePOSTПереносит профиль в корзину, откуда программа умеет его вернуть.
/api/v1/profile/codeGETТекущий шестизначный код для профиля с сохранённым ключом двухфакторного входа и сколько секунд он ещё годен.
/api/v1/proxy/listGETСохранённая библиотека прокси с итогом последней проверки. Паролей в ответе нет никогда.
/api/v1/flow/listGETСценарии на этой машине и число шагов у каждого.
/api/v1/flow/runPOSTЗапускает сценарий по названным профилям, с выбранной вами одновременностью.

Каждый открытый профиль отдаёт свою собственную точку отладки

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. 1

    Включите локальный интерфейс

    Настройки, раздел API и MCP. Пропуск копируется с того же экрана.

  2. 2

    Попросите профиль

    POST /api/v1/browser/start с profileId и пропуском в заголовке x-api-token.

  3. 3

    Возьмите data.ws из ответа

    Это порт и путь сокета именно этого профиля и никакого другого.

  4. 4

    Подключитесь и работайте

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

Экран API и MCP в программе ARMANOS: местный адрес, пропуск и готовая настройка для клиента MCP.
Адрес, пропуск и готовый блок настройки MCP на одном экране, с кнопками копирования.

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

Соберите на этом уже сегодня

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