Implement ecosystem contract v2: capabilities, shared ledgers, attachments

- GET /v1/capabilities and the extended sync entity allowlist (§0, §1).
- Shared ledgers (§7): ledgers, members and hashed one-time invite codes;
  X-Balance-Ledger selects the space on /v1/sync and /v1/attachments with
  its own records, change stream and cursor. Personal space keeps the v1
  tables and behaviour; the migration only adds tables.
- Attachments (§8): SHA-256, size and media type checks, blob files in
  BALANCE_ATTACHMENTS_DIR (default: attachments next to the database),
  garbage collection of unreferenced blobs with a grace period for fresh
  uploads, and ledger deletion removes its blobs.
- Tests for every new endpoint and error code; API, sync, data model,
  security, deployment and README docs updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
wtandClaude Opus 5.5 committed 2026-10-31 23:30:12 +07:00
1 parent 10272f3851
commit dfb4fe4e1a
17 files changed
+2496 -56

No files matched your search

+29
View File
@@ -131,6 +131,35 @@ Server не должен предполагать, что все изменен
Нельзя молча трактовать неизвестный/просроченный cursor как актуальный.
## Пространства (контракт v2)
`POST /v1/sync` работает в одном пространстве данных:
- без `X-Balance-Ledger` — личное пространство пользователя, таблицы
`records`/`changes`, поведение v1 без изменений;
- с `X-Balance-Ledger: <id>` — общее пространство, таблицы
`ledger_records`/`ledger_changes`. Доступ проверяется на каждый запрос.
У общего пространства свой поток изменений: `sequence` берётся из отдельного
AUTOINCREMENT `ledger_changes`, а pull фильтрует по `ledger_id`. Курсор личного
пространства и курсор каждого ledger независимы, клиент хранит их раздельно.
Все участники пишут в одно пространство с тем же last-write-wins по
`updatedAt`; идемпотентность, пагинация (500), лимиты и транзакционность те же.
Удаление пространства физически удаляет его записи и историю изменений.
Удалённый участник теряет доступ (`403`), записи остаются в пространстве.
## Вложения и сборка мусора
Байты вложений загружаются отдельно (`PUT /v1/attachments/{sha256}`) до записи
`attachment` в sync. Файл пространства считается мусором, когда ни одна живая
(`deleted=0`) запись `attachment` этого пространства не ссылается на его
`sha256` (сравнение без учёта регистра). Сборка выполняется в той же транзакции,
что и sync-запрос, содержащий изменения `attachment`, и при каждом PUT.
Файл, ни разу не получивший ссылки, хранится не меньше 24 часов с последней
загрузки, чтобы не удалить байты, чья запись ещё не синхронизирована; повторная
загрузка перезапускает этот срок.
## Security
Sync endpoint всегда ограничен текущим authenticated user.