Specs security / security (push) Successful in 8s
Specs verify / verify (push) Successful in 11s
Conventional commits / commits (pull_request) Successful in 12s
Specs security / security (pull_request) Successful in 7s
Specs verify / verify (pull_request) Successful in 11s
220 lines
26 KiB
Markdown
220 lines
26 KiB
Markdown
# Контейнерная разработка и поставка WealthLedger
|
||
|
||
Основание — утверждённые [Proposal](proposal.md),
|
||
[Delta Specs](specs/application-delivery/spec.md) и [Design revision 1](design.md).
|
||
UI/UX package неприменим: пользовательские экраны и interactions не меняются.
|
||
|
||
Checkbox закрывается только после выполнения действия и его Verification.
|
||
Перед Node-командами активируется `.nvmrc` соответствующего независимого
|
||
репозитория. Git-команды выполняются из репозитория-владельца; production data,
|
||
secrets и опубликованные release tags не используются как test fixtures.
|
||
|
||
Задачи выполняются по порядку разделов. Frontend 2.1–2.2 и backend 3.1–3.2
|
||
могут идти параллельно после 1.2. Workspace orchestration начинается после
|
||
готовности обоих component images. Реальный server rollout выполняется только
|
||
после локального production smoke и документированного backup.
|
||
|
||
## 1. SDD и внешние prerequisites
|
||
|
||
- [x] 1.1 [specs] Проверить утверждённый SDD package закреплённым OpenSpec CLI и сохранить canonical change структурно валидным до Apply.
|
||
- **Covers:** `N/A` — обязательный gate canonical SDD authority; он не добавляет runtime behavior, но разрешает реализацию утверждённых требований и Design.
|
||
- **Verification:** с версией из `openspec/.cli-version` выполнить `openspec schema validate wealth-ledger-sdd --verbose`, `openspec validate containerized-application-delivery --type change --strict --store wealth-ledger-specs`, `openspec validate --specs --strict --store wealth-ledger-specs`, затем `npm run verify`.
|
||
- **Result (2026-09-16):** по явному уточнению владельца использован установленный OpenSpec 1.13.0 вместо 1.10.0. Все четыре проверки прошли; 25 specs passed, 0 failed. Repository-wide pin не изменён.
|
||
|
||
- [ ] 1.2 [workspace] Подтвердить prerequisites выбранного Solution Assessment: Docker Compose на development/server hosts, Docker-capable Gitea runner, Container Registry и раздельные package write/read credentials.
|
||
- **Covers:** `N/A` — техническое предусловие утверждённой Compose/Registry delivery; оно не вводит самостоятельное поведение приложения и блокирует custom fallback при отсутствии platform capability.
|
||
- **Verification:** зафиксировать поддерживаемые Docker/Compose и Gitea versions; `docker version`, `docker compose version`, успешные authenticated registry login для CI publisher и server pull-only identity без вывода credentials.
|
||
- **Partial result (2026-09-16):** локально Docker Desktop 4.91.0, Engine 29.8.0 (Linux arm64), Compose 5.5.1; Gitea API сообщает 1.27.3. Server Docker/Compose и Docker-capable runner пока не подтверждены. MCP identity не имеет доступа к Actions secrets (`user should be the owner of the repo`); раздельные publisher/pull-only login не проверены. Publication и server rollout остаются внешними gates; независимая локальная реализация продолжается.
|
||
|
||
## 2. Frontend image и ingress
|
||
|
||
- [x] 2.1 [frontend] Добавить development и production image targets: Dockerized Vite watch с same-origin `/api/` proxy и nginx runtime со SPA fallback, API proxy, runtime DNS re-resolution, cache policy и non-root-compatible filesystem.
|
||
- **Covers:** `application-delivery` — `Локальная среда воспроизводит границы поставленного приложения`; `application-delivery` — `Серверное приложение имеет одну внешнюю точку входа`.
|
||
- **Verification:** собрать оба targets; development container через container network отдаёт HMR и сохраняет `/api/v1/...`; production container отдаёт SPA/deep link, проксирует API без rewrite, не раскрывает backend port и после backend recreation подключается к новому container address.
|
||
|
||
- [ ] 2.2 [frontend] Добавить release-tag workflow публикации frontend image с проверкой версии, canonical frontend gate, OCI revision metadata и immutable semver tags без `latest`.
|
||
- **Covers:** `application-delivery` — `Поставка допускает независимое обновление компонентов`.
|
||
- **Verification:** workflow syntax принимается Gitea; controlled candidate run после `npm run verify` публикует image, registry возвращает digest, clean pull по digest запускает тот же static artifact; credential отсутствует в logs и image history.
|
||
|
||
- [x] 2.3 [frontend] Выполнить frontend repository completion gate после packaging и routing changes.
|
||
- **Covers:** `N/A` — repository-owned проверка реализации Design и отсутствие regression в существующем frontend; отдельного observable requirement не создаёт.
|
||
- **Verification:** `npm ci`, `npm run verify`, `git diff --check`; вручную просмотреть новые Docker/nginx/workflow files и отсутствие secrets.
|
||
|
||
### Результаты frontend (2026-09-16)
|
||
|
||
- 2.1: оба targets собраны на Linux arm64; Vite HMR WebSocket connected,
|
||
`/api/v1/probe?check=path` сохранён. nginx read-only + tmpfs отдаёт deep link
|
||
с `no-cache`, proxy сохраняет path/query/host/proto/request ID. Backend без
|
||
host port пересоздан с другим IP (.2 → .5); nginx восстановил proxy без restart.
|
||
- 2.2: release-tag workflow реализован с `ubuntu-latest` (label подтверждён
|
||
владельцем). Реальный runner Docker access, registry write/read credentials,
|
||
Gitea workflow acceptance и controlled publication пока не проверены.
|
||
- 2.3: `npm ci`, `npm run verify`, `git diff --check` прошли. Docker context
|
||
ограничен allowlist; `.env`, credentials и host dependencies не копируются.
|
||
Новые Docker/nginx/workflow files просмотрены; publication secrets идут
|
||
только в отдельный Docker login, не в image build arguments.
|
||
|
||
## 3. Backend image, migration и readiness
|
||
|
||
- [x] 3.1 [backend] Добавить development и production image targets с Linux native dependencies, compiled application, generated Prisma client, runtime Prisma CLI/migrations, T-Bank proto assets, non-root process и выделенным data path.
|
||
- **Covers:** `application-delivery` — `Локальная среда воспроизводит границы поставленного приложения`; `application-delivery` — `Постоянное состояние и секреты переживают штатный lifecycle`; `application-delivery` — `Обновление API сохраняет целостность данных`.
|
||
- **Verification:** development target выполняет Nest watch; production target стартует из чистого image, находит proto contracts, принимает config только из environment, пишет только в data path; одноразовый `prisma migrate deploy` проходит на fresh и representative existing SQLite, а приложение не стартует после контролируемой migration failure.
|
||
|
||
- [ ] 3.2 [backend] Добавить release-tag workflow публикации backend image с проверкой версии, полными backend gates, OCI revision metadata и immutable semver tags без `latest`.
|
||
- **Covers:** `application-delivery` — `Поставка допускает независимое обновление компонентов`.
|
||
- **Verification:** workflow syntax принимается Gitea; controlled candidate run выполняет `npm run verify` и `npm run test:e2e`, публикует image и получает digest; clean pull по digest выполняет migration и production start, credential отсутствует в logs и image history.
|
||
|
||
- [x] 3.3 [backend] Выполнить backend repository completion gates и проверить container readiness без нового публичного endpoint.
|
||
- **Covers:** `N/A` — repository-owned проверка packaging, existing auth boundary и SQLite integration из Design; самостоятельного нового API behavior нет.
|
||
- **Verification:** `npm ci`, `npm run verify`, `npm run test:e2e`, `git diff --check`; healthcheck принимает завершённый HTTP-ответ existing auth route, а stopped/uninitialized process остаётся unhealthy.
|
||
|
||
## 4. Workspace Compose orchestration
|
||
|
||
- [x] 4.1 [workspace] Добавить production Compose topology с version-tagged frontend/backend references, migration service, healthchecks, persistent host data directory, internal-only backend и единственным configurable frontend loopback port.
|
||
- **Covers:** `application-delivery` — `Серверное приложение имеет одну внешнюю точку входа`; `application-delivery` — `Поставка допускает независимое обновление компонентов`; `application-delivery` — `Постоянное состояние и секреты переживают штатный lifecycle`; `application-delivery` — `Обновление API сохраняет целостность данных`.
|
||
- **Verification:** `docker compose config` с non-secret fixture environment; rendered config содержит один host port, один backend replica, одинаковый backend image reference у migration/runtime и отдельный data mount; missing required image/config/secret values дают blocking configuration/start failure.
|
||
|
||
- [x] 4.2 [workspace] Добавить development Compose override с local image targets, source watch/mounts, container-owned dependencies, отдельным local data volume и одним frontend browser origin.
|
||
- **Covers:** `application-delivery` — `Локальная среда воспроизводит границы поставленного приложения`; `application-delivery` — `Постоянное состояние и секреты переживают штатный lifecycle`.
|
||
- **Verification:** с чистыми local volumes запустить Compose; frontend edit появляется через HMR, backend edit перезапускает Nest, `/api/v1/...` идёт через frontend origin, host `node_modules` не используется, restart сохраняет local data, production data path отсутствует в rendered config.
|
||
|
||
- [x] 4.3 [workspace] Обновить root tracking allowlist и README entrypoints без custom scripts или нового root CI, затем проверить orchestration configuration.
|
||
- **Covers:** `N/A` — root только связывает независимые component repositories и объясняет стандартные Compose entrypoints согласно Design; runtime behavior реализуется component images и Compose tasks 4.1–4.2.
|
||
- **Verification:** `docker compose config` для production и development variants, `git diff --check`, просмотр `git status --short` и staged paths подтверждают только явно разрешённые root files; root workflow остаётся только commitlint.
|
||
|
||
## 5. Эксплуатационная документация
|
||
|
||
- [x] 5.1 [docs] Добавить ADR об ограниченном single-server SQLite deployment и обновить container architecture фактической Docker/Caddy topology, secret и persistence boundaries.
|
||
- **Covers:** `application-delivery` — `Серверное приложение имеет одну внешнюю точку входа`; `application-delivery` — `Постоянное состояние и секреты переживают штатный lifecycle`.
|
||
- **Verification:** ADR явно связывает решение с прежним local-only baseline, одним backend instance и triggers пересмотра; architecture diagram показывает только frontend port наружу, internal backend и persistent SQLite; ссылки и термины согласованы.
|
||
|
||
- [x] 5.2 [docs] Описать development, first deployment, independent component update, stopped-backend backup, compatibility check, rollback и explicit restore runbooks.
|
||
- **Covers:** `application-delivery` — `Локальная среда воспроизводит границы поставленного приложения`; `application-delivery` — `Поставка допускает независимое обновление компонентов`; `application-delivery` — `Обновление API сохраняет целостность данных`; `application-delivery` — `Восстановление не выдаёт откат приложения за откат данных`.
|
||
- **Verification:** dry-run runbook на disposable data проходит без неописанного шага; frontend-only и backend-only paths не пересоздают другой component; restore требует отдельного решения и сообщает о потере данных после backup.
|
||
|
||
- [x] 5.3 [docs] Выполнить docs repository completion gate.
|
||
- **Covers:** `N/A` — repository-owned проверка durable as-built documentation и ссылок без нового runtime behavior.
|
||
- **Verification:** `npm ci`, `npm run verify`, `git diff --check`; проверить Mermaid rendering, взаимные ADR/architecture/runbook links и отсутствие credentials или host-private values.
|
||
|
||
## 6. Сквозная проверка поставки
|
||
|
||
- [x] 6.1 [workspace] Проверить development Compose на чистом checkout и production Compose на локально собранных release-equivalent images.
|
||
- **Covers:** `application-delivery` — `Локальная среда воспроизводит границы поставленного приложения`; `application-delivery` — `Серверное приложение имеет одну внешнюю точку входа`.
|
||
- **Verification:** development подтверждает HMR/watch и same-origin login; production smoke подтверждает SPA deep link, registration/login/refresh, API proxy, отсутствие host backend port и clean startup через migration service.
|
||
|
||
- [x] 6.2 [workspace] На disposable representative SQLite проверить persistence, frontend-only update, backend-only update, migration failure и rollback boundaries.
|
||
- **Covers:** `application-delivery` — `Поставка допускает независимое обновление компонентов`; `application-delivery` — `Постоянное состояние и секреты переживают штатный lifecycle`; `application-delivery` — `Обновление API сохраняет целостность данных`; `application-delivery` — `Восстановление не выдаёт откат приложения за откат данных`.
|
||
- **Verification:** restart сохраняет user/session/card data и decryptable test connection; frontend update не recreates backend/data, backend update не recreates frontend; failed migration не делает backend healthy; возврат совместимого image сохраняет current data, а restore отдельной backup copy выполняется только в изолированном scenario с явной проверкой потерянного интервала.
|
||
|
||
- [ ] 6.3 [workspace] Проверить release images из Gitea по выбранным version tags перед первым server rollout и сохранить точную установленную пару версий.
|
||
- **Covers:** `application-delivery` — `Поставка допускает независимое обновление компонентов`; `application-delivery` — `Постоянное состояние и секреты переживают штатный lifecycle`.
|
||
- **Verification:** pull-only server identity получает оба versioned images; `docker image inspect` подтверждает expected revisions; production smoke проходит на этих images с disposable config/data, после чего оператор записывает выбранные image references без secrets.
|
||
|
||
## Apply verification — 2026-09-16
|
||
|
||
### Backend (3.1–3.3)
|
||
|
||
- Development и production targets собраны на Linux arm64 / Node 24.21.0.
|
||
Node/nginx base images закреплены digest; OS packages — точными версиями.
|
||
Чистый production runtime обнаружил неверный прежний fallback proto path:
|
||
Nest копирует assets в `dist/src`; loader теперь использует `__dirname`.
|
||
Nest watch использует `ps`; `procps` добавлен только в development target.
|
||
- `npm ci`, `npm run verify` — PASS (35 suites). `npm audit` при переносе
|
||
существующего Prisma 7.10.0 в runtime dependencies — 0 vulnerabilities.
|
||
- `npm run test:e2e` на macOS дважды завершился native Node SIGABRT
|
||
(`RemoveEnvironmentCleanupHook`, `env != nullptr`). Тот же штатный gate
|
||
выполнен в Linux development image: **8 suites / 98 tests PASS**. Это
|
||
Linux delivery proof; macOS native cleanup limitation не скрывается.
|
||
- `npm run test:container -- wealthledger-backend:production` — runnable
|
||
image regression: fresh/existing migration, runtime без source/.env/compiler,
|
||
non-root + read-only filesystem, существующий auth readiness, user/session
|
||
persistence. Он же вызывается publish workflow после clean pull.
|
||
- Controlled broken SQLite migration возвращает non-zero; backend остаётся
|
||
остановленным. После возвращения исправного data path migration идемпотентна.
|
||
- 3.2 остаётся открытой: YAML и shell syntax проверены локально, но Gitea
|
||
acceptance, authenticated publication и pull по registry digest не выполнены.
|
||
|
||
### Compose, docs и lifecycle (4–6)
|
||
|
||
- Production/development `docker compose config` — PASS. Проверены один
|
||
loopback host port, backend scale 1, общий backend image/data mount у
|
||
migration/runtime, readiness dependency и отсутствие production bind mount
|
||
в development services. Пустая production environment блокирует config.
|
||
- `docker compose up -d --wait` — PASS для development и production на
|
||
локально собранных release-equivalent images. Production smoke использовал
|
||
временный override local image references; canonical config остаётся
|
||
digest-pinned. Реальный Gitea digest pull этим не подменяется.
|
||
- Development: source edit вызвал HMR event и изменённый HTTP module, backend
|
||
source edit — новый Nest process и готовый API. Временные изменения
|
||
восстановлены. Same-origin registration/login/refresh и data restart PASS.
|
||
- Clean checkout proof: отдельный HEAD export + task diff, без host .env и
|
||
node_modules, новый Compose project/volume; startup и HMR/watch PASS.
|
||
- Browser proof (bundled Playwright/Chrome): clean development и production
|
||
registration/login/reload-refresh PASS; все API requests same-origin,
|
||
httpOnly/path сохранены, production cookie Secure. Caddy/TLS вне локальной проверки.
|
||
- Production: SPA deep link, registration/login/refresh, неизменённый API path,
|
||
отсутствие backend host port, non-root read-only runtime — PASS.
|
||
- Representative disposable SQLite содержит user/session/card и connection,
|
||
зашифрованный настоящим TbankCrypto на вымышленном token. Restart, migrations
|
||
и independent recreation сохраняют данные и расшифровку без внешнего T-Bank.
|
||
- Отдельные local fixture image IDs проверили frontend-only/backend-only
|
||
update без recreation другого компонента. Возврат прежнего совместимого
|
||
backend image сохраняет запись после backup. Это compatibility fixture,
|
||
не обещание совместимости произвольных будущих релизов.
|
||
- Stopped-backend backup и isolated restore с отдельным project/port/data path
|
||
— PASS: login, карта до backup и decryptable connection доступны; запись
|
||
после backup отсутствует в восстановленной копии. Текущая база не заменялась.
|
||
- Docs: ADR 015 расширяет ADR 001; topology и runbook взаимно связаны.
|
||
`npm ci`, `npm run verify` — PASS (44 Markdown files), `git diff --check`
|
||
— PASS. Mermaid source семантически просмотрен, но renderer в доступном
|
||
окружении не найден; **5.3 оставлена открытой до проверки rendering**.
|
||
- Root Boy Scout/allowlist review: только Compose, два безопасных env examples,
|
||
README и .gitignore; новых root scripts/dependencies/CI нет. Source markers
|
||
watch checks восстановлены. Чужие runtime data и checkout changes сохранены.
|
||
- Docker disk был заполнен. После адресной очистки intermediates этой задачи
|
||
и отдельно разрешённой очистки unused build cache старше суток проверки
|
||
продолжены; чужие images/containers/volumes не удалялись.
|
||
|
||
### Оставшиеся внешние gates
|
||
|
||
- 1.2: server Docker/Compose, Docker capability runner `ubuntu-latest`,
|
||
раздельные package writer и server pull-only authenticated login.
|
||
- 2.2/3.2: реальные Gitea candidate workflows и публикация frontend/backend,
|
||
OCI revisions, registry digests, clean pull и отсутствие credentials в logs.
|
||
Нужны Actions secrets `REGISTRY_USERNAME`/`REGISTRY_TOKEN` в каждом component
|
||
repo и отдельная server identity. Workflow не публикует `latest` и fail-closed
|
||
отказывается от overwrite или недоказанного отсутствия tag.
|
||
- 6.3: smoke опубликованной пары version tags на disposable server config/data,
|
||
запись installed image references/revisions до первого server rollout.
|
||
Server rollout, Caddy, TLS и реальный финансовый запрос не выполнялись.
|
||
|
||
### Упрощение deployment configuration по запросу владельца
|
||
|
||
Обязательные `FRONTEND_DIGEST`/`BACKEND_DIGEST` и отдельные repository fields
|
||
заменены на `FRONTEND_IMAGE`/`BACKEND_IMAGE` с release version tags.
|
||
Design и runbook синхронизированы. Результаты выше описывают первоначальную
|
||
проверку; обязательный digest pinning более не является deployment contract.
|
||
Migration и backend по-прежнему используют один image reference.
|
||
|
||
Verification упрощения: production/development `docker compose config` PASS;
|
||
разные version tags выбираются независимо, migration/runtime получают один
|
||
backend image, один loopback port и development volume сохранены. Пустые
|
||
image/secret values блокируют config. Docs `npm run verify`, OpenSpec strict
|
||
change validation и `git diff --check` в affected repos — PASS.
|
||
|
||
По следующему запросу владельца из `FROM` удалены base image digests:
|
||
используются `node:24.21.0-bookworm-slim` и `nginx:1.28.0-alpine`.
|
||
Version tags не гарантируют неизменность содержимого базового image.
|
||
Verification: повторная сборка production/development targets обоих компонентов
|
||
— PASS (с использованием Docker cache). Dockerfile остаются в component repos,
|
||
чтобы standalone build и release CI не зависели от root checkout.
|
||
|
||
По запросу владельца удалены дополнительные commit tags из обоих workflows;
|
||
release tag защищён от перезаписи, revision остаётся в OCI label. Production
|
||
Compose наследует healthchecks из images; development содержит собственные
|
||
checks с прежними таймингами, migration отключает check.
|
||
Verification: production/development Compose config и invariants, healthchecks
|
||
локальных production images, shell syntax обоих workflows, docs verify,
|
||
OpenSpec strict validation и diff checks — PASS. Registry publication остаётся
|
||
внешней непроверенной частью задач 2.2/3.2.
|