fix(permissions): silence non-destructive ask prompts for autonomous ops #54
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Контекст
Пользователь — соло-разработчик. Даёт агенту задачу и уходит спать. Ночные автономные запуски зависают на
askпромптах вopencode.json(блокpermission, строки 161-408 файла.opencode/opencode.json). Всего 55 записейaskв блокеbash+ отсутствие catch-all в блокеread→ дефолтное поведение opencode (запрос разрешения на чтение любого файла не из явных 9 шаблонов).Конкретные жалобы пользователя:
src/examples/foo.rs,assets/example.png) — блокreadне имеет catch-all"*": "allow", поэтому неописанные пути попадают в дефолтask..env.exampleужеallow(строка 170), но другие "example" файлы — нет.git tag -d,git push --delete) — нужно для release skill.Задача
Уменьшить количество
askпромптов в.opencode/opencode.jsonтак, чтобы ночные автономные запуски не зависали. Изменить 30 записейask→allow, 10 force push записейask→allow, добавить catch-all"*": "allow"в начало блокаread. Оставитьaskтолько для 22 SSH опасных команд (rm/reboot/shutdown/apt/systemctl stop на удалённых хостах) — оркестратор/general их сам не вызывает, ночью не нужны.Контракты
Файл:
/root/workspace/opencode-config/.opencode/opencode.json, блокpermission(строки 161-408).Изменение 1 — блок
read(строки 165-175): добавить catch-all ПЕРВЫМТекущий блок
read(9 записей, без catch-all):Должно стать (добавить
"*": "allow"ПЕРВЫМ ключом — КРИТИЧНО из-за findLast, иначе*переопределит deny для .env/ключей):Это убирает промпты на чтение всех несекретных файлов (включая "example" файлы кроме
.env.example, который уже allow).Изменение 2 — git tag/branch delete: 8 записей
ask→allowСтроки и точные ключи:
"git -C * branch -d *"(строка 222)"git -C * push --delete *"(строка 223)"git -C * push origin --delete *"(строка 224)"git branch -d *"(строка 296)"git push --delete *"(строка 297)"git push origin --delete *"(строка 298)"git tag -d *"(строка 392)"git -C * tag -d *"(строка 398)Все →
"allow".Изменение 3 — force push: 10 записей
ask→allowСтроки и точные ключи:
"git push --force*"(387)"git push -f*"(388)"git push * --force*"(389)"git push * -f*"(390)"git push * :*"(391)"git -C * push --force*"(393)"git -C * push -f*"(394)"git -C * push * --force*"(395)"git -C * push * -f*"(396)"git -C * push * :*"(397)Все →
"allow". Обоснование: соло-разраб, чужой работы нет, branch protection на Forgejo ловит main на серверной стороне.Изменение 4 — docker: 7 записей
ask→allow"docker restart *"(299)"docker stop *"(300)"docker rm *"(301)"docker exec * sh *"(305)"docker exec * bash *"(306)"docker system prune*"(307)"docker volume rm *"(308)Все →
"allow". Оставитьdenyдляdocker exec * rm *(303) иdocker exec * rmdir *(304) — они уже deny.Изменение 5 — прочее: 5 записей
ask→allow"del *"(315)"Remove-Item *"(316)"vastai *"(319) — ноvastai show*(320) иvastai search*(321) уже allow, остаются как есть"kill *"(322)"nohup *"(323)Все →
"allow".ИТОГО изменяемых записей: 1 (read catch-all) + 8 + 10 + 7 + 5 = 31 запись.
Инварианты
denyзаписи остаются БЕЗ ИЗМЕНЕНИЙ:.env*,id_ed25519,id_rsa,*.pem,*secrets*,git reset,git clean,git clone,clone.sh,*-status.pydeny-правила,curl -X DELETE *FORGEJO_URL*,docker exec * rm *,docker exec * rmdir *,ssh * mkfs*,ssh * dd *,ssh * fdisk*,cat .env*,*cat */.env*,cat *id_ed25519*,*cat *ssh/id_ed25519*,doom_loop.ask:ssh * cat *.env*,ssh * cat .env*,ssh * rm *,ssh * rmdir *,ssh * docker restart*,ssh * docker stop*,ssh * docker rm*,ssh * docker rmi*,ssh * docker update*,ssh * systemctl restart*,ssh * systemctl stop*,ssh * apt *,ssh * yum *,ssh * mv *,ssh * chmod *,ssh * chown *,ssh * reboot*,ssh * shutdown*,ssh * powershell -Command Set-*,ssh * powershell -Command New-*,ssh * powershell -Command Remove-*,ssh * powershell -Command Restart-*,ssh * powershell -Command Stop-*.readблоке: catch-all"*": "allow"ДОЛЖЕН быть первым (findLast — последнее совпадение побеждает, конкретные deny должны идти после*чтобы побеждать для секретов).permission(edit,external_directory,doom_loop) не трогать..opencode/agents/reviewer.md,.opencode/agents/memory-syncer.md) не трогать — у них свой permission блок с"*": deny.Граничные случаи
read: если"*": "allow"поставить в конец блокаread, то для.envсовпадут**/.env*(deny) и*(allow), findLast выберет*(последнее) →.envстанет allow = ДЫРА. Поэтому*обязан быть ПЕРВЫМ. Конкретные deny для.env/ключей идут после и побеждают для своих паттернов.**/.env.exampleуже allow (строка 170) — catch-all*не меняет поведение (оба allow), но порядок:*первый,**/.env.exampleallow после — оба allow, конфликта нет.git push * :*(391) — устаревший синтаксис удаления ref. Перевод в allow разрешаетgit push origin refs/heads/foo:refs/heads/bar(законное переименование ref). Это безопасно для соло.vastai *(319) → allow:vastai show*(320) иvastai search*(321) уже allow, остаются. findLast: конкретные правила позже побеждают, но они оба allow — конфликта нет.bash(sh */clone.sh*": "deny") не должно быть trailing comma. После добавления"*": "allow"в началоread— запятая после него обязательна.Влияние на связанные компоненты
.opencode/skills/release/SKILL.md) — используетgit tag -dиgit push --deleteдля пересоздания тегов. После изменения ониallow→ release pipeline не зависнет.allow."*": deny, на них изменения не влияют.configure-opencode/SKILL.md:124) — может срабатывать на опасные правила. Проверить, не начнёт ли он ругаться на force push allow / docker rm allow. Если да — возможно нужно обновить линтер, но это вне scope (см. ниже).Вне scope
check-permissions.py(если он ругается — отдельный issue).edit/external_directory/doom_loop.bash(например"*": "allow") — это было бы опасно.webfetch(его нет в конфиге).~/.config/opencode/opencode.json— это bind-mount к workspace clone, изменения применяются автоматически.Критерии приемки
readдобавлена запись"*": "allow"ПЕРВОЙ по порядку (до*.envdeny).ask→allow.ask→allow.ask→allow.ask→allow.ask(не изменены).denyзаписи (секреты, git reset/clean/clone, status.py, ssh mkfs/dd/fdisk, cat .env*/ключи, docker exec rm/rmdir, curl DELETE FORGEJO, clone.sh) осталисьdeny..opencode/opencode.jsonостался валидным JSON (проверить черезpython -c "import json; json.load(open('.opencode/opencode.json'))").grep -c '"ask"' .opencode/opencode.jsonдолжен вернуть 22 (только SSH записи)..envкоторый deny).