The examples assumed a repository checkout. A reader who installed from the skill registry has `plugin/scripts/ctxguard.py` at the archive root instead -- singular, and without the `plugins/ctxguard/` prefix, because the archive holds exactly one plugin. Following the examples verbatim gave them "No such file or directory" on the first command. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
26 KiB
ctx-tools — как пользоваться
Маркетплейс плагинов для Claude Code. Сейчас в нём один плагин:
ctxguard — санитизация контекста: вырезает креденшлы, подменяет имена компаний,
людей и PII на стабильные обратимые алиасы до того, как они попадут в модель.
Принуждение живёт в хуках, поэтому агент не может это выключить.
Этот файл — про то, как этим пользоваться. Раздача плагина коллегам и процесс релизов — в README.en.md.
Установка
Вариант 1. Через маркетплейс (рекомендуется)
/plugin marketplace add /path/to/skills
/plugin install ctxguard@ctx-tools
Вместо локального пути можно указать <owner>/<repo> на GitHub, git-URL или любой
путь в файловой системе. Ничего не тянется из сети во время работы, зависимостей кроме
python3 нет.
Для всей команды — объявить на уровне проекта и закоммитить результат, чтобы клонирования репозитория было достаточно:
claude plugin marketplace add <owner>/<repo> --scope project
claude plugin install ctxguard@ctx-tools --scope project
Вариант 2. Копированием папки
cp -r plugins/ctxguard ~/.claude/skills/ctxguard
Загрузится в следующей сессии как ctxguard@skills-dir, вместе с хуками. Два
нюанса:
- Имя должно быть свободно. Установленный плагин с тем же именем побеждает, и
просто отключить его недостаточно — имя остаётся занятым до
claude plugin uninstall. - Копируйте каталог, а не симлинк на репозиторий, который потом переедет.
Этот способ — для одной машины или одного коллеги. Маркетплейс — когда одну и ту же версию нужно раздать нескольким людям и обновление должно быть командой, а не перекопированием.
ctxguard: повседневное использование
Ниже ctxguard — сокращение для вызова скрипта. Сам путь зависит от того, как
инструмент установлен, и это первое, обо что спотыкаются:
| Откуда запускаете | Путь к скрипту |
|---|---|
| Клон этого репозитория | python3 plugins/ctxguard/scripts/ctxguard.py |
| Распакованный ZIP из реестра скиллов | python3 plugin/scripts/ctxguard.py |
| Внутри сессии Claude Code | python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ctxguard.py" |
В ZIP-поставке каталог называется plugin в единственном числе и лежит в корне
архива — там нет промежуточного plugins/ctxguard/, потому что архив содержит ровно
один плагин.
Удобно завести алиас под свой случай, например для распакованного архива:
alias ctxguard='python3 ~/.local/share/ctxguard/plugin/scripts/ctxguard.py'
Все примеры ниже записаны через это сокращение, подставьте свой путь.
Что он делает
- Креденшлы удаляются безвозвратно — на их месте остаётся маркер вида
SECRET:правило:хэшв угловых скобках. Восстановить значение нельзя, и это осознанно: отредактированный приватный ключ бесполезен. - Имена и PII превращаются в стабильные алиасы (
CTXG_COMPANY_A,CTXG_PERSON_B,CTXG_EMAIL_7F3A) — и переводятся обратно в реальные значения по пути на диск и в шелл. Поэтому агент продолжает делать реальную работу: код и команды, ссылающиеся на алиасы, работают. - Принуждение живёт в хуках, а не в скилле. Агент не может это выключить, а то, что он забыл правила, безвредно — решение принимает хук.
Хуки: SessionStart, UserPromptSubmit, PreToolUse (перехват до запуска
инструмента), PostToolUse (обратный перевод при записи), SessionEnd.
Первый запуск в новом проекте
ctxguard init # создать состояние, показать что активно
ctxguard scan . # что реально лежит в этом репозитории (только счётчики)
ctxguard entity add "Acme Corp" --type company --hint "розничный клиент"
ctxguard verify # 43 канареечных кейса через настоящие точки входа хуков
ctxguard scan-transcript # что реально дошло до модели
Словарь стартует пустым. Детект креденшлов и PII работает сразу; имена компаний,
людей и хостов защищены только после регистрации — поэтому scan, а затем
entity add — первая работа в новом репозитории.
Словарь привязан к корню git-репозитория, а не к текущему каталогу, так что
сессия, начатая в src/, использует тот же словарь.
Как выбрать --match — от этого зависит, скрывает ли алиас хоть что-то
| Режим | Когда | Что ловит |
|---|---|---|
exact (по умолчанию) |
имя встречается только в прозе | целые слова, терпит гомоглифы (кириллическая с вместо латинской c) и разные разделители: Acme Corp / acme-corp / AcmeCorp |
stem |
всё, что склоняется — в первую очередь русские фамилии | плюс до четырёх символов окончания на слово: Петров / Петрова / Петровым. Перебирает по замыслу: Петров поймает и Петровский |
ident |
любое имя, встречающееся в коде | без границ слова, так что значение находится внутри идентификаторов: ContosoClient, CONTOSO_API_KEY, contoso.rs. С exact все три утекут в реальном написании, выглядя защищёнными. Нужно ≥4 символов |
regex |
точная настройка | используется как есть, без именованных групп |
Побеждает самый длинный шаблон, так что регистрировать и Acme, и
Acme Corporation безопасно.
Ещё два флага:
--hintвставляется в контекст модели буквально, поэтому описывайте сущность, не называя её («розничный клиент», а не «Globex — наш клиент»). Хинт, содержащий само значение, отвергается.--variant(можно повторять) — дополнительные написания: транслитерации, аббревиатуры.
Если у значения несколько слов, а режим ident, — зарегистрируйте отдельно и
отличительное слово: код обычно пишет GlobexClient для Globex Retail.
Слэш-команды в сессии
| Команда | Зачем |
|---|---|
/ctx-entity <значение> [тип] |
Защитить название компании, человека, хост или кодовое имя за стабильным алиасом |
/ctx-sanitize |
Превратить текст (тикет, лог, письмо) в версию с алиасами, которую можно куда-то вставить |
/ctx-audit |
Показать, что ctxguard блокировал и подменял |
/ctx-verify |
Доказать, что защита действительно блокирует заявленное, и измерить реальную утечку |
Промпт нельзя перезаписать хуком — только заблокировать. Поэтому если ваш промпт
отклонён, штатный путь — /ctx-sanitize на текст, ручная подстановка алиасов или
регистрация значения через /ctx-entity.
CLI
| Команда | Что делает |
|---|---|
ctxguard init [--force] |
создать каталог состояния и политику по умолчанию |
ctxguard status |
режим, префикс алиасов, число сущностей, правил, записей аудита |
ctxguard mode enforce|observe |
observe только логирует находки и ничего не блокирует — средство калибровки, не режим эксплуатации |
ctxguard entity add <значение> --type <тип> [--match …] [--hint …] [--variant …] |
защитить значение. Типы: company, person, host, project, email, phone, custom |
ctxguard entity list |
алиасы, типы, режимы совпадения, хинты — значения не печатаются никогда |
ctxguard entity remove <алиас> |
снять защиту |
ctxguard entity export --out <файл> |
выгрузить словарь для коллеги (откажется писать внутрь git-репозитория) |
ctxguard entity import <файл> |
влить словарь, выгруженный на другой машине |
ctxguard sanitize < <файл> |
stdin → текст с алиасами на stdout, сводка по правилам на stderr |
ctxguard scan [путь…] |
какие файлы содержат чувствительные значения (только счётчики и id правил) |
ctxguard scan-transcript [--limit N] [--verbose] [--all-projects] |
что реально дошло до модели |
ctxguard audit -n 40 |
последние решения: id правил, имена инструментов, алиасы — без плейнтекста |
ctxguard verify [--adversarial] |
канареечный корпус через настоящие точки входа хуков |
ctxguard run -- <команда> |
выполнить команду с фильтрацией вывода |
Ни одна подкоманда не печатает реальное значение сущности или секрета — именно это делает сам инструмент безопасным для запуска изнутри сессии агента.
Через Makefile
make test # юнит-тесты движка
make verify # канареечный корпус через настоящие точки входа хуков
make leaks # поиск в транскриптах значений, которые реально дошли до модели
make scan # чувствительные значения в этом репозитории (только счётчики)
make status # что у ctxguard активно
make lint # байт-компиляция всех скриптов + валидация JSON
make all # lint + test + verify
Что видит агент — и что делать, когда вызов отклонён
Пять правил, которые агент получает в контекст:
CTXG_*— алиасы реальных значений, использовать буквально.- Никогда не восстанавливать реальное значение и не просить пользователя его вставить.
- Маркер секрета необратим; записывать его в файл нельзя — это затрёт настоящий креденшл заглушкой, и хук такую запись отклонит. Нужно целиться в более узкий участок через Edit.
- Некоторые пути ведут в санитизированный кэш («twin»). Читать и править как обычно — изменение прописывается в настоящий файл за вас. Незнакомый путь — это ожидаемо и само по себе сигнал, что в файле что-то было.
- Отказ — это информация, а не препятствие. В причине названо правило и штатный путь; попытки обойти сами попадают в аудит.
| Отказ | Штатный путь |
|---|---|
Чтение .env, *.pem, ~/.ssh/… |
Читать .env.example или задокументированную схему. Проверить, что переменная задана, не печатая её: test -n "$VAR" && echo set |
| Команды, чья задача — напечатать креденшл (дамп окружения, выдача токена, чтение секрета кластера) | Проверять наличие, не печатая значение |
Grep с output_mode: content |
Bash + grep (этот путь фильтруется), либо Grep с files_with_matches, а затем Read |
| Поиск реального защищённого значения | Искать по алиасу — в санитизированных копиях лежат алиасы, а не реальные значения |
| Креденшл в командной строке | Передать через переменную окружения или файл, который команда читает сама |
cd / export / source с алиасом |
Они выполняются в постоянном шелле и не могут быть обёрнуты фильтром; перестроить команду или выполнить шаг вручную |
| Аргумент MCP-инструмента с защищённым значением | Использовать алиас; если инструменту нужно реальное значение — этот шаг выполняет пользователь |
Отдельно про изменение файлов: если правка не появилась в настоящем файле —
write-through упал и сообщил об этом (PostToolUse выходит с кодом 2 и печатает
write-through to <path> FAILED). Проверьте права на целевой файл и
ctxguard audit -n 20. Подготовленное содержимое цело в twin-копии.
Проверка, что защита работает
Два разных вопроса — две разные команды, и путать их нельзя:
ctxguard verify # работает ли механика? -> измеряет замысел
ctxguard scan-transcript # утекло ли что-то реально? -> измеряет результат
Доказательство — только второе: оно ищет реальные значения из словаря в
транскриптах, то есть буквально в том, что было отправлено модели. Ненулевой
результат означает, что данные дошли до модели несмотря на хуки — тогда сначала
смотрите файлы инструкций (CLAUDE.md / AGENTS.md грузит харнесс, их не видит ни
один хук), затем ctxguard audit.
По умолчанию сканируются транскрипты только этого проекта: сессии других
проектов оцениваются против словаря, который им не принадлежит. Расширить —
--all-projects.
Часть находок может быть помечена как структурные: значение входит в собственный путь проекта, поэтому появляется в каждом абсолютном пути и в метаданных транскрипта. Никаким хуком это не лечится — только переименованием каталога.
Передача словаря другой машине или коллеге
Плагин путешествует через маркетплейс. Словарь — нет, и не должен.
~/.claude/ctx-guard/ живёт на машине и намеренно вне любого репозитория. Передавать
его нужно явно, потому что алиасы невозможно воспроизвести:
- они выдаются в порядке регистрации, так что добавивший две компании в другом
порядке получит перевёрнутое соответствие — два человека будут понимать под
CTXG_COMPANY_Aразные компании; - соль генерируется на хранилище, поэтому маркеры секретов и PII-алиасы тоже не совпадут, и находки нельзя сопоставить между людьми.
На это есть тест, так что рекомендация не может тихо устареть.
ctxguard entity export --out ~/team-dict.json # откажется писать внутрь git-репозитория
# передавать по каналу, по которому вы отправили бы сами реальные значения — файл содержит их
ctxguard entity import team-dict.json # запускать из каталога проекта
# удалить файл выгрузки сразу после импорта
После импорта обе машины дают побайтово одинаковый вывод — включая маркеры и
PII-алиасы. Если в проекте уже есть словарь с другой солью, импорт откажется:
принять чужую соль (--adopt-salt) значит поменять все уже используемые PII-алиасы и
маркеры секретов.
Чего ctxguard не покрывает
CLAUDE.mdиAGENTS.mdгрузит сам харнесс — ни один хук их не видит. Санитизировать вручную;SessionStartпредупредит, если в них найдены реальные имена.- Структурные утечки: имя, входящее в путь проекта, защитить нельзя.
- Пока сущности не зарегистрированы или не импортированы, живы только детект креденшлов и PII — имена компаний, людей и хостов не защищены.
Подробнее — plugins/ctxguard/skills/context-sanitization/references/threat-model.md.
Прочитайте его прежде, чем обещать кому-либо, что данные в безопасности.
Необязательно, но полезно — чтобы ожидание пережило отключение плагина, добавьте в
свой CLAUDE.md:
Чувствительные данные в этом проекте заменены алиасами (`CTXG_*`), это обеспечивают
хуки ctxguard. Используй алиасы буквально, никогда не восстанавливай реальные
значения, а причину отказа читай вместо того, чтобы его обходить. См. скилл
context-sanitization.
Настройка и отладка
Политика: ~/.claude/ctx-guard/policy.json. Отсутствует или битая — применяются
строгие значения по умолчанию, потому что умолчания и есть безопасный откат.
Хранится как diff от умолчаний, чтобы исправления правил доходили до установленных
копий.
Ключевые поля: mode (enforce / observe), fail_closed (ошибка санитайзера
отклоняет вызов инструмента), alias_prefix, entropy_threshold, max_scan_bytes,
bash_wrap (выключение убирает фильтрацию вывода шелла целиком — не отключайте ради
скорости), deny_read_paths, deny_command_patterns, allowlist,
disabled_secret_rules. Полный список — references/policy.md.
Ложные срабатывания — в порядке предпочтения: точное значение в allowlist →
конкретный маркер в allowlist_substrings → сужение правила в detect.py с тестом →
id правила в disabled_secret_rules. Сначала посмотрите ctxguard audit: правило,
которое постоянно срабатывает на безобидном, — это причина, по которой защиту в итоге
выключают.
Стоимость: порядка 134 мс на решение по Bash и 155 мс на Read, который должен просканировать файл и материализовать twin. На задачу из 50 вызовов инструментов — примерно 6–7 секунд. Twin-копии кэшируются и инвалидируются по mtime+size, так что повторные чтения дешёвые.
Правка плагина не подействовала. Хуки исполняют установленную копию, а не
рабочее дерево, и plugin.json сравнивается по версии, а не по коммиту — релиз без
бампа версии не доедет до установленной копии молча. После каждого изменения:
claude plugin marketplace update ctx-tools
claude plugin update ctxguard@ctx-tools # нужен рестарт сессии
Убедиться:
diff -rq ~/.claude/plugins/cache/ctx-tools/ctxguard/<версия>/scripts plugins/ctxguard/scripts.
Выключить. /plugin uninstall ctxguard@ctx-tools. mode observe оставляет детекторы
и аудит, но ничего не блокирует — это калибровка, а не способ работать защищённо.
Состояние в ~/.claude/ctx-guard/ в обоих случаях остаётся; удалите этот каталог,
чтобы убрать и словарь.
Структура репозитория
.claude-plugin/marketplace.json манифест маркетплейса
plugins/ctxguard/ санитизация контекста: скилл + хуки + CLI
docs/specs/ проектный документ (исторический)
Makefile единственный вход в проверки
Куда положить новый скилл
| Скиллу нужны… | Куда |
|---|---|
| хуки, слэш-команды, MCP-серверы, скрипты | отдельный плагин в plugins/<имя>/ + запись в marketplace.json |
| ничего, кроме инструкций | плагин-«ведро»: plugins/<bucket>/skills/<имя>/SKILL.md |
Хуки не могут ехать внутри голого скилла — именно поэтому репозиторий сделан
маркетплейсом, а не плоским каталогом SKILL.md. Скиллы «только инструкции» стоит
собирать в один плагин-ведро: одна установка покрывает все, тогда как плагины,
ставящие хуки, добавляются осознанно и по одному. Такого ведра здесь пока нет —
создайте его вместе с первым таким скиллом, а не заранее.
Конвенции, вслед за экосистемой установленных плагинов:
- frontmatter в
SKILL.md—name+description, третье лицо, много триггеров - детали уходят в
references/*.md, не вSKILL.md - скрипты — Python 3 только stdlib, вызов через
python3 "${CLAUDE_PLUGIN_ROOT}/..."
Начать чтение стоит с plugins/ctxguard/skills/context-sanitization/SKILL.md.