From 97c6c7c440b4c0bef1a2daedd706d5a2b2525b3b Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sat, 15 Aug 2026 20:39:45 +0300 Subject: [PATCH] =?UTF-8?q?tasks:=20=D0=B7=D0=B0=D0=B2=D0=B5=D0=B4=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D0=B8=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=20=D0=B2=D0=BB=D0=B0=D0=B4=D0=B5=D0=BB=D1=8C=D1=86?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=B0=D0=BD=D0=B5=D0=BB=D0=B8=20=D0=B8=D0=B7=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D0=B0=20=D0=B8=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D1=83=20=D0=BB=D0=BE=D0=BA=D0=B0?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B7=D0=B0=D0=BF=D1=83?= =?UTF-8?q?=D1=81=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - admin-owner-from-config первой строкой: сервис заводит владельца панели по ключу конфига; задача отменяет требование спеки storage, и это названо в рамках - dev-run-task следом: task dev поднимает сервис и заглушку разом, гасит оба по Ctrl+C; зависит от ключа конфига --- tasks/BACKLOG.md | 2 + tasks/items/admin-owner-from-config.md | 59 ++++++++++++++++++++++++++ tasks/items/dev-run-task.md | 51 ++++++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 tasks/items/admin-owner-from-config.md create mode 100644 tasks/items/dev-run-task.md diff --git a/tasks/BACKLOG.md b/tasks/BACKLOG.md index 7df3962..35fdcc0 100644 --- a/tasks/BACKLOG.md +++ b/tasks/BACKLOG.md @@ -43,6 +43,8 @@ ## Очередь +- [✨ Заводить владельца панели по ключу конфига](items/admin-owner-from-config.md) — Владельца панели заводят по одноразовой ссылке из журнала при первом подъёме, и пароль после этого не хранится нигде: на каждой чистой базе шаг повторяется руками — и локально, где база пересоздаётся каждый прогон, и на выкладке с чистого листа. +- [🧹 Поднимать сервис и заглушку одной командой](items/dev-run-task.md) — Локальная проверка требует двух терминалов, ручной остановки обоих процессов и чистки каталога данных; учётную запись заводят проходом по браузеру. - [✨ Сделать экран загрузки записи и её состояния](items/upload-and-status-screen.md) — Первое, ради чего приложение открывают: отдать файл и увидеть, что с ним происходит. - [✨ Сделать экран списка своих записей и чтения текста](items/records-list-screen.md) — Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем. - [✨ Дать владельцу править запись, возвращать её в работу и видеть её путь](items/audiorecord-actions.md) — С записью нельзя сделать ничего: заголовок ставит одна языковая модель, остановленную возвращает в работу только владелец сервиса в панели, а журнал событий пишется и не читается никем, кроме него же diff --git a/tasks/items/admin-owner-from-config.md b/tasks/items/admin-owner-from-config.md new file mode 100644 index 0000000..50f8f07 --- /dev/null +++ b/tasks/items/admin-owner-from-config.md @@ -0,0 +1,59 @@ +# ✨ Заводить владельца панели по ключу конфига + +- **Тип:** feature +- **Категория:** Очередь — решение владельца 2026-08-15: ключ конфига нужен и локальному прогону, и выкладке +- **Зачем:** Владельца панели заводят по одноразовой ссылке из журнала при первом подъёме, и пароль после этого не хранится нигде: на каждой чистой базе шаг повторяется руками — и локально, где база пересоздаётся каждый прогон, и на выкладке с чистого листа. + +Сервис на подъёме заводит владельца панели администратора по ключу конфига, +когда такой учётной записи ещё нет. + +Сторонней командой этого сегодня не сделать: точка входа разбирает свои флаги и +сразу грузит конфиг, поэтому команды хранилища до неё не доходят — проверено +2026-08-15, `go run ./cmd/transcriber superuser upsert …` падает на разборе +конфига. Отсюда и ключ: другого места, откуда взять пароль, у сервиса нет. + +## Затрагивает + +- секция конфига и её проверка на старте — новый ключ, `internal/config`; +- приведение настроек хранилища при подъёме — + `internal/adapter/repo/pocketbase/provider.go`, там же, где применяются + настройки провайдера OIDC; +- `config.example.toml` — образец ключа; +- `docs/conventions/config.md` — конвенция конфига; +- `docs/security.md` — пароль владельца панели становится содержимым конфига, а + вход в панель заводится сам; +- `openspec/specs/storage/spec.md`, требование «Пароль владельца от панели не + лежит в конфигурации» — задача его **отменяет**. Там же сценарий «Владелец + пароля ещё не задан» с проверкой «ни один ключ конфигурации не несёт пароля от + панели». + +## Критерии приёмки + +- Подъём на пустой базе с заполненным ключом заводит владельца, и он входит в + панель этим паролем. Оракул: подъём на временном каталоге данных, затем вход по + `/_/` — и запрос к собственному адресу хранилища с полученной сессией. +- Подъём с пустым ключом никого не заводит и старт не роняет. Оракул: подъём на + пустой базе с пустым значением — сервис слушает порт, в коллекции владельцев + записей нет. +- Повторный подъём с уже заведённым владельцем пароль не переписывает. Оракул: + сменить пароль в панели, поднять сервис заново, войти новым паролем — вход + проходит. +- Пароль не встречается ни в журнале, ни в тексте ошибки. Оракул: поиск значения + по выводу подъёма, включая подъём с негодным значением ключа. + +## Рамки + +Задача идёт **против записанного требования**, и это её главное условие. Спека +`storage` сегодня запрещает ровно то, что здесь заводится, и довод у неё свой: +пароль от панели открывает все записи и все файлы разом, а секрет, лежащий в +конфиге, уезжает вместе с ней в выкладку. Отменять требование — решение владельца +от 2026-08-15; принято оно с открытыми глазами, потому что своей команды завести +владельца у сервиса нет. Работа обязана снять требование дельта-спекой, а не +обойти его молча. + +Имя ключа конфига проект числит **необратимым** (CLAUDE.md, «Работа»): раз +заведённый, он поедет в выкладку и в роль Ansible из `pet-project-server`. +Инвариант «Секрет не покидает конфиг» распространяется на этот пароль целиком. +Пустое значение обязано значить «никого не заводить»: иначе выкладка без ключа +перестанет подниматься. Переписывать пароль существующего владельца нельзя — +человек мог сменить его в панели. diff --git a/tasks/items/dev-run-task.md b/tasks/items/dev-run-task.md new file mode 100644 index 0000000..57cb470 --- /dev/null +++ b/tasks/items/dev-run-task.md @@ -0,0 +1,51 @@ +# 🧹 Поднимать сервис и заглушку одной командой + +- **Тип:** chore +- **Категория:** Очередь — шаг dev заводит владельца панели тем самым ключом — до него команду не собрать +- **Зачем:** Локальная проверка требует двух терминалов, ручной остановки обоих процессов и чистки каталога данных; учётную запись заводят проходом по браузеру. + +Шаг `task dev CONFIG=<путь> USER=<почта>` поднимает сервис и подставной +провайдер OIDC разом, кладёт данные во временный каталог, заводит учётные записи +и гасит оба процесса по Ctrl+C. + +Задуманное устройство: + +- конфиг пишет человек и передаёт путём — шаг его не сочиняет; +- шаг подменяет каталог данных: кладёт копию переданного конфига во временный + каталог со своим `data_dir`, чтобы прогон начинался с пустой базы независимо от + того, что написано в конфиге. Временное живёт в `/tmp` — в `data/` писать + запрещено (CLAUDE.md, «Запреты»); +- оба процесса идут одной группой, `trap` на `INT` и `TERM` гасит их разом, + `wait` держит терминал; +- шаг заводит обычную учётную запись входом: поднимает `cmd/oidcstub` с + `-email` из `USER`, сам проходит цепочку входа и тем создаёт запись; +- владельца панели заводит **ключ конфига**, который приносит + [admin-owner-from-config](admin-owner-from-config.md). До неё шаг владельца + панели завести не может — отсюда и место в списке. + +## Затрагивает + +- `Taskfile.yml` — шаг `dev` с переменными `CONFIG` и `USER`; +- скрипт запуска в `scripts/` — он попадает под шаг `shell` гейта; +- `CLAUDE.md`, раздел «Команды»; +- `README.md`, раздел про локальный запуск. + +## Критерии приёмки + +- Команда поднимает оба процесса и печатает адрес, по которому открывать + приложение. Оракул: прогон с тестовым конфигом — сервис и заглушка слушают свои + порты. +- Ctrl+C гасит обоих и не оставляет висящих процессов. Оракул: `pgrep` по обоим + именам после остановки — пусто. +- После запуска в базе есть учётная запись с переданной почтой. Оракул: запрос к + коллекции пользователей во временной базе. +- Каталог данных лежит во временном месте, а `data/` не тронут. Оракул: путь базы + из журнала подъёма и `git status` после прогона. +- `task gate` остаётся зелёным, скрипт проходит `shellcheck`. Оракул: прогон + гейта целиком. + +## Рамки + +Шаг локальный: наружу не ходит, боевых ключей не требует и в образ не попадает. +Распознавание при выдуманных ключах не работает — это остаётся как есть, шаг его +не подменяет.