From faa1d7c6996998447d280f77d2013814548b343f Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Tue, 11 Aug 2026 10:54:43 +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=D0=B0=20=D1=86=D0=B5=D0=BB=D1=8C=20=D0=BF=D1=80=D0=BE=20?= =?UTF-8?q?=D0=B8=D0=B7=D1=8A=D1=8F=D1=82=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B8=20=D0=B8=D0=B7=20=D0=B0=D1=80=D1=85=D0=B8?= =?UTF-8?q?=D0=B2=D0=B0=20=D0=B8=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D0=B0=20?= =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - data-ownership — обратное право к бессрочному хранению: человек убирает свою запись вместе с файлом, объектом в Object Storage и всеми уровнями текста; - delete-record закрывает все пять пунктов цели: подтверждение, необратимость, чужую запись не тронуть, повторная загрузка того же файла заводит новую задачу, а строки потребления остаются — деньги потрачены; - docs/security.md: пункт «Удаление данных по требованию» перестал говорить, что задачи под это нет. --- docs/security.md | 9 ++++--- tasks/BACKLOG.md | 1 + tasks/ROADMAP.md | 1 + tasks/items/data-ownership.md | 26 ++++++++++++++++++ tasks/items/delete-record.md | 51 +++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 tasks/items/data-ownership.md create mode 100644 tasks/items/delete-record.md diff --git a/docs/security.md b/docs/security.md index f6dfd7e..85f10c0 100644 --- a/docs/security.md +++ b/docs/security.md @@ -187,6 +187,9 @@ Telegram отправителю. - **Стойкость `ffmpeg` к вредоносному входу.** Разбор чужого формата отдан внешней программе, своей песочницы вокруг неё нет. - **Удаление данных по требованию.** Ни файлы, ни расшифровки не удаляются - вовсе, и удаление по требованию в задачах не стоит. С 2026-08-11 это уже не - недосмотр, а следствие решения хранить бессрочно — но требование «убери мою - запись» им не закрыто, и задачи под него нет. + вовсе. С 2026-08-11 это уже не недосмотр, а следствие решения хранить + бессрочно, и тем же днём заведена задача `delete-record`: своя запись + убирается вместе с файлом, объектом в Object Storage и всеми уровнями текста. + Пока она не сделана, единственный способ убрать запись — руками в базе и в + каталоге на сервере. Учёт расхода удалению не подлежит по решению человека: + деньги потрачены, а строки потребления текста не содержат. diff --git a/tasks/BACKLOG.md b/tasks/BACKLOG.md index 0bc2205..74b8675 100644 --- a/tasks/BACKLOG.md +++ b/tasks/BACKLOG.md @@ -35,6 +35,7 @@ - [✨ Собрать каркас приложения и раздать его из бинарника](items/spa-skeleton.md) — Экранов нет и собирать их нечем: ни сборки фронтенда, ни раздачи статики в проекте не существует. - [✨ Сделать экран загрузки записи и её состояния](items/upload-and-status-screen.md) — Первое, ради чего приложение открывают: отдать файл и увидеть, что с ним происходит. - [✨ Сделать экран списка своих записей и чтения текста](items/records-list-screen.md) — Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем. +- [✨ Удалять запись со всеми уровнями текста по требованию владельца](items/delete-record.md) — Ни файлы, ни расшифровки не удаляются вовсе: убрать запись сегодня можно только руками в базе и в каталоге на сервере. - [✨ Узнавать уже загруженный файл по хеш-сумме](items/dedup-by-content-hash.md) — Один и тот же файл, отправленный дважды, распознаётся дважды и оплачивается дважды: приём не смотрит на содержимое вовсе. - [✨ Принимать до десяти файлов одной загрузкой](items/multi-file-upload.md) — Приём берёт один файл в запросе, а с телефона выбирают пачку сразу: десять записей значат десять заходов на экран загрузки. - [✨ Показывать ход загрузки записи на экране](items/upload-progress.md) — Гигабайтный файл уходит на сервер молча: до ответа сервера экран не отличает идущую загрузку от зависшей. diff --git a/tasks/ROADMAP.md b/tasks/ROADMAP.md index a90e4f9..d5d79c5 100644 --- a/tasks/ROADMAP.md +++ b/tasks/ROADMAP.md @@ -33,6 +33,7 @@ - [🎯 Принимается запись любого формата, включая дорожку из видео](items/any-audio-source.md) — Конвертер вызывается одной командой ffmpeg, проверенной на голосовых Telegram; что он берёт помимо них, никто не мерил. - [🎯 Запись длиной до шести часов доходит до текста](items/long-recordings.md) — Потолок не замерен ни на одном звене: Telegram не отдаёт больше 20 МиБ, границы модели deferred-general неизвестны, а перезапуск на середине начинает распознавание заново. - [🎯 Приложение показывает, о чём запись, не читая её целиком](items/text-insights.md) — Расшифровка часового разговора — это стена текста: найти в списке нужную запись и вспомнить, о чём она, сегодня нечем. +- [🎯 Человек убирает свою запись из архива вместе со всеми текстами](items/data-ownership.md) — Хранение бессрочное, а способа убрать запись нет ни одного: ошибочно загруженный файл и разговор, который человек не хочет держать у нас, остаются навсегда. ## Сопровождение diff --git a/tasks/items/data-ownership.md b/tasks/items/data-ownership.md new file mode 100644 index 0000000..b5bf18e --- /dev/null +++ b/tasks/items/data-ownership.md @@ -0,0 +1,26 @@ +# 🎯 Человек убирает свою запись из архива вместе со всеми текстами + +- **Тип:** goal +- **Секция:** Направления +- **Зачем:** Хранение бессрочное, а способа убрать запись нет ни одного: ошибочно загруженный файл и разговор, который человек не хочет держать у нас, остаются навсегда. + +Сервис объявлен архивом 2026-08-11, и с тем же решением у человека появляется +обратное право: сказать «убери это» и убедиться, что убрано. Речь в записи +принадлежит тем, кто говорил, а не хранилищу. + +Стирается всё, что породила запись: сам файл, его фрагменты, объект в Object +Storage и все уровни текста. **Учёт расхода при этом остаётся** — деньги уже +потрачены, и сводка владельца задним числом не переписывается; строки +потребления несут идентификаторы и числа, не текст. + +## Завершение + +1. Своя запись убирается одним действием, и после него не остаётся ни файла, ни + объекта в хранилище, ни одного из уровней текста. +2. Убранное не возвращается: восстановления нет, и человек предупреждён об этом + до подтверждения. +3. Чужую запись убрать нельзя — ни по идентификатору, ни по токену. +4. Сводка расхода после удаления не меняется: потраченное остаётся видно + владельцу сервиса. +5. Тот же файл, загруженный снова, обрабатывается как новая запись, а не + узнаётся дедупликацией удалённой. diff --git a/tasks/items/delete-record.md b/tasks/items/delete-record.md new file mode 100644 index 0000000..432ab78 --- /dev/null +++ b/tasks/items/delete-record.md @@ -0,0 +1,51 @@ +# ✨ Удалять запись со всеми уровнями текста по требованию владельца + +- **Тип:** feature +- **Категория:** Очередь +- **Зачем:** Ни файлы, ни расшифровки не удаляются вовсе: убрать запись сегодня можно только руками в базе и в каталоге на сервере. +- **Теги:** goal:data-ownership + +Двигает все пять пунктов «Завершения» цели: запись убирается одним действием +вместе с файлом, объектом в хранилище и всеми уровнями текста. Чужую запись +убрать нельзя. Учёт расхода остаётся. + +Удаление необратимо и потому спрашивает подтверждения. Задача, которая ещё в +работе, тоже убирается: конвейер обязан заметить исчезнувшую запись и не +продолжать её шаг. + +## Затрагивает + +- новый эндпоинт удаления своей записи и его код ответа; +- экран списка и экран записи: действие удаления и подтверждение; +- файл в каталоге хранения, его фрагменты и объект в Object Storage; +- строки задачи и файла в базе, а также колонка хеш-суммы, по которой ищет + дедупликация; +- `internal/service/transcribe.go` — шаги, держащие задачу захватом + (`FindAndRunConversionJob`, `FindAndRunTranscribeJob`, + `FindAndRunTranscribeCheckJob`), и захват `FindAndAcquire`; +- `docs/security.md`, «Что вне модели» — пункт про удаление данных перестаёт + быть правдой и переписывается; +- `docs/passport.md` — граница «сколько запись лежит на диске». + +## Критерии приёмки + +- Удаление своей записи убирает задачу, файл на диске и объект в Object + Storage, а повторное чтение отвечает «не найдено». Оракул — тест на подставном + хранилище: вызов удаления объекта один, каталог пуст, чтение даёт `404`. +- Чужая запись не удаляется по своему идентификатору. Оракул — тест под другой + учётной записью: ответ «не найдено», задача на месте. +- Строки потребления удалённой записи остаются, и сводка расхода не меняется. + Оракул — тест: сумма по владельцу до и после удаления совпадает. +- Тот же файл после удаления загружается и распознаётся заново, а не узнаётся + дедупликацией. Оракул — тест: загрузка, удаление, повторная загрузка — вторая + заводит новую задачу. +- Удаление задачи, которую держит воркер, не оставляет ни файла, ни повисшего + шага: шаг заканчивается без записи в удалённую задачу. Оракул — тест на задаче + в состоянии `transcribe` с истёкшим захватом. + +## Рамки + +Удаление необратимо: восстановления не делаем и корзины не заводим. Массового +удаления и удаления по сроку хранения здесь нет — только своя запись по одной. +Учёт расхода не трогается по решению человека от 2026-08-11. Берётся после +`record-ownership`: до неё неизвестно, чья запись.