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

Очередь подтверждений для автономных агентов не должна быть набором кнопок доступа. Это работающая система атрибуции. Если два или больше запуска агента могут запросить одни и те же учетные данные, вызвать один API или открыть одно SSH-соединение, рецензент должен перед подтверждением ответить на один вопрос: какой работающий процесс просит разрешить именно это действие?
Большинство интерфейсов подтверждения ошибается, потому что каждая карточка выглядит так, будто пришла от одного безымянного субъекта, «агента». В демонстрации с одним окном терминала это безобидно. Но когда один агент устраняет инцидент в продакшене, а другой готовит релиз, используя тот же клиент и запрашивая доступ к тому же сервису, такой дизайн опасен. Рецензент узнает знакомое назначение и в общих чертах понимает задачу, но подтверждает правильное действие для неправильного запуска.
Подтверждение - это решение об атрибуции
Рецензент подтверждает не отдельный HTTP-запрос, а запрос, потому что тот относится к конкретной работе, которая, как он ожидает, сейчас выполняется. Если интерфейс не сохраняет эту связь, кнопка подтверждения превращается в догадку, замаскированную под человеческий контроль.
Каждое подтверждение можно представить как утверждение из пяти частей:
- Его инициировал конкретный исполняемый файл или процесс агента.
- Конкретный сеанс этого процесса всё еще активен.
- Этот сеанс связан с названной задачей или рабочим элементом.
- Он хочет выполнить одно конкретное внешнее действие.
- Рецензент разрешает именно эту совокупность признаков или отклоняет ее.
«Разрешить доступ к API развертывания» - неполное утверждение. Рецензент может разрешить запуску релиза прочитать запись сборки, но не разрешить несвязанному запуску отладки изменить настройку релиза. Одно назначение не передает весь смысл.
Одинаковый клиент агента часто создает оба запуска. Команда, полномочия подписи и путь к репозиторию могут совпадать, а запросы могут быть почти одинаковыми. Но два одновременных запуска всё равно не становятся одним субъектом безопасности.
Иногда команды добавляют слова в описание действия, хотя настоящая проблема - отсутствие атрибуции. «POST /deployments» заменяют на «POST /deployments для запуска развертывания staging версии 1.8.4». Это объясняет операцию, но оставляет две карточки, которые обе выглядят как развертывание staging. Более удачное предложение не заменит границу сеанса.
Интерфейс должен показывать иерархию идентичности. Основной объект - действие. Инициирующий процесс и активный сеанс объясняют, почему оно появилось. Метка задачи помогает распознать намерение, но не заменяет технические идентификаторы.
Подписанный процесс не равен работающему сеансу
Идентичность подписи кода сообщает, кто подписал программу, но не то, какой запуск этой программы создал ожидающий запрос. Требования к коду устанавливают его идентичность, а designated requirement описывает, что считается тем же кодом в разных версиях. Это полезное свидетельство о вызывающей программе, но не замена идентификатору запуска.
Например:
Request from: Acme Agent CLI
Signed by: Example Engineering, Team ABCD1234
Такой заголовок показывает, что доступ запросил ожидаемый клиент. Но он не говорит, является ли это помощником релиза, запущенным пять минут назад, тестовым помощником из утреннего сеанса или копией команды во втором терминале.
Идентичность процесса и идентичность сеанса отвечают на разные вопросы:
| Поле | На какой вопрос отвечает | На какой не отвечает |
|---|---|---|
| Полномочия подписи кода | Кто создал этот исполняемый файл? | Какой это запуск? |
| Путь к исполняемому файлу | Какой установленный клиент его запустил? | Какую задачу он выполняет? |
| Идентификатор процесса | Какой локальный процесс владеет соединением? | Можно ли узнать его после перезапуска? |
| Идентификатор сеанса | Какой ограниченный запуск создал запрос? | Разумно ли это действие? |
| Метка задачи | Что пользователь попросил сделать? | Правдива ли метка и достаточно ли ее? |
Не скрывайте первые два поля после добавления идентификатора сеанса. Полномочия процесса помогают заметить неожиданный клиент, идентификатор сеанса разделяет одновременно работающие ожидаемые клиенты, а метка задачи связывает технический идентификатор с понятной картиной работы.
Практическое правило простое: показывайте идентичность процесса как источник запроса, а идентичность сеанса - как единицу, которой выдается разрешение.
На macOS одного идентификатора подписи особенно мало для метки подтверждения. Apple отмечает, что один идентификатор подписи могут заявлять несколько подписывающих сторон, и рекомендует сочетать проверку идентификатора с категорией валидации и, для кода не от Apple, с идентификатором команды. Не выводите необработанный язык требований как основную метку. Покажите полномочия подписи понятными словами, технические требования оставьте в подробностях и добавьте узнаваемое имя исполняемого файла. Идентификатор сеанса разместите так, чтобы его было видно без раскрытия карточки.
Дайте каждому запуску идентификатор, который сохраняется в очереди
Идентификатор сеанса должен появиться до первого защищенного вызова, оставаться неизменным до конца запуска и исчезать из набора разрешений после его завершения. Иначе при нагрузке возникает неоднозначность.
Создавайте непрозрачный идентификатор с высокой энтропией при регистрации соединения или процесса. Полное значение храните в аудите, а в запросе показывайте короткий однозначный префикс. Не выводите идентификатор только из времени, позиции в очереди, имени репозитория или PID.
{
"session_id": "ses_7TQ4N8M2KDXP6R9V",
"display_id": "7TQ4N8M2",
"process": {
"pid": 84172,
"executable": "/usr/local/bin/agent-cli",
"signing_authority": "Example Engineering (Team ABCD1234)"
},
"task": {
"label": "Prepare the staging release notes",
"workspace": "/Users/maya/work/app"
},
"started_at": "2026-07-22T16:42:11Z"
}
Метка задачи должна исходить из видимой человеку инструкции или заранее заданного названия сеанса, а не из фразы, которую модель меняет при каждом вызове инструмента. Если сеанс переходит от «подготовить заметки к релизу» к «заменить учетные данные production webhook», рецензенту нужно явно показать смену задачи или создать новый сеанс. Тихое переписывание метки затрудняет чтение истории и позволяет запуску заимствовать доверие у прежней безопасной задачи.
Используйте четкий жизненный цикл:
- Создавайте сеанс до любого действия с учетными данными.
- Привязывайте к нему каждый запрос, ответ, отказ, отмену и результат.
- Закрывайте сеанс, когда инициирующий процесс завершается или теряет действительное соединение.
- Немедленно отзывайте сеанс после команды «отозвать».
- Отклоняйте ожидающие подтверждения после закрытия или отзыва, даже если карточка еще видна.
Последнее правило предотвращает гонку. Рецензент видит карточку запуска A, запуск A завершается, затем начинается запуск B с похожим запросом, а старая карточка всё еще принимает нажатие. Если сервис привязывает нажатие к «последнему запросу для этих учетных данных», рецензент подтверждает B, глядя на A. Привязывайте карточку к одному неизменяемому идентификатору запроса и одному идентификатору сеанса. Когда любой из них становится недействительным, безопасной кнопкой остается только закрытие.
Не используйте названия вроде «Сеанс 1», «Запуск агента» или «Текущая задача». Они ломаются при появлении второго запуска. Дружелюбная метка допустима, но нужен идентификатор, который сохраняет смысл после перезапуска процесса, сна ноутбука и расследования через несколько дней.
Разместите важные факты в первых двух строках
Первые две строки запроса должны позволять отличить его от всех остальных ожидающих запросов без открытия подробностей. Сначала укажите действие, затем прямо под ним - инициирующий процесс и сеанс.
Allow POST to api.example.internal/v1/releases?
agent-cli, signed by Example Engineering, session 7TQ4N8M2
Task: Prepare the staging release notes
Creates a release record named "2026.07.22-rc3"
Credential: release-service-write
[Review request] [Deny] [Allow]
Строка действия сообщает, что произойдет и где. Строка атрибуции говорит, кто запросил действие. Строка задачи связывает запрос с нужной работой. Предварительный просмотр показывает вероятное последствие. Такой порядок выбран намеренно.
Не начинайте с «Запрошено подтверждение» или «Агент хочет использовать секрет». Эти фразы не помогают сортировать загруженную очередь. Не начинайте и с имени учетных данных: это деталь реализации. Сначала рецензенту важно понять, относится ли запрос к правильному запуску.
Для SSH укажите удаленный хост и класс команды, а не просто «доступ SSH»:
Allow SSH command on build-staging-03.example.internal?
agent-cli, signed by Example Engineering, session 7TQ4N8M2
Task: Verify the staging migration
Runs: /usr/local/bin/check-migration --database app_staging
Credential: deploy-ssh
[Review command] [Deny] [Allow]
Если безопасный понятный предварительный просмотр невозможен, честно сообщите об этом. «Аргументы команды недоступны» лучше выдуманного описания, скрывающего раскрытие shell, косвенный скрипт или непроверенный ввод.
Не заставляйте рецензента определять запуск по временной метке. Время относится к подробностям и журналу активности. Два агента могут отправить запросы в одну секунду, а люди плохо сопоставляют карточки с терминалами по времени. Время должно быть дополнительным свидетельством, но не основной меткой атрибуции.
Не прячьте идентификатор сеанса в бледном нижнем колонтитуле. В параллельной очереди это не диагностический мусор, а поле, которое не дает принять похожие карточки за взаимозаменяемые.
Очереди нужна группировка без ложного объединения
Группировка карточек по сеансам помогает ориентироваться, но становится опасной, если скрывает значимые различия между запросами.
Пусть рецензент видит все ожидающие действия одного сеанса вместе, но сохраняйте отдельное решение для каждого действия с разными последствиями. Несколько вызовов API только для чтения можно показать компактной группой. Чтение задачи, запись записи о развертывании и удаленная миграция требуют трех решений.
Неправильный вариант:
agent-cli requests access to 5 services
[Allow all]
Такая кнопка просит подтвердить набор действий еще до того, как рецензент соотнес каждый элемент с запуском и изучит последствия. Со временем она формирует привычку очищать очередь, не вникая.
Лучше показывать заголовки сеансов как контекст, а не как широкое разрешение:
Session 7TQ4N8M2 · agent-cli · Prepare the staging release notes
2 pending requests
GET api.example.internal/v1/builds/rc3 [Allow]
POST api.example.internal/v1/releases [Review]
Session C5J1W6PA · agent-cli · Investigate test failure #1842
1 pending request
SSH build-staging-03.example.internal [Review]
Рецензент может просматривать очередь по запускам, но каждая строка по-прежнему содержит отдельное утверждение. Если есть разрешение на уровне сеанса, четко укажите область действия: «Этот сеанс авторизован до завершения». Не создавайте впечатление, будто это подтверждает каждое действие вызова.
Дайте два представления: группированное по умолчанию для атрибуции и отсортированное по времени для реагирования на инцидент. Метка сеанса должна оставаться видимой в обоих вариантах. Не объединяйте карточки только из-за общего назначения и учетных данных. Два запуска, записывающие в одну конечную точку, создают именно ту неоднозначность, которую очередь должна устранять.
Подтверждение должно относиться к изученному запросу
Даже правильный запуск не спасает, если подтверждение относится к изменяемому шаблону, а не к неизменяемому действию. Рецензент должен подтверждать конкретное представление запроса, а не будущий запрос, случайно использующий тот же маршрут, команду или учетные данные.
Создайте каноническую запись до появления карточки. Для HTTP включите метод, нормализованные источник и путь, выбранные заголовки, псевдоним учетных данных и дайджест тела. Для SSH включите нормализованный хост, пользователя, порт, байты команды и псевдоним учетных данных. Полные защищенные данные должны оставаться внутри доверенного компонента.
{
"approval_id": "apr_K9H2D7LQ",
"session_id": "ses_7TQ4N8M2KDXP6R9V",
"action_digest": "sha256:2a13c4e0...",
"expires_at": "2026-07-22T16:47:11Z",
"decision": "allow_once"
}
Во время выполнения заново вычисляйте дайджест фактического действия. Если он изменился, отклоняйте подтверждение и создавайте новую карточку. Не считайте измененный запрос «достаточно похожим»: новый параметр URL может перенаправить платеж, заголовок изменить область учетной записи, а аргумент shell превратить проверку в запись.
Короткий срок действия ожидающего подтверждения нужен не для наказания рецензента, а для защиты от старого решения после изменения контекста. Если действие всё еще нужно, агент может отправить новый запрос с теми же данными процесса и сеанса.
Сделать подтверждение легким для нажатия не значит сделать его осмысленным. Решение должно быть достаточно маленьким для проверки и достаточно конкретным для ответственности.
Подробности должны разрешать споры
Компактная карточка не может содержать каждый байт запроса, но подробное представление должно отвечать на вопросы внимательного рецензента. Для HTTP покажите полный метод и URL, заголовки с редактированием секретов, поля тела, псевдоним учетных данных, идентичность процесса, ID сеанса, метку задачи и время создания. Для большого или двоичного тела укажите размер, тип содержимого и дайджест.
Для SSH покажите хост, пользователя, порт, состояние проверки хоста, точную команду, рабочий каталог и влияющие на поведение переменные окружения. Предварительный просмотр должен сохранять кавычки и границы аргументов. Небезопасно превращать массив в нечетко объединенную строку shell.
Редактирование секретов ради безопасности отличается от пропуска деталей ради удобства. Скрывайте bearer-токен, приватный ключ, пароль и секретное поле тела, но не скрывайте путь, целевую учетную запись, удаленный хост, команду или измененное поле. Именно они часто показывают, относится ли запрос к нужному запуску.
Используйте стабильный отпечаток запроса. Рецензент должен иметь возможность сказать: «Я отклонил запрос apr_K9H2D7LQ сеанса 7TQ4N8M2», а коллега должен найти ровно одну запись.
Allow once
This decision authorizes only this POST request with digest 2a13c4e0…
It does not authorize later calls from session 7TQ4N8M2.
Ясно сообщайте, что именно разрешает решение и чего оно не разрешает. Это предотвращает как неверное понимание широкого доступа, так и последующее незаметное расширение области действия.
Отмена и отзыв должны иметь видимые последствия
Рецензент может подтвердить не ту карточку или заметить, что запуск отклонился от курса. Восстановление должно быть быстрым, заметным и связано с теми же идентификаторами.
Разделяйте отказ для одного запроса и отзыв одного сеанса. Отказ означает «не выполнять это действие». Отзыв означает «этот процесс потерял разрешение, отмените его ожидающие подтверждения и отклоняйте последующие действия». Не объединяйте их в неоднозначную кнопку «Остановить».
Revoke session 7TQ4N8M2?
agent-cli is running “Prepare the staging release notes.”
Pending requests from this session will be cancelled. New protected actions
from this process will be denied until it starts a new session.
[Keep session] [Revoke session]
Если запрос уже выполняется, сообщите это честно. Отзыв может остановить будущие привилегированные действия, но не отменить HTTP-запрос, уже принятый удаленным сервисом, и не остановить удаленную команду, которая уже началась. В журнале указывайте состояние: ожидает, отправлен, завершен, завершился ошибкой или отменен. Не пишите «отозван», если внешний эффект уже произошел.
ID сеанса нужен в каждой карточке и для инцидентов. Если очередь показывает только agent-cli, можно остановить полезный запуск и оставить вредоносный активным.
Журнал сеансов и журнал активности должны использовать одинаковые идентификаторы. Один показывает, что сеанс начался, получил разрешение и был отозван, другой - какие действия он выполнял до и после каждой смены состояния.
Проверьте ошибочное подтверждение до пользователей
Запустите два экземпляра одного клиента агента в одном рабочем пространстве и попросите их выполнить похожие защищенные вызовы. Тест успешен только тогда, когда рецензент правильно различает запросы в загруженной очереди.
- Запустите A с меткой «Проверить неисправную staging-сборку».
- Запустите B с меткой «Опубликовать запись staging-релиза».
- В течение нескольких секунд попросите оба запуска использовать одни учетные данные.
- Сделайте первые запросы похожими, например вызовами одного API.
- Измените одно действие так, чтобы подтверждение для другого запуска имело заметное нежелательное последствие в тестовой среде.
Попросите человека, который не создавал интерфейс, подтвердить только запрос B. Не подсказывайте ему карточку по позиции в очереди. Если он полагается на время, порядок карточек или память о терминалах, интерфейс не справился. Если он без другого экрана видит полномочия процесса, ID сеанса, задачу, назначение и действие, это хороший результат.
Проверьте устаревшие подтверждения: создайте карточку A, завершите A, запустите B и нажмите старую карточку. Система должна отклонить нажатие и сообщить, что запрос больше не активен. Повторите тест после изменения тела HTTP-запроса или аргумента SSH. Нужно новое подтверждение.
Проверяйте совпадающие названия. Две задачи могут называться «Исправить CI», каталоги - иметь одно имя, а ветки - один номер релиза. Полная схема должна работать даже при неоднозначном дружелюбном тексте.
Sallyport удобно устанавливает такую границу: первый вызов нового процесса агента идентифицирует запуск для подтверждения, а ключи для отдельных вызовов могут требовать отдельного подтверждения. Сохраняйте на экране метку сеанса и полномочия процесса, иначе параллельные запуски снова сольются в одного безымянного «агента».
Аудит должен воспроизводить решение
Аудит полезен только тогда, когда после события можно понять, что видел рецензент и что выполнила система. Записи «пользователь подтвердил доступ к API» недостаточно. Нужно знать, для какого запуска, действия и отображенного контекста было выдано разрешение.
{
"event": "approval_granted",
"approval_id": "apr_K9H2D7LQ",
"session_id": "ses_7TQ4N8M2KDXP6R9V",
"process_identity": "agent-cli / Example Engineering (Team ABCD1234)",
"task_label": "Prepare the staging release notes",
"action_digest": "sha256:2a13c4e0...",
"displayed_action": "POST api.example.internal/v1/releases",
"decision_scope": "once",
"recorded_at": "2026-07-22T16:44:03Z"
}
Храните каноническую запись действия отдельно или рядом с событием, применяя шифрование и контроль доступа. Аудит должен показывать, что было написано на карточке, а каноническая запись - какие байты и назначение использовал доверенный компонент. Несовпадение между ними - дефект безопасности.
Цепочка хешей помогает обнаружить изменение истории, но не исправляет расплывчатое содержимое события. Целостность и атрибуция решают разные задачи, нужны обе.
Sallyport хранит записи уровня сеанса и отдельных вызовов в одном зашифрованном аудиторском журнале с цепочкой хешей. Команда sp audit verify проверяет эту цепочку офлайн по шифротексту, поэтому можно подтвердить неизменность журнала, не раскрывая секреты.
Финальная проверка проста. Возьмите завершенное действие и двигайтесь назад: можно ли определить процесс, конкретный сеанс, показанную метку задачи, точный проверенный запрос, область решения рецензента и результат? Затем двигайтесь вперед от сеанса: видны ли по порядку все ожидающие, отклоненные, подтвержденные, отмененные и выполненные действия? Если где-то нужна догадка, очередь всё еще допускает ошибочное подтверждение запуска.
Параллельные агенты сделают такие очереди обычной частью работы. Решение не в том, чтобы завалить рецензентов карточками или дать им широкую кнопку «разрешить всё». Дайте каждому процессу устойчивую идентичность сеанса, разместите ее рядом с действием и привяжите каждое нажатие к запросу, который рецензент действительно изучил. Только так подтверждение сохраняет смысл, когда одновременно работают несколько агентов.
Вопросы и ответы
Что такое очередь подтверждений параллельных агентов?
Очередь параллельных подтверждений агентов состоит из запросов, которые одновременно создают несколько процессов агентов. Рецензенту нужен контекст, позволяющий понять, какой работающий процесс создал каждый запрос, а не считать все карточки взаимозаменяемыми запросами от «агента».
Почему имени процесса недостаточно для подтверждений агента?
Имя процесса показывает, кто запустил работу, но не определяет конкретный запуск. Два окна терминала могут использовать один и тот же подписанный клиент и отправлять похожие запросы, поэтому карточке также нужны идентификатор сеанса и короткая метка задачи.
Каким должен быть идентификатор сеанса агента?
Используйте стабильный непрозрачный идентификатор сеанса, созданный при подключении процесса агента, и не меняйте его до завершения или отзыва процесса. Не используйте позицию в очереди, один лишь счетчик запросов или временную метку как единственный идентификатор сеанса.
Какая информация должна быть первой в запросе на подтверждение?
Сначала укажите действие и назначение, а сразу под ними назовите инициирующий процесс и сеанс. Обычно рецензент сначала решает, относится ли действие к нужной работе, и только потом изучает заголовки, параметры команды или тело запроса.
Нужно ли показывать в запросах полную команду или HTTP-запрос?
Карточка должна показывать краткий предварительный просмотр и давать возможность изучить точную команду, URL, назначение, метод и измененные поля. Не заставляйте рецензента подтверждать расплывчатое описание, пока полный запрос спрятан в журнале активности.
Как не допустить устаревания карточек подтверждения?
Карточка не должна незаметно менять смысл после появления. Привяжите подтверждение к неизменяемому дайджесту запроса, сделайте его недействительным после завершения сеанса и отклоняйте действие, если процесс пытается повторно использовать подтверждение для измененных аргументов.
Можно ли использовать короткий идентификатор сеанса в запросе на подтверждение?
Используйте постоянный идентификатор, который человек может сопоставить с карточкой, журналом сеанса и журналом активности. Короткая форма для отображения допустима, но она должна однозначно соответствовать полному идентификатору в аудите.
Что должно произойти после отзыва сеанса агента?
Отмена должна указывать затронутый сеанс и сообщать, что произойдет с ожидающими запросами. Если отзыв отменяет их, скажите об этом прямо. Если уже подтвержденный запрос может завершиться, покажите это состояние, не создавая впечатления, что отмена остановила всё.
Чем подтверждение сеанса отличается от подтверждения отдельного вызова?
Подтверждение сеанса отвечает на вопрос «может ли этот процесс вообще работать?». Подтверждение отдельного вызова отвечает на вопрос «может ли он выполнить именно это действие сейчас?». Проблемы начинаются, когда широкое подтверждение сеанса выдают за доказательство того, что ожидаем любой последующий разрушающий запрос.
Как тестировать очередь подтверждений агента?
Запустите параллельные сеансы с одним клиентом агента, репозиторием, учетными данными и конечной точкой. Если на снимке очереди со свернутыми подробностями рецензент не может понять, какая карточка относится к какому запуску, очередь не готова к реальной работе.