Секреты и безопасный режим
Пароль, токен или ключ раньше попадали в чат единственным способом: вы писали их сообщением. Дальше они жили в транскрипте на диске, в контексте модели и в каждом следующем запросе к провайдеру — убрать их оттуда было уже нельзя.
Теперь у агента есть отдельный путь: он просит значение карточкой, а вы отдаёте его так, что в переписку оно не попадает.
Как это выглядит
- Агенту нужен пароль — он просит его по имени и объясняет, зачем.
- Ответ встаёт, а над композером появляется карточка: поле-пароль, «Готово» и «Не дам». Значок «глаз» в самом поле показывает набранное, если надо проверить.
- Значение уходит на сервер и живёт только в его памяти. Агенту достаётся ответ «принят» — он знает имя, а не значение.
- Дальше агент пишет в команде
{{secret:имя}}, и сервер подставляет значение уже при запуске.
Пока карточка висит, работа стоит. Об этом зовут те же уведомления, что и о вопросе модели: пуш в браузер, тост в трее, шторка Android, сообщение бота — а в списках чатов такая сессия стоит с 🔑, именем секрета и оранжевой рамкой. Ответить из уведомления нельзя нигде и намеренно: значение вводится только карточкой в самом чате. Пароль, отправленный боту, лёг бы на серверы Telegram.
Записка рядом со значением
Второе поле карточки — записка агенту, и она едет открытым текстом. Секрет редко бывает самодостаточным: «это временный, до вечера», «сначала VPN», «у этой базы другой порт». Без записки карточка умела ровно два ответа — «принят» и «не дам», — и всё остальное приходилось дописывать сообщением.
Пароль — в поле-пароль, условия — в записку. Не наоборот.
Что происходит со значением
- Значение не пишется на диск и не попадает в транскрипт: оно живёт в памяти сервера, пока идёт эта сессия.
- Команда получает его через ввод оболочки, а не аргументом: в
psкомандные строки чужих процессов видны всей машине, и пароль в аргументе — пароль на виду. - Вывод команды чистится: если она сама напечатает то, что ей дали, в ленте и у модели будет
•••. - Перевод строки в значении не принимается — построчная передача его не переживёт, и вместо тихой порчи вы получите отказ.
Команда может отдать секрет дальше сама
Защищена командная строка нашей оболочки, но не той, которую она запускает. ssh host "… $PASS", docker run -e PASS=$PASS, wsl.exe -- bash -lc "PASS='$PASS' …" раскрывают значение в ps чужого процесса. Агент об этом предупреждён, но если такую команду пишете вы — помните.
Безопасный режим
Рубильник на самом чате, рядом с выбором режима прав, — и в вебе, и в Android. Включён по умолчанию, выключается осознанно и по одному чату: секрет, один раз попавший в переписку, оттуда уже не убрать.
Что он делает:
- говорит агенту, как просить — не искать пароли в файлах и переменных окружения, не звать вас прислать их сообщением, а поднимать карточку;
- закрывает файлы-хранилища —
.env,.credentials.json, ключи ssh,*.pem,.aws/**,.npmrc,.netrc,.docker/config.json: чтение таких путей инструментами файлов отбивается с объяснением, что делать вместо этого.
Чего он не делает:
- не перекрывает оболочку —
Bashпрочитает файл сотней способов, и запрет наcatрисовал бы границу там, где её нет. Настоящая граница другая: секрета нет на диске вовсе, а терминал ключу можно и не выдавать; - не защищает от вас самих — пароль, написанный сообщением, лежит в транскрипте, и режим тут ни при чём;
- не шифрует память сервера — тот, кто читает память процесса, читает и секреты. От этого защищает не режим, а то, что доступ к машине есть только у вас.
В терминале пароль тоже никуда не оседает
ssh, sudo, gh auth login спрашивают пароль сами. Терминал видит, что идёт скрытый ввод, — по настоящему признаку, а не по словам приглашения, — и набранное не остаётся ни на экране, ни в истории клиента.
Чего пока нет
- Секретов между сессиями. Каждый новый чат просит заново: хранить их дольше — значит завести хранилище на диске, а это ровно то, от чего уходили.
- Подстановки в свои команды.
{{secret:имя}}понимает только агент; команда, которую вы набираете сами в терминале или через!, пароль спросит обычным образом.