tasks: владельца панели заводит отдельная команда, а не ключ конфига
- admin-owner-from-config закрыта: решение владельца 2026-08-15, ключ конфига не заводится, требование спеки storage остаётся в силе - dev-run-task вобрала cmd/devadmin и стоит первой строкой: инструмент и шаг порознь не нужны, поэтому едут одной задачей
This commit is contained in:
+1
-2
@@ -43,8 +43,7 @@
|
|||||||
|
|
||||||
## Очередь
|
## Очередь
|
||||||
|
|
||||||
- [✨ Заводить владельца панели по ключу конфига](items/admin-owner-from-config.md) — Владельца панели заводят по одноразовой ссылке из журнала при первом подъёме, и пароль после этого не хранится нигде: на каждой чистой базе шаг повторяется руками — и локально, где база пересоздаётся каждый прогон, и на выкладке с чистого листа.
|
- [🧹 Поднимать сервис и заглушку одной командой](items/dev-run-task.md) — Локальная проверка требует двух терминалов, ручной остановки обоих процессов и чистки каталога данных; учётную запись заводят проходом по браузеру, а владельца панели — по одноразовой ссылке из журнала.
|
||||||
- [🧹 Поднимать сервис и заглушку одной командой](items/dev-run-task.md) — Локальная проверка требует двух терминалов, ручной остановки обоих процессов и чистки каталога данных; учётную запись заводят проходом по браузеру.
|
|
||||||
- [✨ Сделать экран загрузки записи и её состояния](items/upload-and-status-screen.md) — Первое, ради чего приложение открывают: отдать файл и увидеть, что с ним происходит.
|
- [✨ Сделать экран загрузки записи и её состояния](items/upload-and-status-screen.md) — Первое, ради чего приложение открывают: отдать файл и увидеть, что с ним происходит.
|
||||||
- [✨ Сделать экран списка своих записей и чтения текста](items/records-list-screen.md) — Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем.
|
- [✨ Сделать экран списка своих записей и чтения текста](items/records-list-screen.md) — Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем.
|
||||||
- [✨ Дать владельцу править запись, возвращать её в работу и видеть её путь](items/audiorecord-actions.md) — С записью нельзя сделать ничего: заголовок ставит одна языковая модель, остановленную возвращает в работу только владелец сервиса в панели, а журнал событий пишется и не читается никем, кроме него же
|
- [✨ Дать владельцу править запись, возвращать её в работу и видеть её путь](items/audiorecord-actions.md) — С записью нельзя сделать ничего: заголовок ставит одна языковая модель, остановленную возвращает в работу только владелец сервиса в панели, а журнал событий пишется и не читается никем, кроме него же
|
||||||
|
|||||||
@@ -25,3 +25,4 @@
|
|||||||
- 2026-08-15 `rename-audiorecord` — ✨ Дать владельцу записи переименовать её. Причина: Слилась в audiorecord-actions 2026-08-15: правка заголовка живёт на экране одной записи вместе с возвратом в работу и журналом событий, и порознь каждое действие несло бы свою правку того же экрана. Была секция: Очередь.
|
- 2026-08-15 `rename-audiorecord` — ✨ Дать владельцу записи переименовать её. Причина: Слилась в audiorecord-actions 2026-08-15: правка заголовка живёт на экране одной записи вместе с возвратом в работу и журналом событий, и порознь каждое действие несло бы свою правку того же экрана. Была секция: Очередь.
|
||||||
- 2026-08-15 `resume-halted-record` — ✨ Вернуть остановленную запись в работу из приложения. Причина: Слилась в audiorecord-actions 2026-08-15: возврат в работу живёт на экране одной записи вместе с правкой заголовка и журналом событий. Была секция: Очередь.
|
- 2026-08-15 `resume-halted-record` — ✨ Вернуть остановленную запись в работу из приложения. Причина: Слилась в audiorecord-actions 2026-08-15: возврат в работу живёт на экране одной записи вместе с правкой заголовка и журналом событий. Была секция: Очередь.
|
||||||
- 2026-08-15 `record-events-screen` — ✨ Показывать журнал событий записи её владельцу. Причина: Слилась в audiorecord-actions 2026-08-15: журнал событий читается с экрана одной записи вместе с правкой заголовка и возвратом в работу. Была секция: Очередь.
|
- 2026-08-15 `record-events-screen` — ✨ Показывать журнал событий записи её владельцу. Причина: Слилась в audiorecord-actions 2026-08-15: журнал событий читается с экрана одной записи вместе с правкой заголовка и возвратом в работу. Была секция: Очередь.
|
||||||
|
- 2026-08-15 `admin-owner-from-config` — ✨ Заводить владельца панели по ключу конфига. Причина: Решение владельца 2026-08-15: владелец панели нужен только для локальной проверки, и заводит его отдельная команда cmd/devadmin — инструмент разработчика рядом с cmd/oidcstub. Ключ конфига не заводится, требование спеки storage «Пароль владельца от панели не лежит в конфигурации» остаётся в силе. Проверено прогоном: команда открывает базу тем же pbrepo.New, накатывает схему целиком, пароль существующего владельца не переписывает, на настройки провайдера OIDC не влияет — их сервис применяет из конфига при каждом подъёме. Была секция: Очередь.
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
# ✨ Заводить владельца панели по ключу конфига
|
|
||||||
|
|
||||||
- **Тип:** 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`.
|
|
||||||
Инвариант «Секрет не покидает конфиг» распространяется на этот пароль целиком.
|
|
||||||
Пустое значение обязано значить «никого не заводить»: иначе выкладка без ключа
|
|
||||||
перестанет подниматься. Переписывать пароль существующего владельца нельзя —
|
|
||||||
человек мог сменить его в панели.
|
|
||||||
+26
-14
@@ -1,13 +1,18 @@
|
|||||||
# 🧹 Поднимать сервис и заглушку одной командой
|
# 🧹 Поднимать сервис и заглушку одной командой
|
||||||
|
|
||||||
- **Тип:** chore
|
- **Тип:** chore
|
||||||
- **Категория:** Очередь — шаг dev заводит владельца панели тем самым ключом — до него команду не собрать
|
- **Категория:** Очередь — оснастка локального прогона: без неё каждую проверку начинают с двух терминалов и ручной уборки
|
||||||
- **Зачем:** Локальная проверка требует двух терминалов, ручной остановки обоих процессов и чистки каталога данных; учётную запись заводят проходом по браузеру.
|
- **Зачем:** Локальная проверка требует двух терминалов, ручной остановки обоих процессов и чистки каталога данных; учётную запись заводят проходом по браузеру, а владельца панели — по одноразовой ссылке из журнала.
|
||||||
|
|
||||||
Шаг `task dev CONFIG=<путь> USER=<почта>` поднимает сервис и подставной
|
Шаг `task dev CONFIG=<путь> USER=<почта>` поднимает сервис и подставной
|
||||||
провайдер OIDC разом, кладёт данные во временный каталог, заводит учётные записи
|
провайдер OIDC разом, кладёт данные во временный каталог, заводит учётные записи
|
||||||
и гасит оба процесса по Ctrl+C.
|
и гасит оба процесса по Ctrl+C.
|
||||||
|
|
||||||
|
Работа состоит из двух частей, и они едут одним коммитом: инструмент
|
||||||
|
`cmd/devadmin` и шаг `dev`, который его зовёт. Порознь они не нужны — шаг без
|
||||||
|
инструмента не заведёт владельца панели, инструмент без шага останется вызовом,
|
||||||
|
который никто не делает.
|
||||||
|
|
||||||
Задуманное устройство:
|
Задуманное устройство:
|
||||||
|
|
||||||
- конфиг пишет человек и передаёт путём — шаг его не сочиняет;
|
- конфиг пишет человек и передаёт путём — шаг его не сочиняет;
|
||||||
@@ -15,20 +20,24 @@
|
|||||||
каталог со своим `data_dir`, чтобы прогон начинался с пустой базы независимо от
|
каталог со своим `data_dir`, чтобы прогон начинался с пустой базы независимо от
|
||||||
того, что написано в конфиге. Временное живёт в `/tmp` — в `data/` писать
|
того, что написано в конфиге. Временное живёт в `/tmp` — в `data/` писать
|
||||||
запрещено (CLAUDE.md, «Запреты»);
|
запрещено (CLAUDE.md, «Запреты»);
|
||||||
- оба процесса идут одной группой, `trap` на `INT` и `TERM` гасит их разом,
|
- владельца панели заводит `cmd/devadmin` — отдельный main-пакет рядом с
|
||||||
`wait` держит терминал;
|
`cmd/oidcstub`, в образ он не едет. Базу открывает тем же `pbrepo.New`, что и
|
||||||
|
сервис, и потому получает её со схемой целиком; зовётся до подъёма сервиса, на
|
||||||
|
пустом каталоге. Пароль существующего владельца не переписывает;
|
||||||
- шаг заводит обычную учётную запись входом: поднимает `cmd/oidcstub` с
|
- шаг заводит обычную учётную запись входом: поднимает `cmd/oidcstub` с
|
||||||
`-email` из `USER`, сам проходит цепочку входа и тем создаёт запись;
|
`-email` из `USER`, сам проходит цепочку входа и тем создаёт запись;
|
||||||
- владельца панели заводит **ключ конфига**, который приносит
|
- оба процесса идут одной группой, `trap` на `INT` и `TERM` гасит их разом,
|
||||||
[admin-owner-from-config](admin-owner-from-config.md). До неё шаг владельца
|
`wait` держит терминал.
|
||||||
панели завести не может — отсюда и место в списке.
|
|
||||||
|
|
||||||
## Затрагивает
|
## Затрагивает
|
||||||
|
|
||||||
|
- `cmd/devadmin` — новый main-пакет: заводит владельца панели в базе;
|
||||||
- `Taskfile.yml` — шаг `dev` с переменными `CONFIG` и `USER`;
|
- `Taskfile.yml` — шаг `dev` с переменными `CONFIG` и `USER`;
|
||||||
- скрипт запуска в `scripts/` — он попадает под шаг `shell` гейта;
|
- скрипт запуска в `scripts/` — он попадает под шаг `shell` гейта;
|
||||||
- `CLAUDE.md`, раздел «Команды»;
|
- `CLAUDE.md`, раздел «Команды»;
|
||||||
- `README.md`, раздел про локальный запуск.
|
- `README.md`, раздел про локальный запуск;
|
||||||
|
- `Dockerfile` — строка сборки называет точку входа поимённо, и новый пакет в
|
||||||
|
образ попасть не должен.
|
||||||
|
|
||||||
## Критерии приёмки
|
## Критерии приёмки
|
||||||
|
|
||||||
@@ -37,15 +46,18 @@
|
|||||||
порты.
|
порты.
|
||||||
- Ctrl+C гасит обоих и не оставляет висящих процессов. Оракул: `pgrep` по обоим
|
- Ctrl+C гасит обоих и не оставляет висящих процессов. Оракул: `pgrep` по обоим
|
||||||
именам после остановки — пусто.
|
именам после остановки — пусто.
|
||||||
- После запуска в базе есть учётная запись с переданной почтой. Оракул: запрос к
|
- После запуска в базе есть учётная запись с переданной почтой, а владелец панели
|
||||||
коллекции пользователей во временной базе.
|
входит своим паролем. Оракул: запрос к коллекции пользователей во временной базе
|
||||||
|
и вход по `auth-with-password` в коллекцию владельцев.
|
||||||
- Каталог данных лежит во временном месте, а `data/` не тронут. Оракул: путь базы
|
- Каталог данных лежит во временном месте, а `data/` не тронут. Оракул: путь базы
|
||||||
из журнала подъёма и `git status` после прогона.
|
из журнала подъёма и `git status` после прогона.
|
||||||
- `task gate` остаётся зелёным, скрипт проходит `shellcheck`. Оракул: прогон
|
- `task gate` остаётся зелёным, скрипт проходит `shellcheck`, а образ не получает
|
||||||
гейта целиком.
|
нового пакета. Оракул: прогон гейта целиком и `task image`.
|
||||||
|
|
||||||
## Рамки
|
## Рамки
|
||||||
|
|
||||||
Шаг локальный: наружу не ходит, боевых ключей не требует и в образ не попадает.
|
Шаг локальный: наружу не ходит, боевых ключей не требует и в образ не попадает.
|
||||||
Распознавание при выдуманных ключах не работает — это остаётся как есть, шаг его
|
Пароль владельца в конфиг **не заводится** — требование спеки `storage` «Пароль
|
||||||
не подменяет.
|
владельца от панели не лежит в конфигурации» остаётся в силе, и работа его не
|
||||||
|
трогает. Распознавание при выдуманных ключах не работает — это остаётся как есть,
|
||||||
|
шаг его не подменяет.
|
||||||
|
|||||||
Reference in New Issue
Block a user