# Локальный контроль действий в рабочих процессах с AI-программированием: где он уместен

Локальный контроль действий подходит для рабочего процесса с AI-агентом, когда один разработчик владеет компьютером, может подтвердить запуск и хочет, чтобы агент выполнял действия за пределами репозитория, не получая сами секреты. Это хороший ответ на узкую, но распространённую задачу: агент достаточно безопасно редактирует код в рабочем пространстве, однако ему также нужно обратиться к API, загрузить закрытый артефакт или выполнить команду по SSH.

Этот подход плохо работает, когда его пытаются превратить в систему авторизации для всего парка устройств. Подтверждение на Mac может показать, что конкретному локальному процессу разрешено использовать учётные данные. Но оно не скажет production-сервису, какой арендатор, окружение, окно изменений или бизнес-правило должно разрешить получившийся запрос. Такие решения нужно принимать там, где находится ресурс.

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

## Локальный контроль относится к машине, которой пользуется конкретный человек

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

Вопрос владения кажется очевидным, пока команда не посмотрит, как на самом деле выполняется работа. Ноутбук может быть закреплён за одним человеком, но регулярно использоваться через удалённый рабочий стол. На общем Mac в лаборатории за неделю могут войти несколько инженеров. Управляемый сборочный хост может работать от имени разработчика, но запускать задания, созданные pull request. Ни один из этих фактов сам по себе не создаёт настоящего человеческого контроля.

Перед добавлением локальных подтверждений задайте четыре конкретных вопроса:

- Кто может физически разблокировать компьютер и одобрить запрос?
- Какой исполняемый файл запускает агента и может ли владелец распознать издателя его подписи?
- Остаётся ли работа агента внутри интерактивной сессии или продолжается после ухода человека?
- Если компьютер скомпрометирован, что ограничивает доступ учётных данных к удалённому сервису?

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

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

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

## Владение компьютером не сводится к имени учётной записи

Имя учётной записи разработчика не доказывает, что одобренный процесс действительно выполняет работу. В macOS агент может быть запущен терминалом, расширением редактора, вспомогательным процессом или скриптом из репозитория. У этих источников совершенно разный уровень доверия.

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

```sh
ps -axo pid,ppid,user,etime,command | grep -i '[a]gent'
```

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

Затем проверьте исполняемый файл, а не доверяйте имени:

```sh
codesign -dv --verbose=4 /path/to/executable 2>&1 | \
  grep -E '^(Identifier|TeamIdentifier|Authority)='
```

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

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

Спецификация Model Context Protocol описывает обмен вызовами инструментов между MCP-клиентом и сервером. Она не подтверждает, что вызывающая сторона является одобренным локальным процессом. Совместимость с MCP отвечает на вопрос об интерфейсе. Идентификация процесса, хранение учётных данных и авторизация остаются отдельными задачами.

## Одобрение сессии работает, когда у запуска есть естественная граница

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

Границей должна быть реальная длительность процесса, а не расплывчатое понятие вроде «работа сегодня днём». Сессия заканчивается, когда процесс завершается. Такое правило легко объяснить, отозвать и трудно переиначить агенту. После перезапуска процесс запросит подтверждение снова. Если человек отозвал разрешение, будущие вызовы завершатся отказом и не унаследуют доверие от прежнего нажатия.

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

Одобрение сессии имеет более узкую цель, чем движок политик. Оно означает: «Я узнаю этот локальный процесс и разрешаю ему использовать допустимый путь действий, пока он работает». Оно не должно пытаться определить, безопасен ли SQL-запрос, правдоподобно ли выглядит название задачи или заслуживает ли текущая ветка доступа к production. Это решения удалённой авторизации или правила рабочего процесса, а естественные языковые запросы плохо подходят для их хранения.

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

Окна подтверждения также должны предсказуемо вести себя при сбое. Если хранилище заблокировано, каждое действие должно завершаться отказом. Если интерфейс подтверждения недоступен, потому что Mac заблокирован или находится в режиме сна, каждое действие также должно завершаться отказом. Скрытый резервный путь, позволяющий запросам проходить при ошибке интерфейса, превращает человеческую границу в декорацию.

## Подтверждение каждого вызова нужно для действий с дорогими последствиями

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

Классифицируйте действие по последствиям, а не по протоколу. HTTP POST может создавать всего лишь временную запись для предварительного просмотра. SSH-команда может только прочитать журнал развёртывания. GET-запрос может экспортировать весь набор данных клиентов. Режим подтверждения определяют не метод и транспорт.

Используйте подтверждение каждого вызова, если действие обладает одним или несколькими свойствами:

- Удалённый сервис не может надёжно отменить результат.
- Учётные данные могут повлиять на общий production-ресурс.
- Запрос способен передать конфиденциальные данные за пределы ожидаемого назначения.
- Операция выполняется настолько редко, что осознанное подтверждение не превратится в привычное нажатие.

Последний пункт особенно важен. Подтверждение каждого частого действия приводит к тому же механическому нажатию, что и слишком чувствительное подтверждение сессии. Разделяйте учётные данные, когда это возможно. Для обычной разработки используйте учётные данные с доступом только к development-ресурсам, а учётные данные с правами production оставьте для небольшого числа действий, ради которых оправдано прерывание.

Агент не должен сам решать, какие вызовы требуют подтверждения. Если он может пометить запрос как «только чтение» или самостоятельно выбрать класс учётных данных, инъекция в запрос или простая ошибка реализации направит его по более лёгкому пути. Требование подтверждения должен задавать владелец учётной записи за пределами рабочего пространства агента.

Подтверждение должно быть достаточно понятным для настоящего решения. Человеку нужны имя или назначение учётных данных, адрес назначения, метод запроса или форма команды и идентификатор процесса, который инициировал вызов. Полное тело запроса может раскрыть секреты или перегрузить человека. Текст «Одобрить действие?» не даёт полезного контекста. Хороший дизайн подтверждения сообщает меньше лишнего, но достаточно для отклонения неожиданного вызова.

## Хранение секретов локально решает одну проблему, но не все

Локальное хранилище не даёт агенту держать API-ключи и закрытые SSH-ключи в открытом виде. Это заметно снижает риск случайной утечки через запросы, журналы, файлы, скопированные данные и код, написанный агентом. Кроме того, оператор может отозвать разрешение активного запуска, не меняя сразу сами учётные данные.

Но возможности удалённых учётных данных от этого не меняются. Если API-токен позволяет удалить все проекты, сервис примет запрос на удаление после локального подтверждения. Если у SSH-учётной записи широкие права sudo, защита закрытого ключа не превратит её в узкую учётную запись для развёртывания. Максимальный ущерб по-прежнему определяют минимальные права на удалённой стороне.

Различайте два вопроса:

1. Хранение учётных данных: может ли агент получить или воспроизвести секрет? Локальное хранилище хорошо решает эту задачу.
2. Авторизация ресурса: должен ли сервис принять действие в текущих условиях? На этот вопрос должны ответить API, хост, поставщик удостоверений или система развёртывания.

Команды часто смешивают эти вопросы, потому что оба возникают, когда агент обращается к API. Результат предсказуем: они устанавливают локальное хранилище секретов, а за ним оставляют долгоживущий токен администратора. Токен теперь сложнее случайно раскрыть, но путь действий всё ещё слишком широк.

SSH делает различие особенно очевидным. Локальный компьютер может защищать закрытый ключ, но именно хост определяет, что разрешено выполнять учётной записи, связанной с открытым ключом. По возможности создавайте отдельные учётные записи или ограничения forced command для автоматизации. Ограничивайте доступ к хостам. Не используйте личный ключ инженера с правами администратора как универсальный ключ агента. Со временем в личных ключах накапливаются исключения, а именно их автономный процесс не должен наследовать.

Sallyport хранит API- и SSH-учётные данные в зашифрованном хранилище приложения и выполняет HTTP- или SSH-действие, не передавая секрет агенту. Это полезно на компьютере разработчика, но команде всё равно нужны узкие учётные данные и серверные права.

## Проверьте путь действия до того, как начнёте ему доверять

Перед подключением production-возможности протестируйте шлюз действий на безвредной конечной точке и временных учётных данных. Тест должен показать три вещи: агент не получил секрет, шлюз записал вызов, а удалённый сервис увидел ожидаемую личность вызывающей стороны.

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

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

Для восстановления событий полезна запись аудита, содержащая достаточно сведений, но не сам секрет. Для HTTP-действия записывайте время, идентификатор процесса или сессии, метку учётных данных, адрес назначения, метод, статус результата и идентификатор запроса, если удалённый сервис его предоставляет. Для SSH записывайте назначение, метку учётной записи, результат команды и её представление с учётом правил конфиденциальности. Не добавляйте в журнал bearer-значения, закрытые ключи или полные конфиденциальные данные только ради ощущения полноты аудита.

Связывание записей хешами помогает обнаружить изменение локальной истории, но не доказывает, что исходное действие было разумным. Храните запись шлюза рядом с журналом аудита принимающего сервиса. Если они расходятся, это повод для расследования, а не основание автоматически объявлять один источник правильным.

Sallyport проецирует журналы сессий и активности из зашифрованного аудита, связанного хешами, а `sp audit verify` проверяет эту цепочку офлайн без ключа хранилища. Запускайте проверку во время разбора инцидента или передачи релиза, но не путайте проверку целостности с авторизацией.

## Серверный контроль нужен, когда людей нет рядом или ресурсы общие

Серверный контроль необходим, если действие должно продолжаться без конкретного разработчика, который подтвердит его. Сюда относятся задания CI, плановое исправление проблем, агенты на серверах, общие воркеры и рабочие процессы развёртывания. Локальный Mac не может быть окончательной инстанцией для работы, которая должна пережить разряженную батарею, поездку, сон компьютера или уход сотрудника из компании.

Размещайте решение об авторизации рядом с защищаемым ресурсом, если выполняется хотя бы одно условие:

- Один и тот же рабочий процесс могут запускать несколько людей или систем.
- Целью служат production, данные клиентов, финансовые операции или регулируемая система.
- Агент работает на инфраструктуре, а не на принадлежащем человеку компьютере с интерактивной сессией.
- Сервис должен соблюдать границы арендаторов, окна изменений, правила окружений или разделение обязанностей.
- Рабочему процессу нужна высокая доступность без подтверждения в окне человеком.

В таких случаях используйте идентичность рабочей нагрузки с узкими правами, коротким сроком действия, если это поддерживает система удостоверений, и серверными записями аудита. Устанавливайте границы окружений в системе развёртывания или API. Если организации нужно подтверждение изменения, требуйте его там же. Локальный компьютер может помочь разработчику подготовить и проверить изменение, но не должен быть точкой enforcement для production-действия.

NIST Special Publication 800-207 описывает zero trust как модель, в которой решения о доступе сосредоточены на защите ресурсов, а не на доверии к расположению в сети. Полезный вывод для рабочих процессов с агентами не в том, что каждому локальному инструменту нужен сложный язык политик. Важно, чтобы production API или хост самостоятельно принимал решение о вызывающей стороне и запрошенном ресурсе. Подтверждение на Mac не может заменить это решение.

Не решайте проблему, передавая локальные учётные данные разработчика в CI. Так управляемые человеком учётные данные превращаются в учётные данные без оператора, но не получают ясности ни одной из моделей. Создайте отдельную идентичность рабочей нагрузки и выдайте ей только необходимые права.

## Не превращайте окна подтверждения в движок политик

Команды часто хотят правила вроде «разрешать GET-запросы, кроме нерабочего времени» или «разрешать SSH, только если имя ветки содержит release». Запрос кажется привлекательным: он должен уменьшить число нажатий и сохранить контроль локально. Обычно результатом становится хрупкая система политик, которую никто не может объяснить в напряжённой ситуации.

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

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

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

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

## Смешанный рабочий процесс даёт каждому средству контроля свою задачу

Большинству команд нужны и локальный, и серверный контроль. Практическая архитектура не сводится к выбору «всё или ничего».

Разработчик может запустить локального интерактивного агента, чтобы проверить staging API, прочитать метаданные закрытого пакета или выполнить узкую SSH-диагностику. После проверки личности процесса он одобряет запуск. Локальное хранилище предоставляет учётные данные, которые агент никогда не видит. Записи активности позволяют провести последующую проверку.

Тот же агент может подготовить изменение для развёртывания, не получая права его выполнить. Затем серверный конвейер запускается с собственной идентичностью рабочей нагрузки, применяет ограничения production и записывает результат развёртывания. Если конвейеру нужно одобрение человека, разместите его в системе, которая отвечает за production-изменение. Там оно останется видимым людям, отвечающим за это окружение.

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

Начните с того, что нарисуйте все действия с учётными данными в одном актуальном рабочем процессе. Запишите, какой процесс их инициирует, на каком компьютере хранится секрет, какой ресурс его получает, кто может остановить действие и должно ли задание выполняться без этого человека. Так быстро обнаружатся действия, подходящие для локального контроля, действия, требующие серверного enforcement, и неудобные случаи, где сейчас нет ни того, ни другого.
