Перейти к содержимому

Agent Gateway: API для агентов с подписью в HSM

Agent Gateway предоставляет агенту чтение состояния и разрешённые операции с одним кошельком. Агент хранит bearer-токен, а ключ подписи обрабатывается HSM собственной разработки — 2d-hsm — в конфиденциальной виртуальной машине.

Агент обращается к шлюзу по аутентифицированному HTTP, шлюз — к подписанту через Unix-сокет. Перед подписью проверяются права токена и политика операции. Утечка токена всё равно даёт доступ к разрешённым им операциям; вынесение ключа в HSM не устраняет риск потери средств из-за скомпрометированного агента.

Шлюз доступен по адресу https://poa.net/77/agent/v1 в основной сети POA с идентификатором 77. Операции с кошельком требуют bearer-токена и соответствующего права доступа; частота запросов ограничивается по IP и агенту. Исключение — публичный POST /agent/v1/register, который выдаёт токен и назначает кошелёк с нулевым балансом. У регистрации отдельные квоты; она не подписывает платежи и не начисляет средства. Устаревший GET /faucet не публикуется.

МетодПраво доступаНазначение
GET /agent/v1/networkreadСведения о сети: возвращает версию API, метаданные цепочки, учётную запись и права агента, список разрешённых RPC-методов и лимиты запросов. Вызывать первым.
GET /agent/v1/balancereadБаланс кошелёк агента в базовых единицах токена (USDC, 6 десятичных знаков) плюс форма с 18 десятичными знаками для совместимости с eth_getBalance. Используйте этот метод для проверки баланса агента.
POST /agent/v1/rpcreadJSON-RPC-прокси для чтения. Принимает один запрос или пакет; набор методов фиксирован.
POST /agent/v1/faucetfaucetВыплата на кошелёк агента из казначейства сервиса выдачи средств, подписывается в анклаве. По умолчанию выключен.
POST /agent/v1/x402/authorizetransferАвторизация x402-платежа от кошелёк агента с проверкой котировки и политики подписанта.
POST /agent/v1/transferstransferПеревод с кошелёк агента получателю, подписывается в анклаве. По умолчанию выключен.

Демонстрационный ресурс и сервис расчётов x402 (facilitator) доступны отдельно. Агент запрашивает авторизацию платежа через указанный выше метод с правом transfer. Неоплаченный GET https://poa.net/77/x402/demo отвечает 402. В качестве asset используется прекомпайл NativeTransferAuth, а не контракт USDC в Ethereum. См. x402.

Регистрация не включает расходование средств. Faucet, transfer и x402 имеют отдельные выключенные по умолчанию переключатели, помимо AGENT_GATEWAY_ENABLED и требуемого права токена:

ПоверхностьВключение и лимиты платежейПовторы и граница восстановления
FaucetAGENT_FAUCET_ENABLED; оператор задаёт AGENT_FAUCET_PAYOUT_AMOUNT, AGENT_FAUCET_COOLDOWN_MS и AGENT_FAUCET_DAILY_LIMIT. Получатель — только кошелёк агента вызывающего; публичный токен не имеет faucet.Обязателен idempotency_key: повтор использует сохранённый результат, изменённый запрос конфликтует. FundingGate проверяет казначейство, получателя, резервные копии, привязку ключей и применимые подтверждения восстановления либо лимиты до его проверки.
TransferAGENT_TRANSFER_ENABLED; AGENT_TRANSFER_PER_TX_LIMIT и AGENT_TRANSFER_DAILY_LIMIT по умолчанию равны нулю. Опциональный agent_transfer_recipient_allowlist сужает получателей.Обязателен idempotency_key в пределах агента и вида операции; изменение данных запроса вызывает конфликт. FundingGate проверяет исходный кошелёк и подтверждения восстановления либо лимит кошелька до его проверки.
x402 authorizationAGENT_X402_ENABLED; точные сумма и получатель заданы AGENT_X402_AMOUNT и AGENT_X402_PAY_TO. AGENT_X402_MAX_TIMEOUT_SECONDS ограничивает срок действия (по умолчанию 180 секунд). В основной сети нужен действующий подписант Gateway.Идемпотентность: (agent_id, kind, idempotency_key), ключ по умолчанию x402:<nonce>; изменённые запросы конфликтуют. Действуют проверка привязки ключа и политика подписанта, но FundingGate для faucet/transfer, лимиты до проверки восстановления и суточные квоты переводов не применяются.

Для faucet и transfer до подтверждённого восстановления действует режим zero_only. Ненулевые лимиты допустимы только при явном исключении approved_non_zero с действующим локальным одобрением. Временное исключение не подтверждает готовность аварийного восстановления. x402 проверяется отдельно: выключение переводов или уменьшение их квот не выключает x402 и не ограничивает его совокупные расходы. Перед включением проверьте политику подписанта x402 и готовность восстановления. URL HTTP-ресурса входит в хеш запроса Gateway, но не в криптографическую авторизацию платежа.

RPC-прокси принимает только методы из списка, заданного в RpcProxy:

eth_chainId, eth_blockNumber, eth_getBalance,
eth_getTransactionByHash, eth_getTransactionReceipt, eth_getBlockByNumber

Остальные методы отклоняются с JSON-RPC-ошибкой и списком разрешённых методов. Через /rpc нельзя отправить транзакцию или изменить состояние, независимо от прав токена. Для операций агента предназначены отдельные методы /faucet, /transfers и /x402/authorize. Пакет RPC ограничен 100 запросами и размером 64 КБ в закодированном виде.

Начните с запроса сведений о сети:

Окно терминала
curl -s -H "Authorization: Bearer $AGENT_TOKEN" \
https://poa.net/77/agent/v1/network | jq .

Ответ содержит идентификатор сети 77, нативную валюту USDC с 6 десятичными знаками, последний блок, agent_id, assigned_wallet, права доступа, разрешённые RPC-методы и лимиты. Используйте эти сведения для настройки клиента.

Метод /balance возвращает только баланс кошелька агента. Метод eth_getBalance через /rpc позволяет прочитать баланс любого адреса. Право read также разрешает чтение квитанций подтверждённых транзакций: такие запросы не изменяют состояние.

Откройте Create API token. Когда регистрация включена, приглашение и email не нужны. Токен возвращается один раз; сервер хранит только хеш секрета. Аккаунт получает отдельный проверенный ключ анклава с резервной копией и фиксированные права read и transfer. Клиент не может указать кошелёк, право доступа или секрет токена.

Регистрация выключена в конфигурации, а начальная политика БД находится на паузе. После включения лимиты по умолчанию: 3 успешные регистрации на IP за сутки UTC, 100 на всю сеть за сутки UTC, общий потолок 1 000 и резерв 10 проверенных ключей. Попытки дополнительно ограничены 10 на IP и 120 глобально в минуту. IPv6 группируется по /64; IP-квота ограничивает злоупотребления, но не доказывает уникальность человека. Глобальные потолки ограничивают распределённую атаку.

Квота выдачи и назначение ключа фиксируются вместе под блокировкой БД. Ошибка назначения ключа всё равно расходует отдельный бюджет попыток. Пустой пул или достижение резерва возвращают 503; превышение частоты запросов — 429 с Retry-After. При потере HTTP-ответа кошелёк может быть выделен без доставки токена, поэтому автоматический повтор запрещён.

Закрытая консоль оператора показывает выдачи, отказы, запас ключей, последние аккаунты и использование API на текущем узле. В ней можно приостановить регистрацию, изменить квоты и отключить аккаунт без переиспользования ключа. Публичные маршруты Explorer не открывают административный API.

Авторизация и ограничение частоты запросов

Заголовок раздела «Авторизация и ограничение частоты запросов»

Каждый запрос к API кошелька проверяется по учётной записи агента: в БД хранятся идентификатор токена, хеш секрета, assigned_wallet, флаг enabled и права capabilities. Допустимы три права: read, faucet, transfer. Маршрут проверяет нужное право отдельно; токен только с read не может вызвать платёжный метод. Неизвестное право отклоняется при создании записи.

Компонент AgentAuth принимает запрос только с действующим токеном включённой учётной записи и нужным правом. Некорректный или отозванный токен и отключённая запись дают 401 agent_auth_failed; отсутствие нужного права — 403 agent_capability_required. Выключенный шлюз возвращает 404.

Ограничение частоты реализовано двумя скользящими окнами ETS в AgentRateLimit:

  • До аутентификации, по IP: 60 неудач аутентификации за 60 секунд. Защита от брутфорса токена. Учитываются только неуспешные аутентификации, поэтому легитимный клиент с большим числом запросов после одного успешного входа не штрафуется.
  • После аутентификации, по агенту: 600 запросов за 60 секунд. Ограничивает нагрузку от одного клиента независимо от числа используемых IP. Счётчик локален для узла.

Оба ограничения возвращают 429 при превышении. Для подсчёта по исходному IP за обратным прокси задайте доверенные CIDR в AGENT_TRUSTED_PROXIES. Иначе все клиенты будут учтены по общему адресу прокси. Настройка отделена от доверенных прокси операторского API моста. Публичный адрес работает по HTTPS: передача bearer-токена по открытому HTTP допускает его перехват.

Общий переключатель — AGENT_GATEWAY_ENABLED. Если значение отсутствует или отличается от строки "true", методы возвращают 404, а фоновые процессы шлюза не запускаются.

У каждой учётной записи ровно один assigned_wallet — 20-байтный адрес, полученный обычным для POA способом (см. Адреса). Ключ secp256k1 создаётся и используется для подписи в 2d-hsm. Кошельки агентов и оператор моста используют разные роли и ключи, описанные в топологии HSM. Агент и Gateway не получают открытый ключевой материал; резервные копии экспортируются в зашифрованном виде.

Анклав реализован на Rust и использует сообщения CBOR с обрамлением. Gateway подключается к сокету на хосте через AGENT_SIGNER_2D_HSM_ENDPOINT (unix://); прокси на хосте передаёт сообщения по vsock. Разрешён только профиль agent_gateway.

Для каждой привилегированной операции протокол передаёт объект полномочий capability с разделением доменов 2d-hsm/agent-cap/v1. Эти полномочия протокола HSM отличаются от прав API-токена. Методы API /transfers и /x402/authorize требуют права transfer; отдельного права x402 у токена нет. Для x402 дополнительно проверяются его переключатель, сумма, получатель и срок действия, а суточная квота обычных переводов не применяется. Генерация ключей и экспорт зашифрованных копий требуют уровня admin, восстановление — уровня recovery. Область финансовых полномочий, включая казначейство, привязана к анклаву; общего разрешения тратить средства всех анклавов протокол не предоставляет.

Ключи кошельков назначаются из пула. Анклав генерирует их партиями; каждая партия получает зашифрованную резервную копию и проходит проверку перед использованием. IdentityProvisioner выбирает свободный ключ в транзакции:

SELECT … FOR UPDATE OF k SKIP LOCKED LIMIT 1

Блокировка строки не позволяет двум параллельным операциям назначить один ключ. Учётная запись получает assigned_wallet и agent_transfer_key_ref — 64-символьный hex-идентификатор в хранилище анклава. В той же транзакции ключ переводится в состояние assigned. Пустой пул возвращает :agent_key_pool_exhausted, занятые конкурентными операциями строки — :agent_key_pool_busy; учётная запись при этом не создаётся. Публичная регистрация использует тот же механизм с полями, выбранными сервером. Ручная вставка также должна соблюдать инвариант назначения.

RefillWorker пополняет пул до целевого размера. BatchSweeper помечает зависшие партии ошибкой для последующей сверки. BackupSink сохраняет партию через временный файл, атомарное переименование и повторное чтение с проверкой SHA-256. Продвижение партии требует зашифрованной резервной копии и проверенной второй копии.

Bearer-токен даёт все назначенные агенту права до отзыва. Дополнительно Gateway периодически проверяет владение ключом: подписант создаёт identity proof, а шлюз проверяет подпись по сохранённому открытому ключу.

Подписываемое сообщение имеет отдельный домен 2d-hsm/agent-identity-proof/v1 и префикс EIP-191 0x19. Это отделяет подтверждение владения ключом от транзакционных подписей. Сообщение связывает четыре поля:

  • идентификатор сети (77),
  • идентификатор среды, к которому привязано хранилище анклава,
  • несжатый публичный ключ кошелька (65-байтовый SEC1, с проверкой on-curve),
  • и адрес (20 байт), который должен совпадать с адресом, выведенным из публичного ключа.

Анклав подписывает хеш сообщения keccak256 алгоритмом secp256k1 с требованием EIP-2 low-S. Gateway восстанавливает адрес из подписи, сверяет его с открытым ключом и отклоняет несовпадение. Формат определён в IdentityProof.

Подтверждение устанавливает владение ключом, но не личность вызывающего, подлинность образа TEE или готовность восстановления. Периодическая проверка не мешает украденному токену выполнять разрешённые операции, включая переводы и x402, пока токен не отозван и соответствующие политики разрешают эти действия. Аттестацию среды и восстановление проверяют отдельно.

По умолчанию AGENT_SIGNER_LIVE_IDENTITY_ENABLED не включён. Проверка владения ключом использует заглушку, возвращающую :agent_signer_protocol_unavailable. IdentitySweep работает и с заглушкой, и с действующим подписантом. Ответ заглушки относится к :terminal_unknown и не отключает кошельки.

При включении выбирается SignerClient.Live, который запрашивает настоящее подтверждение владения ключом. До переключения обязательна контрольная проверка (canary) с той же конфигурацией, что будет у узла. Она проверяет один назначенный ключ:

  • код 0: проверенный ключ соответствует конфигурации; это необходимая проверка перед переключением, но она не подтверждает аттестацию или восстановление;
  • код 1: нет ключа для проверки; сначала заполните пул;
  • код 2: не включайте режим; конфигурация или протокол не совпали либо временный сбой не позволил получить результат.

Canary не изменяет БД: даже несовпадение оставляет ключ в состоянии assigned. Проверка обязательна, поскольку ошибка конфигурации может повлиять на весь пул. Отсутствующий идентификатор сети или среды даёт ошибку конфигурации при каждом обходе. Некорректный, но допустимый по формату идентификатор сети может вызвать ошибку подписи класса :destructive, после чего обход отключит затронутые кошельки. Это не списывает средства, но требует исправления конфигурации и повторного включения ключей.

При подключённом подписанте IdentitySweep работает на одном назначенном экземпляре сервиса. Он повторяет проверку для включённых записей, срок предыдущей проверки которых истёк. Только один класс результата изменяет доступность ключа:

КлассЧто означаетДействие
:destructiveКлюч, открытый ключ, адрес или подпись не соответствуют сохранённой учётной записиКлюч отключается; автоматического повтора нет.
:retryableВременная ошибка связи с анклавомОшибка записывается; кошелёк остаётся включённым; повтор при следующем обходе.
:terminal_config / :terminal_wire / :terminal_agent / :terminal_malformedОшибка конфигурации, несовпадение протокола или некорректный ответОшибка записывается при каждом обходе; состояние кошелька сохраняется.
:terminal_unknownНеожиданное исключение, завершение процесса или ответ «недоступно» от заглушкиОшибка записывается; состояние кошелька сохраняется.

Только :destructive отключает кошелёк. Временная ошибка транспорта или неподключённый подписант сохраняют прежнее состояние. Повторное отключение не меняет результат. Для восстановления оператор устраняет причину и включает ключ через закрытый API.

Операции faucet, transfer и авторизация x402 записывают agent_actions до подписи, с уникальностью (agent_id, kind, idempotency_key). Faucet и transfer требуют явный ключ; x402 использует kind :x402 и по умолчанию ключ x402:<nonce>. Изменение payload при том же ключе вызывает конфликт.

Операции faucet и transfer проходят состояния requested → signed → submitted → confirmed либо завершаются ошибкой. Авторизация x402 возвращает подписанные данные платежа и сохраняет их для повтора; расчёт отдельно выполняет facilitator. Поэтому запись x402 не проходит этапы отправки и подтверждения обычного перевода и не подчиняется его квотам. Проверка владения ключом не заменяет FundingGate и условия восстановления. Ограничения котировки, nonce и повторного исполнения описаны в x402. У регистрации нет такого механизма безопасного повтора.

Пока производство блоков остановлено, запрос faucet или transfer с новым idempotency_key получает 503 с error.code producer_halted и retryable: true. Запись не создаётся и ничего не подписывается, поэтому повторите тот же ключ позже. Ключ, использованный до остановки, получает сохранённый ответ.

Безопасность Gateway зависит от трёх отдельных механизмов:

  1. Авторизация и отключение токена. HSM подписывает только разрешённые запросы. Украденный токен сохраняет свои права до отключения учётной записи; состояние записи проверяется при каждом запросе. Отключение не отменяет уже отправленную транзакцию.
  2. Проверка ключа. Периодическое подтверждение владения выявляет несовпадение с сохранённой записью и может отключить кошелёк. Оно не подтверждает личность вызывающего, не заменяет аттестацию и проверку восстановления и не мешает злоупотреблять действующим токеном.
  3. Независимые переключатели и лимиты. Шлюз, faucet, transfer и x402 включаются отдельно. У переводов по умолчанию нулевые лимиты; ненулевые лимиты до проверки восстановления требуют временного одобрения. x402 имеет собственные сумму, получателя и срок, но не суточную квоту обычных переводов.

Список RPC-методов, ограничения размера пакета и частоты запросов дополнительно ограничивают API. См. RPC-методы и ограничения пакетов, а также авторизация и частота запросов.

Кошельки агентов используют отдельное пространство имён в хранилище 2d-hsm. Для правил сети это обычные аккаунты: к ним применяются та же модель безопасности и проверка корня состояния.

Сеть 77 — основная сеть POA. Значения по умолчанию в коде, активная конфигурация и проведённые проверки восстановления — разные факты. Перед изменением политики пополнения или подписи проверьте развёрнутый релиз, подписанные результаты проверки восстановления и актуальный canary. Эта статья не подтверждает проведение конкретной процедуры восстановления.

Публичная регистрация никогда не даёт faucet. Право transfer разрешает обычные переводы и авторизацию x402, когда это разрешают их отдельные переключатели и политики. Потеря токена может лишить доступа к кошельку: сохраните токен до пополнения. Назначенные ключи не переиспользуются после таймаута или отключения аккаунта.

Gateway предоставляет прикладной API той же сети POA. См. форматы адресов, модель безопасности и разделение ролей подписантов. Подпись блоков ML-DSA описана отдельно в архитектуре TEE.