Дозволи важливіші за промпт: fail-closed контур OpenClaw
Промпт пояснює агенту, що бажано зробити. Дозвіл визначає, що він технічно може зробити. Це різні рівні контролю: інструкція може бути проігнорована або неправильно витлумачена, а заборона, яку перевіряє runtime, має зупиняти дію незалежно від тексту відповіді.
Для OpenClaw це особливо важливо, коли один Telegram-бот одночасно читає vault, запускає команди, змінює файли й надсилає зовнішні повідомлення. Надійна схема починається з ролей і меж, а не з довшого system prompt.
Що саме контролюють Claude, Codex і OpenCode
У Claude Code правила allow, ask і deny застосовуються до інструментів, а порядок має значення: спочатку перевіряється deny, потім ask, потім allow. Важливе обмеження: правила дозволів Claude Code enforced runtime-ом, а не моделлю. Текст у CLAUDE.md може спрямувати модель, але не надати їй доступ. Окремий sandbox Claude Code обмежує shell-команди, однак file tools, MCP-сервери й hooks працюють поза ним. Отже, «shell у sandbox» не означає «весь агент ізольований».
У Codex sandbox і approval policy — дві різні осі. Документація Codex про sandbox описує технічну межу: які файли й мережеві ресурси доступні. Approval policy визначає, коли агент мусить зупинитися й попросити підтвердження. Практичний локальний режим — workspace-write + on-request; danger-full-access прибирає filesystem- і network-межі, тому це вже окремий рівень довіри, а не просто «швидший режим».
У OpenCode кожна дія має результат allow, ask або deny, а granular rules матчать конкретний інструмент і його аргументи. Правила OpenCode використовують принцип «останнє співпадіння перемагає», тому catch-all ставлять першим, а вузькі винятки — після нього. За замовчуванням OpenCode досить permissive: для unattended-запуску це зручно, але небезпечно як базова політика.
Спільний висновок: автономність треба видавати конкретній ролі, конкретній дії й конкретному шляху. Не «агенту взагалі».
Мінімальна модель ролей для OpenClaw
Почніть із матриці, яку можна перевірити до запуску:
| Роль | Vault | Команди | Зовнішня відправка | Призначення |
|---|---|---|---|---|
router |
читати дозволені індекси | ні | ні | розібрати Telegram-запит і вибрати маршрут |
researcher |
читати джерела, писати staging | лише read-only | ні | зібрати матеріал і чернетку |
executor |
читати й змінювати staging | allowlist | ні | виконати конкретну операцію після маршрутизації |
publisher |
читати затверджене | тільки перевірки | лише визначений канал | опублікувати й повернути receipt |
Роль router не повинна мати «про всяк випадок» shell або write-доступ. Якщо запит вимагає змінити файл, router створює typed task envelope для executor, а не виконує дію сам:
task: publish_article
source: staging/article.md
checks:
- validate_frontmatter
- validate_links
- build_site
approval: required
side_effects:
- git_commit
- telegram_receipt
Це не чарівний формат OpenClaw, а контракт між ролями. Його сенс у тому, що worker отримує обмежене завдання і може відмовити, якщо поле side_effects або шлях не входить до allowlist.
Чотири робочі схеми
1. Telegram як control plane, а не універсальний shell
Зробіть Telegram-вхід тонким: бот приймає команду, нормалізує її, показує план і запускає окрему сесію worker-а. Worker повертає короткий receipt: task_id, змінені ресурси, перевірки й помилки. Сам router не отримує довільного exec, доступу до секретів або права надсилати повідомлення назовні.
Плюс — компрометація або prompt injection у вхідному повідомленні не дає одразу повного доступу. Мінус — зʼявляється стан задачі: потрібні idempotency key, timeout і спосіб відновити незавершену роботу. Для щоденних публікацій це краще за одну довгу сесію, яка має всі повноваження.
2. Vault із двома зонами запису
Розділіть сховище на source, staging і published. Більшість агентів отримує read-доступ до source, writer — write лише до staging, а publisher — read до staging і write тільки до контрольованого каталогу публікації. Не давайте досліднику права перезаписувати оригінальні нотатки: це зменшує ціну помилки й спрощує аудит diff-ом.
Псевдополітика може виглядати так:
permissions:
source/**:
read: allow
write: deny
staging/**:
read: allow
write: ask
published/**:
read: allow
write: deny
roles:
publisher:
write:
- published/approved/**
Нестандартний, але корисний варіант — write lease: writer отримує право запису на один файл на короткий час і втрачає його після receipt або timeout. Це зменшує довгоживучі дозволи, але потребує блокувань, прибирання прострочених lease і зрозумілого recovery-процесу.
3. Shell через вузькі capabilities
Не описуйте в інструкції «запускай безпечні команди». Винесіть безпечний набір у runtime-політику: git diff, валідатор, тестовий runner. Усе інше — ask або deny. Особливо окремо обмежуйте мережу, доступ до сокетів, package manager і команди, що можуть читати environment.
executor:
shell:
allow:
- "git diff --check"
- "make test"
- "./scripts/validate"
ask:
- "git commit *"
- "npm install *"
deny:
- "git push *"
- "curl * | *"
- "cat .env*"
Називайте це capability policy, а не security prompt. Якщо конкретний runtime не вміє патерни команд, обгорніть операції у короткі скрипти з фіксованими аргументами; це зменшує поверхню матчинг-помилок.
4. Окремий агент для небезпечної дії
Для публікації, видалення, відправлення пошти або змін у production використовуйте окрему роль із вузьким toolset. Вона отримує не всю історію Telegram, а структурований пакет: що змінюється, чому, які перевірки пройдено, який scope і яка дедлайн-умова. Після дії повертає receipt, а не довільний текст.
Це відрізняється від звичайного «попросімо другий агент перевірити» — тут межа потрібна саме для виконання, а не лише для ревʼю. Ціна — більше orchestration-коду й затримка на передачу пакета. Вигода — чіткий blast radius і можливість відтворити рішення без повного чату.
Операційний playbook
Перед підключенням нового tool або MCP-сервера перевірте пʼять речей:
- Яка мінімальна роль його потребує: router, researcher, executor чи publisher?
- Які аргументи повинні бути
allow, а які завждиaskабоdeny? - Чи може tool читати більше, ніж здається з його назви: environment, усі файли, мережу, локальні сокети?
- Який fail-closed результат при timeout, невідомому аргументі або відсутньому approval?
- Який receipt доведе, що дія справді виконана, а не лише обіцяна моделлю?
Тестуйте не тільки позитивний сценарій. Зробіть по одному негативному тесту на роль: router просить shell, researcher намагається писати в source, executor отримує шлях поза staging, publisher отримує неперевірений пакет. Очікуваний результат — блокування з поясненням, а не «модель пообіцяла бути обережною».
Що налаштувати сьогодні
- Випишіть ролі й заборони в одну матрицю; починайте з
deny, потім додавайте вузькіallow. - Приберіть write і зовнішню відправку з Telegram-router-а.
- Введіть staging-зону для vault і receipt для кожної мутації.
- Залиште ручне підтвердження для commit, публікації, видалення та зовнішніх повідомлень.
- Раз на тиждень перевіряйте фактичні tool calls проти матриці, бо конфігурація з часом розширюється тихими винятками.
Найкращий агент — не той, якому можна довірити все, а той, чию помилку можна локалізувати. Промпт допомагає обрати правильну дію; дозволи визначають, наскільки дорогою буде неправильна.