docs(status): snapshot v0.9 (2026-05-06)

Закрыло за сутки: bundle 7→40 справочников, Альтум pull verified,
Tempo+Loki+Grafana+SLO burn rate alerts (Google SRE multi-window),
UI scope grouping + accents, INTERNAL словари visibility (rolesClaim),
CNPG 1.24→1.25.4 + spec.probes, CI helm --atomic + auto-rollback,
config-server startupProbe, 75→109 unit тестов.

Готовы к prod (manifests + runbooks): multi-instance Postgres HA,
external S3 backup, k6 benchmark setup. Block — DNS/cert prod,
S3 provider creds, real ЦУОД data.

Прогресс: 21/33 (v0.5) → 27/39 требований (v0.9). 1-2 недели до v1.
v0.5 файл оставлен как историческая запись.
This commit is contained in:
Zimin A.N.
2026-05-06 13:27:31 +03:00
parent 9b20cbf9b9
commit 81c959b7e2
2 changed files with 352 additions and 0 deletions
+157
View File
@@ -0,0 +1,157 @@
# Состояние разработки НСИ (Ordinis ЦУОД ОДХ)
**Версия:** v0.5 (MVP+, anchor v1 — в процессе закрытия)
**Дата:** 2026-05-05
**Owner:** zimin.an@nstart.space
**Окружения:** staging (192.168.100.161 / cluster.265), prod (192.168.100.142 / cluster.142, hibernated)
---
## 1. Перечень требований
### 1.1. Реализовано (production-ready на staging)
| # | Требование | Статус | Где зафиксировано |
|----|--------------------------------------------------------------|--------|---|
| 1 | Каталог НСИ ЦУОД ОДХ — единый источник истины | ✅ | bundle `cuod` v1.1.0 |
| 2 | Bitemporal модель (валидное-время + транзакционное-время) | ✅ | `dictionary_records` + `valid_from`/`valid_to` + Hibernate Envers |
| 3 | JSON Schema валидация записей (Draft-07) | ✅ | `ordinis-validation` (networknt) |
| 4 | i18n поля (RU + EN) с patternProperties | ✅ | `x-localized: true` extension |
| 5 | Уникальные поля (бизнес-ключ + произвольные) | ✅ | `x-unique` extension + native query enforcement |
| 6 | Авто-derive businessKey из data поля | ✅ | `x-id-source` extension |
| 7 | История версий записи + diff | ✅ | `/records/{key}/history` + UI drawer |
| 8 | Schema versioning + breaking-change detection | ✅ | `diffSchemas` + Save-block + auto major/minor/patch |
| 9 | Аудит-лог (кто, когда, что, IP, trace_id) | ✅ | `audit_log` + `AuditLogger` + UI `/audit` + CSV export |
| 10 | Kafka outbox (at-least-once гарантия) | ✅ | `OutboxPoller` + DLQ cap (1000 attempts) |
| 11 | Idempotency для мутирующих запросов | ✅ | `Idempotency-Key` header + sha-256 hash |
| 12 | Multi-scope доступ (PUBLIC / INTERNAL / RESTRICTED) | ✅ | `DataScope` + JWT roles + Kafka per-scope topics |
| 13 | Keycloak OIDC (issuer-uri auto-discovery) | ✅ | `ordinis-auth` + `JwtDecoders.fromIssuerLocation` |
| 14 | Visual schema builder + CRUD UI | ✅ | admin-ui (React 19 + TanStack + @nstart/ui) |
| 15 | SSO для admin-ui (PKCE Public client) | ✅ | react-oidc-context |
| 16 | Bundle import (idempotent seed) | ✅ | `CuodBundleImporter` startup runner |
| 17 | Swagger UI / OpenAPI 3 spec | ✅ | springdoc на `/swagger-ui.html` |
| 18 | 7 справочников + 51 запись на staging | ✅ | spacecraft (14), satellite_type (5), ground_station (3), mission (9), instrument (11), processing_level (8), data_format (8) |
| 19 | Friendly error responses (409/400/404 вместо 500) | ✅ | `GlobalExceptionHandler` |
| 20 | Метрики Prometheus + actuator/health | ✅ | `ordinis_outbox_*`, `ordinis_kafka_*` |
| 21 | Тесты | ✅ | 24 backend + 51 frontend = **75 unit-тестов**, все зелёные |
### 1.2. В работе / required для v1 closure
| # | Требование | Статус | Блокер |
|----|-----------------------------------------------------------|--------------|--------|
| 22 | Real consumer integration (Альтум — заказ съёмки) | 🟡 в процессе | Альтум собирает BFF (FastAPI), ждём первого pull от них |
| 23 | DNS A-record + Let's Encrypt cert на prod | 🔴 | DNS не проброшен на 192.168.100.142, prod hibernated |
| 24 | `ORDINIS_AUTH_REQUIRED=true` на prod | 🔴 | Зависит от готовности SSO admin-ui на prod + Альтум |
| 25 | Distributed tracing (Tempo) на prod | 🔴 | Не развёрнут observability stack |
| 26 | CNPG backup в S3 + DR rehearsal | 🔴 | S3-endpoint не настроен |
| 27 | SLO + alerts (Prometheus) | 🔴 | Не описаны метрики |
| 28 | 30+ справочников ЦУОД ОДХ (full coverage) | 🟡 | Сейчас 7 — anchor-набор. Нужны: антенны, частотные диапазоны, орбиты, операторы, потребители, контракты, зоны покрытия, климатические зоны и т.д. |
| 29 | Operator runbook | 🟡 | Частично: docs/integration, docs/tech. Нет процедуры unseal Vault, поднятия hibernated prod, рестарта Kafka |
| 30 | e2e API тесты (testcontainers) | 🔴 | Только unit |
| 31 | Component тесты frontend (RTL) | 🔴 | Только pure-logic |
| 32 | Bundle code-splitting (~660KB gzip) | 🔴 | echarts/lottie/@nstart/ui тянут единый chunk |
| 33 | Vault auto-unseal | 🔴 | Критично после reboot ноды — обнаружено 2026-05-05 |
### 1.3. v2 (governance / data quality / federation)
Не блокирует prod stand-up. Плановый roadmap (~2-3 квартала):
- Approval workflow (draft → review → publish), ownership trees, RBAC per-dictionary
- Lineage / dependency graph
- Geo first-class (карта ввода/превью, bbox/polygon query)
- Cross-dictionary FK с cascade rules
- Data quality rules engine (cross-field constraints, ML-anomaly detection)
- Federation MDM-кластеров (master/replica)
- Schema registry (Apicurio/Confluent) для Kafka
- Webhooks для не-Kafka consumer'ов
---
## 2. Нерешённые технические вопросы
### 2.1. Критические для prod
1. **Vault auto-unseal.** 2026-05-05 после reboot ноды staging Vault sealed → 3 Spring Boot pod'а залипли в Init на 3+ минут. Любой prod reboot = downtime + ручной шаг (3 unseal-ключа из 5). Решения:
- (a) Transit secret engine из master Vault (рекомендуется при наличии)
- (b) Yandex Cloud KMS / Selectel KMS auto-unseal
- (c) Скриптованный manual + runbook (минимум для v1, мигрировать позже)
2. **Audience валидация JWT.** Сейчас не enforced — принимаем токены от любого клиента в realm `nstart`. Допускает scope-эскалацию через interactive client другого приложения. На prod нужно либо включить audience-check, либо доказать что Альтум client мапит `aud=ordinis` через Keycloak Audience Mapper.
3. **Outbox poll-interval 1 сек на prod.** При 100+ событиях/сек → 86k+ запросов к Postgres/день. Возможно нужен batch tuning или reactive listener вместо poll.
4. **Single-instance Postgres.** На staging и prod — CNPG cluster с `instances: 1`, реплика отсутствует. Failover нет. Для prod-нагрузки нужен `instances: 3` с PgBouncer.
### 2.2. Архитектурные открытые вопросы
5. **Realtime invalidation для consumer'ов.** TTL 5 мин кэша достаточен для v1, но при критичных операциях (закрытие КА за день до запуска) нужен push. Kafka topic `nsi.dictionary.changed` запланирован на v2 — критичность для consumer'ов оценивается.
6. **Геоданные.** В spacecraft.schema есть orbit-параметры, в ground_station — geometry (PostGIS). Но Альтум-задача (заказ съёмки) использует bbox/polygon — bitemporal-выборка по геометрии не реализована. Нужно подтверждение от ЦУОД что геопоиск нужен в v1 или v2.
7. **Multi-tenancy.** Сейчас `bundle: cuod` — anchor customer. Второй клиент → второй bundle, своя тройка Kafka топиков. Но шаринг справочников между bundle (например spacecraft общий для ЦУОД и геопортала) не определён архитектурно.
8. **Object storage / большие справочники.** Если справочник вырастет до 100k+ записей (например climate zones), JSONB в одной таблице может стать узким местом. Партиционирование `dictionary_records` по `dictionary_id` — потенциальное решение, нужна оценка.
### 2.3. Operational
9. **Vault-agent-init backoff.** ~3 минут при transient Vault unavailability — слишком долго. Нужен tuning либо переход на CSI Driver.
10. **JDK 25 + Mockito inline** несовместим. Перешли на subclass mock-maker. При апгрейде до Java 26 проверить.
11. **CI path-filter.** Был костыль, рассыпался при односторонних изменениях. Сейчас все docker jobs запускаются всегда (~30s extra на layer-cache). Можно вернуться к smart-filter когда GitLab variable interpolation в trigger jobs будет надёжной.
12. **OTel exporter logs/metrics 404.** Tempo на staging принимает только traces, logs/metrics endpoint отсутствует. Pod-логи замусориваются. Либо отключить logs/metrics export, либо поднять Loki/Mimir.
### 2.4. Compliance / правовые
13. **Audit retention policy.** Сейчас `audit_log` пишется бессрочно. По регламенту ЦУОД нужно определить срок хранения (5 лет? 10 лет?) и автоматическую архивацию.
14. **Personal data handling.** В audit_log пишется `user_id` (Keycloak sub) + IP. ФЗ-152 — нужна оценка / DPA если в realm будут юзеры — не-сотрудники.
15. **Электронная подпись изменений.** Если потребуется юридически значимая фиксация изменений справочников (КЭП оператора при close записи) — отдельный модуль, не реализован.
---
## 3. Наличие технической документации
### 3.1. Создана и актуальна
| Документ | Назначение | Расположение |
|---|---|---|
| **API integration guide** | Гайд для интеграторов (Альтум, геопортал): auth flow, endpoints, кэш-стратегии, bitemporal модель, businessKey'и | `docs/tech/api-integration.md` |
| **Альтум integration ТЗ** | Согласованный план интеграции «Заказ съёмки», схемы 4 справочников, BFF архитектура | `docs/integration/altum-imaging-order.md` |
| **OpenAPI 3 spec (Swagger)** | Машиночитаемая спецификация всех endpoints, можно генерить SDK | `https://ordinis.k8s.265.nstart.cloud/swagger-ui.html` + `/v3/api-docs` |
| **Bundle manifest + schemas** | JSON Schema каждого справочника + readable manifest | `ordinis-cuod-bundle/src/main/resources/bundles/cuod/` |
| **Архитектурные решения inline** | JavaDoc + конструктивные комментарии в ключевых классах: `ScopeContext`, `OutboxPoller`, `DictionaryRecordService`, `AuditLogger` | в коде |
| **CI/Helm values** | Параметризация per-env, CORS, ingress, image-tag override | `ordinis-infra/charts/ordinis/{values,values-staging,values-prod}.yaml` |
### 3.2. Отсутствует / требует написания
| Документ | Приоритет | Назначение |
|---|---|---|
| **Operator runbook** | 🔴 high | Procedure cards для типовых задач: unseal Vault, поднятие hibernated prod, рестарт Kafka StrimziPodSet, миграция БД, рестарт outbox-loop, разбор DLQ. Инцидент 2026-05-05 показал острую необходимость. |
| **DR plan** | 🔴 high | Backup → S3, RTO/RPO, процедура восстановления из CNPG snapshot |
| **Архитектурный обзор (HLA)** | 🟡 medium | Single-pager для нового тех-лида: модули, потоки данных, scope-фильтрация, bitemporal-логика — без необходимости читать код |
| **User docs / справка оператора НСИ** | 🟡 medium | Как создать справочник, как закрыть запись, как читать журнал аудита, как разобрать ошибку валидации схемы — для не-инженерных юзеров |
| **Миграционный гайд** | 🟢 low | Как мигрировать данные из существующих локальных справочников (Альтум, геопортал) в Ordinis bundle |
| **Threat model + security review** | 🟡 medium | OWASP-чеклист, audit роли, аудит trace-id, ограничения rate-limit на ingress |
| **CHANGELOG.md** | 🟢 low | Сейчас изменения видны только через git log. Для consumer'ов нужен structured changelog с breaking changes |
| **Релизная политика** | 🟢 low | semver для bundle vs schemaVersion vs Maven version. Сейчас не описана |
### 3.3. Inline-источники (читаются как docs)
- `git log --oneline ordinis-app/` — история архитектурных решений в commit messages
- JavaDoc / TSDoc на ключевых классах (~70% покрытия)
- TanStack Router file-based routing — структура UI самодокументируется
- Liquibase changelog'и в `ordinis-migrations/src/main/resources/db/changelog/changes/` — история DB-схемы
---
## Резюме
- **Функционально v0.5 MVP покрывает 21 из 33 требований v1**, недостающее — operational (observability, backup, runbook), а не feature-уровня.
- **Архитектурный фундамент готов** для масштабирования до 30+ справочников и 5+ consumer'ов.
- **Главный риск** — отсутствие auto-unseal Vault и DR-плана. Для prod stand-up — критично.
- **Документация интеграторов готова** (Swagger + 2 markdown). Документации для operations и end-users — недостаточно.
**Прогноз до prod-ready (v1):** ~2-3 недели при текущем темпе, при условии что Альтум-интеграция отвалидирована на staging до prod cutover.