# Хранилищем становится SQLite с каталогом файлов, а PocketBase уходит целиком

- **Дата:** 2026-08-22
- **Источник:** [../research/storage-without-pocketbase.md](../research/storage-without-pocketbase.md) —
  записка разведки о выборе хранилища

## Решение

PocketBase уходит из проекта целиком: состояние записей и метаданные переезжают
в SQLite, с которым сервис работает напрямую через `modernc.org/sqlite`, файлы
записей — в свой каталог со своей раскладкой, маршруты и слои — на `net/http`,
шаги схемы — на свой раннер. Панель администратора теряется и **не заменяется
ничем**: пока идёт стройка, остановленную запись возвращает в работу запрос к
базе.

**Два решения абзаца выше сменились при разметке изменения**, и заменившее
названо здесь.

Шаги схемы двигает библиотека `github.com/pressly/goose/v3`, а не свой раннер.
Инструмент выбрал владелец 2026-08-22: библиотека уже была в этом проекте и ушла
вместе с PocketBase, а из трёх норм, которые накат обязан выполнять, две
выполняет сама.

Остановленную запись возвращает в работу подкоманда `cmd/devtools resume`, а не
запрос к базе руками. Возврат сбрасывает не одно поле записи и пишет событие
журнала с происхождением `entity.EventOriginHuman`; рука за клавиатурой не делает
ни того, ни другого. Последствие ниже — «возврат остановленной в работу […]
делает запрос к базе руками» — читается этой сменой.

Доводы обоих решений записаны в
[design.md](../../openspec/changes/archive/2026-08-23-storage-without-pocketbase/design.md),
разделы «Шаги схемы двигает `goose`, а не свой раннер» и «Панель не заменяется
ничем, а возврат в работу делает подкоманда оснастки».

Запись заменяет три:
[ADR-2026-08-11-pocketbase-storage-with-admin-panel](ADR-2026-08-11-pocketbase-storage-with-admin-panel.md),
[ADR-2026-08-11-queue-as-pocketbase-collection](ADR-2026-08-11-queue-as-pocketbase-collection.md) и
[ADR-2026-08-12-protected-file-behind-session](ADR-2026-08-12-protected-file-behind-session.md).

**Что из заменённых решений подтверждается, а не отменяется.** Очередь остаётся
своей таблицей, захват — одним запросом с `RETURNING`, готовую библиотеку
очереди по-прежнему не берём: замер, которым это решено, снят на
`modernc.org/sqlite` — том самом драйвере, который остаётся и после ухода.
Отменяется у той записи одно слово: таблица перестаёт быть коллекцией.
Приложение остаётся в своём корне `/app/`
([ADR-2026-08-15-app-namespace](ADR-2026-08-15-app-namespace.md)). Файл записи
остаётся закрытым — но проверкой владельца в своём обработчике, а не защищённым
полем коллекции и коротким токеном.

## Почему

Разведка мерила не «хранилище против хранилища», а то, что библиотека держит в
этом коде. Цитата из источника:

> Разрез «хранилище против хранилища» вопроса не покрывает: библиотека держит
> шесть ролей сразу, и только две из них про хранение.

Довод, на котором стоял перевод, отпал сам. Источник, раздел о трёх доводах:

> Вход делает само приложение с 2026-08-22 […]: пришедшего называет заголовок
> прокси, а учётную запись заводит наш `EnsureUser`. Пользователи в коллекции
> есть **потому, что их пишет наш код**, а не провайдер библиотеки. Довод,
> которым отвергнут отвергнутый вариант, перестал быть верным.

Отвергнут вариант «уйти в два шага», оставив панель жить в промежутке, и
отвергнут решением владельца: панель на стройке заменяется запросом к базе, а
вторая порция работы стоит дороже, чем то, что она сберегает.

Обстоятельство, которое назначило момент:

> на сервере данных нет и сервис остановлен, поэтому смена стоит только кода.
> Дешевле она не станет никогда — каталог `internal/controller/http` прирастает
> кодом на чужих типах с каждой задачей.

## Последствия

- `+` периметр сервиса становится только нашим. Панель `/_/` исчезает вместе с
  дефектом `/%5f/` из [../security.md](../security.md), а пространство
  хранилища `/api/` — вместе с необходимостью держать его открытым ради файлов.
- `+` пропадает секрет, которого не было до перевода, — пароль суперпользователя
  панели.
- `+` файлы ложатся своей раскладкой, и загрузка частями, узнавание по хеш-сумме,
  удаление записи и вторая копия рядом становятся обычной работой с файлами.
- `+` из сборки уходят шесть модулей, достижимых только через библиотеку:
  `imaging`, `mailyak`, `jwt`, `fexpr`, `cobra`, драйвер MySQL.
  `modernc.org/sqlite` остаётся, и сборка по-прежнему обходится без CGO.
- `−` владелец сервиса остаётся без панели. Правку записи, возврат остановленной
  в работу и просмотр очереди до появления экранов делает запрос к базе руками.
  Задачи `audiorecord-actions` и `play-recording-in-app` этим становятся не
  улучшением, а заменой утраченного инструмента.
- `−` шаги схемы, отдачу файла, ограничитель частоты и настройку базы пишем и
  сопровождаем сами. Единственный писатель у `modernc.org/sqlite` — наша забота
  с этого дня.
- `−` раскладка каталога данных меняется необратимо. Цена сегодня нулевая:
  стройка, на сервере пусто; после первой боевой записи она перестаёт быть
  нулевой.
- `−` 1497 строк контроллера и 3075 строк его проверок написаны на
  `*core.RequestEvent` и переписываются целиком.
