Files
mdm-ordinis/docs-internal/status/2026-05-05-state.md
T
Zimin A.N. bcfb07f547 docs: split integrator guide (Diplodoc) vs internal docs + add build-docs CI
Чёткое разделение audiences:
- docs/ — consumer-facing only, Diplodoc-built. Public contract для
  интеграторов (Альтум, BI, third-party).
- docs-internal/ — operator runbooks, status snapshots, legacy tech pages.
  НЕ в Diplodoc build, НЕ публикуются.

Изменения:
- Move docs/{ops,status,tech} → docs-internal/{ops,status,tech}
- docs/toc.yaml: убраны секции Operations, Архитектура (legacy), Status
- docs/index.md: убран tab "оператор / on-call", focus на integrators
- docs/README.md: rewritten — scope только integrator guide, editorial
  guidelines (no leak internal architecture, stable API contract)
- .yfm: убран явный ignore status/* (теперь не нужно — папка переехала)
- docs-internal/README.md: index для внутренней документации с rationale
  разделения

Scrubbing implementation details из Diplodoc страниц:
- events.md: убраны Strimzi/cluster.142 references, OutboxPoller детали,
  internal alert names (OrdinisOutboxLagHigh, OrdinisOutboxDLQNonEmpty),
  metric names (nsi_outbox_*). Заменены на consumer-observable contract.
- webhooks.md: убраны metric names (nsi_webhook_ssrf_rejected_total),
  alert table (OrdinisWebhookHighFailureRate и т.д.). Retry policy
  переписана на user-side perspective.
- errors.md: убран alert name OrdinisReadApiErrorBudgetBurnFast, Tempo
  endpoint URL, internal connection details.
- x-references.md: OrphanReferenceScanner → general "Ordinis периодически
  детектирует". Убраны metric/alert names. Cascade roadmap без link на
  internal design doc.
- auth.md: убран file path ScopeContext.java.
- bundle-cuod.md: убран Maven module path.
- caching.md: cascade design link заменён на text reference.

CI:
- .gitlab-ci.yml: новый job build-docs (stage: build) — Node 20 alpine,
  pnpm install --frozen-lockfile + pnpm build, artifact docs/_dist на
  неделю. Triggers: auto на docs/**, либо manual web. Готов к follow-up
  pages-deploy job когда host решён.
- Новый pattern .docs-changes для rules.

Build verified clean: 13 HTML pages, 6.9M, no broken links/refs.
2026-05-07 23:27:03 +03:00

158 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Состояние разработки НСИ (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.