# Состояние разработки НСИ (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.
