# База данных приложения

Эндпоинты для работы с SQLite-базой приложения в Галактике: чтение схемы и строк, SQL-запросы, правка строк, копии базы и восстановление из копии. База лежит в постоянном томе `/data` контейнера, и платформа работает с ней, не останавливая приложение.

Скоуп: `vibe:infra`

## Операции

- [Получить сведения о базе](./database/info.md) — `GET /v1/infra/servers/:id/db`
- [Получить схему базы](./database/schema.md) — `GET /v1/infra/servers/:id/db/schema`
- [Выполнить SQL-запрос на чтение](./database/query.md) — `POST /v1/infra/servers/:id/db/query`
- [Получить строки таблицы](./database/rows.md) — `GET /v1/infra/servers/:id/db/tables/:table/rows`
- [Добавить строку](./database/rows-create.md) — `POST /v1/infra/servers/:id/db/tables/:table/rows`
- [Изменить строку](./database/rows-update.md) — `PATCH /v1/infra/servers/:id/db/tables/:table/rows/:key`
- [Удалить строку](./database/rows-delete.md) — `DELETE /v1/infra/servers/:id/db/tables/:table/rows/:key`
- [Выполнить SQL на запись](./database/execute.md) — `POST /v1/infra/servers/:id/db/execute`
- [Получить статус подготовки хоста](./database/prepare-status.md) — `GET /v1/infra/servers/:id/app-db/prepare`
- [Подготовить хост](./database/prepare.md) — `POST /v1/infra/servers/:id/app-db/prepare`
- [Получить список копий базы](./database/backups.md) — `GET /v1/infra/servers/:id/db/backups`
- [Создать копию базы](./database/snapshot.md) — `POST /v1/infra/servers/:id/db/backups`
- [Восстановить базу из копии](./database/restore.md) — `POST /v1/infra/servers/:id/db/restore`

Во всех путях `:id` — ID приложения с `kind` равным `GALAXY_APP` из [`GET /v1/infra/servers`](/docs/infra/servers/list), а не ID галактики-носителя.

Добавление, изменение и удаление отдельных строк доступны только через REST API. MCP-инструмент `manage_app_db` записывает в базу только SQL с `action: "execute"`.

## Когда база доступна

Работа с базой требует четырёх условий:

1. **Возможность включена для портала.** Признак — поле `features.appDb` в ответе [`GET /v1/me`](/docs/keys-auth/me). При `false` все эндпоинты раздела отвечают `403 APP_DB_DISABLED`.
2. **Приложение размещено в Галактике.** Отдельная виртуальная машина получает `400 APP_DB_UNSUPPORTED_PLACEMENT`. Как устроено размещение — [Galaxy-приложение](/docs/infra/galaxy).
3. **Работающее приложение держит базу в режиме WAL и не закрывает соединение.** Платформа подставляет в контейнер переменные `VIBE_DB_PATH=/data/app.db` и `VIBE_DATA_DIR=/data`, если автор приложения не задал их сам. Приложение открывает файл из `VIBE_DB_PATH`, включает `PRAGMA journal_mode=WAL` и `PRAGMA busy_timeout=5000`. Каждый рабочий процесс открывает одно соединение при старте и держит его до завершения процесса, а не открывает новое на каждый запрос. Если работающее приложение закрыло соединение, чтение схемы, строк, SQL-запросы и копии отвечают `409 APP_DB_WAL_CLOSED`. Базу спящего приложения, чей контейнер остановлен, можно читать и копировать и с закрытым соединением: платформа открывает её только для чтения. Если приложение проснулось во время чтения или копирования, результат отбрасывается, и приходит `409 APP_DB_BUSY`.
4. **Хост галактики подготовлен.** Для чтения и копий на хосте нужен служебный образ. Пока хост не подготовлен, чтение отвечает `503 APP_DB_TOOL_UNAVAILABLE`. Проверить готовность — [`GET /app-db/prepare`](./database/prepare-status.md), подготовить — [`POST /app-db/prepare`](./database/prepare.md).

Доступ к базе есть у ключа, которым создано приложение, у ключа приложения и у участника команды разработки этого приложения из того же портала. Внешние участники доступа не получают. Чужое или удалённое приложение отвечает `404 NOT_FOUND`.

Чтение и запись базы, подготовка хоста и ручная копия будят спящий хост галактики, контейнер приложения при этом не запускается. На подключение хоста отводится до 10 секунд. Не дождался — ответ `409 APP_DB_BUSY` с заголовком `Retry-After`, пробуждение продолжается, и повтор после паузы получает результат.

Чтение видит изменения, которые приложение уже зафиксировало, даже если журнал WAL ещё не перенесён в основной файл. Изменения незавершённой транзакции приложения не видны.

## Защитные копии перед записью

Перед каждым изменением базы платформа сохраняет защитную копию с `triggerType` равным `PRE_CHANGE`. Пока копия не готова, вызов записи отвечает `409 APP_DB_BACKUP_NOT_READY` с заголовком `Retry-After`, и его нужно повторить. Создание копии занимает до нескольких минут.

- [`POST /db/execute`](./database/execute.md) создаёт новую защитную копию перед каждым вызовом.
- Добавление, изменение и удаление строк используют одну защитную копию в течение 10 минут. Копия для другого `file` создаётся не раньше чем через 10 минут после предыдущей копии правки строк этого приложения. До этого вызов отвечает `409 APP_DB_BACKUP_NOT_READY`.

ID защитной копии приходит в поле `backupId` ответа записи. Из неё можно [восстановить базу](./database/restore.md), если изменение оказалось ошибочным. Правки строк в течение 10 минут получают одну и ту же копию, поэтому восстановление из неё отменяет все правки строк, сделанные после её создания, а не только последнюю.

## Копии и их хранение

- Копируется один SQLite-файл, а не приложение и не весь том. Приложение продолжает работать во время копирования: в копию входят подтверждённые изменения из журнала WAL.
- Ежедневная копия делается только для работающего хоста, расписание хост не будит.
- Копия хранится семь дней, срок указан в поле `expiresAt`.
- Ручные, ежедневные и защитные копии занимают оплачиваемое хранилище портала по размеру зашифрованного объекта. Перед удалением приложения платформа сохраняет копию с `triggerType` равным `PRE_DELETE`, её хранение оплачивает платформа (`platformPaid: true`).
- Копии шифруются и хранятся в S3-совместимом объектном хранилище в российском регионе (152-ФЗ).
- Заморозка удаления аккаунта или портала блокирует создание копий и восстановление.

## Лимиты

| Группа запросов | Лимит на портал |
|---|---|
| Чтение: сведения, схема, SQL-запрос на чтение, строки, список копий, статус подготовки | 60 в минуту |
| Запись: SQL на запись, правка строк, создание копии, восстановление, запуск подготовки | 20 в минуту |

В таблице — общий лимит платформы на портал, он один для всех ключей и приложений портала. Действующий лимит для ключа и остаток приходят в заголовках `x-ratelimit-limit` и `x-ratelimit-remaining` каждого ответа. Если платформа делит лимит между репликами, значение `x-ratelimit-limit` меньше числа из таблицы, поэтому паузы между запросами настраивайте по заголовкам. Превышение лимита — `429 RATE_LIMITED` с заголовком `Retry-After`.

## Типовой сценарий

1. Проверить, что возможность включена: [`GET /v1/me`](/docs/keys-auth/me) → `features.appDb: true`.
2. Проверить готовность хоста: [`GET /app-db/prepare`](./database/prepare-status.md). При `state` не равном `ready` — [`POST /app-db/prepare`](./database/prepare.md) и опрашивать статус до `ready`.
3. Посмотреть таблицы: [`GET /db/schema`](./database/schema.md).
4. Прочитать данные: [`GET /db/tables/:table/rows`](./database/rows.md) или [`POST /db/query`](./database/query.md).
5. Перед крупным изменением сохранить копию: [`POST /db/backups`](./database/snapshot.md) и дождаться `status: "COMPLETED"` в [`GET /db/backups`](./database/backups.md).
6. Изменить данные: [`POST /db/execute`](./database/execute.md) или правка отдельных строк.

## Смотрите также

- [Galaxy-приложение](/docs/infra/galaxy)
- [Полный деплой приложения](/docs/infra/deploy/deploy)
- [Хранилище](/docs/storage)
- [Инфраструктура](/docs/infra)
