93 lines
10 KiB
Markdown
93 lines
10 KiB
Markdown
# platform — control plane (SaaS-слой)
|
||
|
||
Зона записи сессии «Платформа». P0 собран 04.08 (скелет: HTTP · сессии · схема read-model ·
|
||
интерфейс ингеста), P1 — 05.08 (вход через OIDC · кредитный леджер · админ-CLI · деплой-юнит ·
|
||
закрытие регистра дефектов). Направление зоны — `docs/PLATFORM_DIRECTION.md`, критерии приёмки —
|
||
`docs/ENGINEERING_STANDARDS.md`, дефекты — `docs/DEFECT_REGISTER.md`, зонный журнал —
|
||
`docs/platform-PROGRESS.md` (весь прогресс зоны здесь, решение владельца 04.08), стек —
|
||
`docs/STACK_DECISIONS.md`.
|
||
|
||
Батарея зоны: `make check` (build · vet · fmt · lint · test -race). Тесты со схемой требуют
|
||
`TM_PLATFORM_TEST_DSN` (как поднять Postgres без root — `docs/STACK_DECISIONS.md`); без него они
|
||
пропускаются, и `check` называет пропуски вслух. `make vuln` и `make fuzz` — отдельными целями.
|
||
|
||
Бинари: `cmd/tmplatformd` (сервис) и `cmd/tmplatformctl` (админ: гранты, КОРРЕКТИРОВКИ (`adjust`), баланс, журнал входов,
|
||
отзыв сессий, дев-интейк `book add`, и `exit-marker` — его зовёт systemd на конце прогона). Деплой — `deploy/`.
|
||
|
||
Метрики — отдельным слушателем (`TM_PLATFORM_METRICS_ADDR`, дефолт `127.0.0.1:9464`), формат
|
||
Prometheus; на контрактную поверхность они не выходят и наружу не привязываются (`docs/STACK_DECISIONS.md` §24).
|
||
Эффективная конфигурация печатается на старте с источником каждой настройки; секреты и денежные
|
||
суммы — фактом наличия, без значения (PD-114).
|
||
|
||
⚠ **Прогон — транзиентный systemd-юнит, а не ребёнок сервиса** (D39.106). Установка требует
|
||
`loginctl enable-linger`, иначе не стартует ни один прогон; почему именно так и что было
|
||
измерено — `docs/STACK_DECISIONS.md` §15–20.
|
||
|
||
## ⚠ Git и зона (читать ДО первой строки кода)
|
||
|
||
**Платформенная сессия не коммитит — лендит оркестратор.** Канон — `../CLAUDE.md`, §Гардрейлы; продублировано здесь, потому что зона живёт своим онбордингом. Направление зоны (вход · деньги · стандарты · скорость) — `docs/PLATFORM_DIRECTION.md`; стандарты и критерии приёмки — `docs/ENGINEERING_STANDARDS.md`; дефекты и уязвимости — `docs/DEFECT_REGISTER.md` (каждая находка получает строку ДО закрытия).
|
||
|
||
1. **Писать только внутрь `platform/`.** Ничего за её пределами — ни `docs/`, ни `backend/`, ни `frontend/`, ни корневых файлов. Нужна правка вне зоны — пинг владельцу, её сделает оркестратор.
|
||
2. **Чужие незакоммиченные файлы в дереве не трогать**: параллельные сессии — норма.
|
||
3. **НИКАКИХ** `git add -A`, `reset --hard`, `amend`/`rebase`, перезаписи истории и `checkout` поверх грязного дерева.
|
||
4. ⚠ Ревью-гард модулей (D39.85): путь Go-модуля платформы **никогда** не вкладывать под путь движка — иначе он получит доступ к `backend/internal/*` по правилу префикса.
|
||
|
||
## Что здесь будет
|
||
|
||
Сервис между фронтом и движком перевода. Всё, что относится к ПОЛЬЗОВАТЕЛЯМ и не относится
|
||
к переводу:
|
||
|
||
- аутентификация и аккаунты — **есть (P1)**: вход через OIDC даёт только СОБЫТИЕ входа, сессия
|
||
своя; ключ личности `(provider, subject)`, почта не ключ. Оплаты нет и в бете не будет
|
||
(владелец 05.08): аккаунты живут на кредитном балансе, фри-тир — запись `grant` в леджер;
|
||
- библиотека книг: чья книга, права доступа, хранение исходников и экспортов — **приём есть (P5)**:
|
||
`POST /v0/books` принимает multipart потоково со своим потолком тела и своим дедлайном чтения,
|
||
кладёт исходник в каталог книги под `TM_PLATFORM_BOOKS_DIR` и ведёт книгу по статусам
|
||
`uploading → parsing → not_started | rejected`; разбор — $0-команда движка `tmctl manifest`.
|
||
⚠ Кто кладёт стартовый `book.yaml` в каталог новой книги — открытый вопрос на ратификации
|
||
(D39.110 §2b): без него разбор честно отказывает, шов назван `books.ErrNotProvisioned`;
|
||
- учёт денег **на пользователя** — **схема и операции есть (P1)**: append-only леджер в целых
|
||
микро-долларах, резервации, кэш баланса с инвариантом `balance == SUM(ledger)`. Защита прогона —
|
||
холд ДО спавна плюс книжный потолок движку (жёсткий стоп исполняет движок). ⚠ Потолок, который
|
||
получает движок, — это НАКОПЛЕННЫЙ потолок КНИГИ (`committed + reserved` за всю её историю плюс
|
||
купленный прирост), а не бюджет прогона: платформа переводит одно в другое сама (D39.122);
|
||
- остановка и продолжение перевода — **есть (P5)**: `POST /v0/runs/{id}/stop` пишет НАМЕРЕНИЕ стопа
|
||
в Postgres до сигнала (иначе стоп и авария — один и тот же выход движка, PD-152) и просит systemd;
|
||
`/resume` открывает новую попытку с ОСТАТКОМ бюджета прогона, переиспользуя механику
|
||
перезапуска реконсилятора;
|
||
- очередь задач и запуск воркеров, статусы прогонов, ретраи — **есть (P4)**: River на том же
|
||
Postgres; задание очереди выдаёт только РАЗРЕШЕНИЕ стартовать, а жизнь прогона ведёт
|
||
реконсилятор, который читает мир (Postgres · журнал книги · маркер выхода) и не ждёт процесса;
|
||
- SSE-поток прогресса во фронт (⚠ денежные суммы на провод и на экран НЕ идут — D39.84; пользователь
|
||
видит ОСТАТОК процентом — окон со сбросом больше нет, владелец 05.08).
|
||
|
||
## Чего здесь НЕ будет
|
||
|
||
Перевода. Движок (`../backend/`) остаётся как есть: один процесс на книгу, свой SQLite под
|
||
эксклюзивным flock (`store.Open` — «один процесс владеет файлом проекта»). Платформа его
|
||
ЗАПУСКАЕТ как воркер, а не поглощает. Причина та же, по которой движок не ветвится по паре
|
||
языков: пользователи и квоты ничего не меняют в проводе запроса, значит им нечего делать в
|
||
кодовой базе, где каждый байт свёрнут в снапшот-хеш.
|
||
|
||
## Известное требование к движку (не забыть)
|
||
|
||
⚠ **Глобальный брокер конкурентности.** `pipeline/ratelimit.go` строит рейт-гарды НА ПРОГОН,
|
||
а лимит провайдера — на весь аккаунт. N параллельных пользователей = N независимых гардов
|
||
против общего лимита (в комментарии там зафиксировано: mistral валит ~48% вызовов под
|
||
параллелизмом). Воркер должен отпрашиваться у платформы перед вызовом. Это единственная
|
||
по-настоящему новая механика на стыке.
|
||
|
||
## Стек
|
||
|
||
Пины, даты релизов и обоснования — [`docs/STACK_DECISIONS.md`](docs/STACK_DECISIONS.md) (зонный,
|
||
live-сверка 04–05.08); общая записка по обоим новым сервисам — `../frontend/docs/STACK_DECISIONS.md` §5.
|
||
|
||
Коротко: Go 1.26.4 в `go.mod` (тулчейн сборки ≥1.26.5) · стандартный `net/http` + `ServeMux` без
|
||
роутер-библиотеки · PostgreSQL 18 · pgx v5.10.0 · goose v3.27.3 · очередь River v0.42.0 на том же
|
||
Postgres (запинена, ещё не подключена — П-3) · вход `x/oauth2` v0.36.0 + `go-oidc/v3` v3.20.0 ·
|
||
`x/time` v0.15.0 для лимита на `/auth/login` · `govulncheck` отдельной целью.
|
||
**Redis не заводим нигде** — зафиксировано как архитектурное «нет».
|
||
|
||
Прогресс наружу — SSE, события **пушит воркер**, а не фронт опрашивает read-model.
|
||
Аутентификация — одна серверная сессия в Postgres, два способа предъявления: `__Host`-кука для
|
||
браузера и `Authorization: Bearer` для десктопа и CLI; эндпоинты про куки не знают ничего.
|