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.
This commit is contained in:
Zimin A.N.
2026-05-07 23:27:03 +03:00
parent d5268b9395
commit bcfb07f547
23 changed files with 232 additions and 215 deletions
+38
View File
@@ -54,6 +54,12 @@ default:
.frontend-changes: &frontend-changes
- "ordinis-admin-ui/**/*"
# Documentation changes — Diplodoc-built integrator guide.
# docs-internal/ исключено явно: оно НЕ build'ится Diplodoc'ом и не должно
# триггерить docs job.
.docs-changes: &docs-changes
- "docs/**/*"
# ─── TEST ──────────────────────────────────────
maven-test:
stage: test
@@ -112,6 +118,38 @@ maven-package:
- ordinis-migrations/src/main/resources/db/changelog/**
expire_in: 1 day
# ─── BUILD: Integrator Guide (Diplodoc) ────────
# Build static HTML documentation для consumers через @diplodoc/cli (yfm).
# Source — docs/ (Markdown + toc.yaml). Output — docs/_dist/ HTML.
#
# Triggers:
# - auto при изменении docs/**
# - manual через web UI (для force-rebuild без code changes)
#
# Artifact `docs/_dist/` сохраняется неделю — pages-deploy job (TBD,
# когда host решён — GitLab Pages либо k8s ingress) подхватит из него.
build-docs:
stage: build
image: repo.nstart.cloud/library/node:20-alpine
before_script:
- corepack enable
- corepack prepare pnpm@9.0.0 --activate
script:
- cd docs
- pnpm install --frozen-lockfile
- pnpm build
- echo "Built $(find _dist -name '*.html' | wc -l) HTML pages, total size:"
- du -sh _dist
artifacts:
name: "ordinis-docs-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA"
paths:
- docs/_dist
expire_in: 1 week
rules:
- changes: *docs-changes
- if: '$CI_PIPELINE_SOURCE == "web"'
when: manual
# ─── PUBLISH (Docker images) ───────────────────
.docker_base:
stage: publish
+57
View File
@@ -0,0 +1,57 @@
# Ordinis — internal documentation
Технические детали реализации, архитектура, operator runbooks и project state
snapshots. **НЕ deployment'ится** через Diplodoc — это внутренние артефакты для
команды Ordinis (не для consumers).
Consumer-facing документация находится в [`docs/`](../docs/) и build'ится через
Diplodoc → static HTML.
## Что внутри
```
docs-internal/
├── ops/ # Operator runbook (on-call, incidents, recovery)
│ ├── README.md
│ ├── slo.md
│ ├── outbox-dlq.md
│ ├── db-migrations.md
│ ├── vault-unseal.md
│ └── kafka-restart.md
├── status/ # Project state snapshots (по дням сессий)
│ └── *-state.md
└── tech/ # Архитектурные deep-dives, legacy документы
└── api-integration.md # legacy (мигрирован в docs/integration/api/)
```
## Когда что использовать
| Situation | Read |
|---|---|
| Я — consumer интегрирующий свой backend | [docs/integration/api/](../docs/integration/api/index.md) (Diplodoc) |
| Я — on-call инженер с alert | [ops/README.md](ops/README.md) |
| Я — Ordinis dev планирующий feature | [status/](status/) latest snapshot + design docs в `~/.gstack/projects/claude/` |
| Я — новый разработчик в команде | Сначала consumer docs, потом ops/, потом status/ для context |
## Архитектурные artifacts (вне репозитория)
Design docs / план-плансы хранятся в `~/.gstack/projects/claude/`:
- `zimin-main-design-production-prep-20260507-123032.md` — prod stand-up plan
- `zimin-main-design-approval-workflow-v2-20260507-121632.md` — approval flow v2 (DEFERRED)
- `zimin-main-design-dict-relationships-v2-20260507-230237.md` — lineage + cascade rules
- `checkpoints/*.md` — session state checkpoints для context-restore
## Rationale разделения
Diplodoc документация — **public contract** для интеграторов:
- Может быть опубликована на public Pages
- Не должна leak'ать internal architecture (security)
- Должна быть стабильной (consumer'ы строят на ней свои интеграции)
`docs-internal/`**operational state**:
- Runbooks меняются часто (incidents, lessons learned)
- Status snapshots — historical record для команды
- Tech deep-dives — для разработчиков, не consumers
Это разделение делает Diplodoc более фокусированным и менее шумным
для consumers.
-1
View File
@@ -8,5 +8,4 @@ strict: false
ignore:
- node_modules
- _dist
- status/*-state.md
addSystemMeta: true
+54 -74
View File
@@ -1,42 +1,44 @@
# Ordinis docs (Diplodoc / YFM)
# Ordinis Integrator Guide (Diplodoc / YFM)
Документация Ordinis НСИ ЦУОД ОДХ построена через **Diplodoc** (open-source
documentation platform от Yandex). Markdown source — YFM (Yandex Flavored
Markdown) с расширениями для tabs, notes, includes, mermaid диаграмм.
Consumer-facing документация Ordinis НСИ ЦУОД. Build через **Diplodoc**
(open-source documentation platform от Yandex), source — YFM (Yandex
Flavored Markdown).
**Scope:** только то, что нужно интеграторам (Альтум team, BI, third-party
consumers). Operator runbooks, архитектурные deep-dives и project state —
во внутренней документации в [`../docs-internal/`](../docs-internal/),
не в Diplodoc build.
## Структура
```
docs/
├── package.json # @diplodoc/cli + scripts
├── .yfm # Diplodoc config (langs, plugins)
├── toc.yaml # Содержание / навигация (рендерится в sidebar)
├── index.md # Главная страница
── integration/ # Интеграция по API
├── api/ # Главы для consumers
│ ├── index.md # Обзор
│ ├── auth.md # Keycloak / JWT
│ ├── read-endpoints.md
│ ├── events.md # Kafka outbox
│ ├── webhooks.md
│ ├── bitemporal.md
│ ├── x-references.md
│ ├── caching.md
│ ├── errors.md
│ ├── best-practices.md
│ └── bundle-cuod.md
└── altum-imaging-order.md # Прикладной сценарий
├── ops/ # Operator runbook
│ ├── README.md
│ ├── slo.md
│ ├── outbox-dlq.md
│ ├── db-migrations.md
│ ├── vault-unseal.md
│ └── kafka-restart.md
── status/ # State snapshots (исключено из build)
│ └── *-state.md
└── tech/ # Legacy технические страницы
└── api-integration.md
├── package.json # @diplodoc/cli + build scripts
├── .yfm # Diplodoc config
├── toc.yaml # Содержание / sidebar
├── index.md # Главная (integrator-focused tabs)
── integration/
├── api/ # 11 глав про API integration
│ ├── index.md
│ ├── auth.md
│ ├── read-endpoints.md
│ ├── events.md
│ ├── webhooks.md
│ ├── bitemporal.md
│ ├── x-references.md
│ ├── caching.md
│ ├── errors.md
│ ├── best-practices.md
│ └── bundle-cuod.md
└── altum-imaging-order.md
```
Внутренние материалы (НЕ в Diplodoc build):
```
docs-internal/
├── ops/ # Operator runbook (on-call)
├── status/ # Project state snapshots
── tech/ # Legacy technical pages
```
## Сборка
@@ -48,62 +50,40 @@ cd docs
pnpm install
pnpm build # → ./_dist (HTML)
pnpm serve # → http://localhost:8088
# либо одной командой:
pnpm dev
pnpm dev # build + serve одной командой
```
Build output (`_dist/`) — статический HTML, можно деплоить в:
Build output (`_dist/`) — статический HTML, deployable в:
- GitLab Pages
- S3 + CloudFront
- nginx/ingress на cluster.142
- S3 / object storage
- nginx/ingress на k8s cluster
## Автор содержимого
## Editorial guidelines
| Раздел | Owner |
|---|---|
| `/` (index, integration/api/*) | zimin.an@nstart.space |
| `ops/*` | on-call rotation team |
| `integration/altum-imaging-order.md` | Альтум team contact |
Этот set документации — **public contract** для интеграторов:
## Что не committable
1. **Не leak'аем internal architecture** — никаких internal alert names,
service paths, internal infrastructure URLs.
2. **API contract стабилен** — изменения требуют semver bump + announcement.
3. **Consumer-side examples** для каждой концепции (Python, Node, либо Spring).
4. **Версионирование** через bundle / API major.
- `_dist/` — build output (gitignore'нут)
- `node_modules/` — dependencies (gitignore'нут)
## Deployment
В roadmap — GitLab CI job `build-docs`:
```yaml
build-docs:
stage: build
image: node:18
script:
- cd docs
- pnpm install --frozen-lockfile
- pnpm build
artifacts:
paths: [docs/_dist]
expire_in: 1 week
rules:
- changes: ["docs/**/*"]
```
Затем deploy в GitLab Pages либо как отдельный k8s deployment с nginx.
Для operator-side / dev-side материалов — `docs-internal/` либо design docs
в `~/.gstack/projects/claude/`.
## YFM extensions используются
- `{% list tabs %}` — табы для switching между audiences
- `{% note info|warning|tip %}` — выделенные блоки
- `mermaid` code blocks — диаграммы (graph LR, gantt)
- Cross-links в `toc.yaml` — sidebar navigation
- `varsPreset: external` — для shared includes (TBD when нужно)
- `toc.yaml` cross-links — sidebar navigation
## Roadmap
- [x] Базовая структура integration guide (11 страниц)
- [ ] CI job `build-docs` в `.gitlab-ci.yml`
- [ ] GitLab Pages deployment
- [ ] Search через Diplodoc search plugin
- [ ] EN translation (i18n via `langs:` в `.yfm`)
- [ ] GitLab Pages / k8s ingress deployment
- [ ] Search plugin
- [ ] EN translation (через `langs:` в `.yfm`)
- [ ] Includes для shared snippets (auth examples, error tables)
- [ ] Versioning — separate doc trees per Ordinis major version
- [ ] Versioning — separate doc trees per API major version
+11 -14
View File
@@ -1,6 +1,8 @@
# Ordinis НСИ ЦУОД ОДХ
# Ordinis НСИ ЦУОД ОДХ — Integrator Guide
Документация для разработчиков, интеграторов и операторов.
Документация для команд, интегрирующих свои сервисы с Ordinis НСИ ЦУОД.
Сюда не входят operator runbooks и архитектурные deep-dives — те материалы
во внутренней документации проекта.
**Anchor v1 — production LIVE на cluster.142** (с 2026-05-07).
Альтум consumers подключены, blue/green cutover завершён.
@@ -26,14 +28,10 @@
- [Bundle cuod](integration/api/bundle-cuod.md) — список 40 справочников ЦУОД
- [Best practices](integration/api/best-practices.md) — частые ошибки
- Я — оператор / on-call
- Я обрабатываю ошибки
- [Runbook indexed by symptom](ops/README.md)
- [SLO + alert thresholds](ops/slo.md)
- [Outbox DLQ](ops/outbox-dlq.md)
- [Database migrations](ops/db-migrations.md)
- [Vault unseal](ops/vault-unseal.md)
- [Kafka restart](ops/kafka-restart.md)
- [Ошибки и rate limits](integration/api/errors.md) — error codes, retry strategy
- [Лучшие практики](integration/api/best-practices.md) — robust integration
{% endlist %}
@@ -41,15 +39,14 @@
| Среда | URL | Auth | Назначение |
|---|---|---|---|
| **prod** | `https://ordinis.k8s.264.nstart.cloud/` | JWT обязателен | Альтум real users |
| **prod** | `https://ordinis.k8s.264.nstart.cloud/` | JWT обязателен | Real users |
| staging | `https://ordinis.k8s.265.nstart.cloud/` | optional (legacy mode) | Dev environment |
| local | `http://localhost:8080` | off | docker-compose dev |
## Поддержка
- Issues / bug reports: `git.nstart.cloud:2-6/2-6-4/terravault/ordinis`
- Эскалация: `zimin.an@nstart.space`
- Запросы новых ролей / scope в Keycloak: тот же email.
- Запросы новых ролей / scope в Keycloak: `zimin.an@nstart.space`
- Bug reports / feature requests: через issue tracker (запросить URL у Ordinis team)
## Версионирование
@@ -59,4 +56,4 @@ API стабилизирован под v1 — breaking changes только ч
Bundle справочников ЦУОД versioned отдельно (`cuod-bundle:1.2.1`). Изменение
schemas — minor bump, добавление словарей — minor bump, удаление — major bump
(никогда без уведомления consumers через `nsi.dictionary.changed` event).
(никогда без уведомления consumers через `record.closed` events).
+1 -1
View File
@@ -69,7 +69,7 @@ Ordinis использует трёхуровневый доступ к слов
Альтернативный синтаксис ролей (для compat с другими сервисами в realm):
суффикс `-PUBLIC|INTERNAL|RESTRICTED` (например `altum-PUBLIC`,
`geoportal-INTERNAL`). Маппинг определяется в `ordinis-auth/.../ScopeContext.java`.
`geoportal-INTERNAL`). Маппинг встроен в auth layer Ordinis.
{% note info %}
+3 -2
View File
@@ -77,8 +77,9 @@ graph LR
I --supportedLevelCodes--> PL
```
(Это subset, для full графа см. отдельные schemas в коде:
`ordinis-cuod-bundle/src/main/resources/cuod/schemas/`.)
(Это subset. Полный граф можно получить через `GET /dictionaries` +
inspection schemas через `GET /dictionaries/{name}` — каждый возвращает
JSON Schema с x-references маркерами.)
## Типичные сценарии
+6 -4
View File
@@ -24,7 +24,10 @@ useQuery({
})
```
### Server-side кэш (Caffeine + Spring)
### Server-side кэш (на consumer side)
Любая стандартная in-memory cache library с TTL — Caffeine, Guava,
Redis с EXPIRE и т.д. Пример (Spring + Caffeine):
```java
@Cacheable(value = "ordinis-records", key = "#dictionary + ':' + #lang")
@@ -170,14 +173,13 @@ referencing records. Consumer должен слушать events и invalidate
def on_event(event):
cache.delete(f"ordinis:{event['dictionary']}:byKey:{event['businessKey']}")
# NEW v2: cascade source — заодно invalidate dependents
# NEW v2: cascade source — заодно invalidate источник
if event.get('cascadeSource'):
# Источник тоже нужно invalidate (мы видим только закрытие dependent)
src = event['cascadeSource']
cache.delete(f"ordinis:{src['dictionary']}:byKey:{src['businessKey']}")
```
См. design doc `dict-relationships-v2` (внутренний artifact).
Spec появится перед deployment v2.
## Stale cache на 5xx / timeout
+8 -8
View File
@@ -97,9 +97,9 @@ Ordinis следует стандартам REST с расширениями:
| Code | Описание |
|---|---|
| `internal_unexpected` | Server bug — escalate с traceId |
| `db_unavailable` | Postgres недоступен |
| `vault_unavailable` | Vault sealed либо unreachable |
| `kafka_publish_failed` | Outbox не смог publish (но запись в БД OK — eventually consistent) |
| `db_unavailable` | БД недоступна — retry с long backoff |
| `dependent_unavailable` | Зависимый сервис (config, secrets) недоступен — retry |
| `event_publish_failed` | Запись в БД ОК, но события не отправлены — eventually consistent (events доедут позже) |
## Rate limits
@@ -114,9 +114,8 @@ limits через resource constraints:
| Concurrent JWT verifications | bounded heap (~5k/sec практический максимум) |
При превышении (типично DDoS-like patterns):
- HTTP `429 Too Many Requests` после ingress rate limit (ingress nginx default).
- HTTP `429 Too Many Requests` после ingress rate limit.
- HTTP `503 Service Unavailable` при exhausting connection pool.
- `5xx error rate spike` → alert `OrdinisReadApiErrorBudgetBurnFast`.
### Recommended client-side limits
@@ -155,13 +154,14 @@ def retry_with_backoff(
## Tracing
Каждый response имеет `X-Trace-Id` header и `traceId` field в error body.
Ordinis эмитит OpenTelemetry traces в Tempo (`tempo.monitoring:4317`).
При эскалации Ordinis team:
1. Скопируй `traceId` из response либо логов.
2. Создай issue с воспроизведением.
3. Ordinis team query'ит Tempo: `traceId="abc123-..."`.
4. Trace покажет full path: ingress → read-api → postgres → response.
3. Ordinis team поднимет trace по этому ID — full path запроса виден.
Без `traceId` investigation занимает существенно больше времени —
обязательно сохраняй его при ошибках.
## Дальше
+28 -56
View File
@@ -12,53 +12,27 @@ Ordinis публикует все изменения справочников в
| `ordinis.cuod.events.internal` | INTERNAL | INTERNAL словари — нужна role `ordinis-internal`+. |
| `ordinis.cuod.events.restricted` | RESTRICTED | RESTRICTED — role `ordinis-restricted`. |
Topology: 1 broker (single-node на cluster.142), 3+ partitions per topic
(historical setup имел 12 partitions, при cluster recreate 2026-05-07
spec задал 3; existing topics annotated `strimzi.io/managed=false`,
broker partitions сохранены), `cleanup.policy: delete`,
`retention.ms: 604800000` (7 дней).
Параметры топиков (для consumer config):
- `cleanup.policy: delete`
- `retention.ms: 604800000` (7 дней)
- Multi-partition (точное число — спросить Ordinis team при registration,
partition count может меняться по operational reasons).
## Подключение
### Адреса bootstrap
### Bootstrap address + creds
| Среда | Bootstrap servers | Auth |
|---|---|---|
| prod | `ordinis-kafka-kafka-bootstrap.ordinis-prod:9092` (TLS+SASL: `:9093`) | SCRAM-SHA-512 |
| staging | `ordinis-kafka-kafka-bootstrap.ordinis-staging:9092` | SCRAM-SHA-512 |
Запросить у Ordinis team:
- Bootstrap servers (внутренний DNS на per-environment basis)
- SASL credentials (SCRAM-SHA-512 username + password)
- Topic ACL (Read + Describe на нужные топики)
- Consumer group prefix (для group ACL)
### Получение SASL креденшелов
Auth: **SASL/SCRAM-SHA-512**. TLS опциональный (отдельный listener).
Запросить у Ordinis team создание Kafka user (Strimzi `KafkaUser` CR):
```yaml
apiVersion: kafka.strimzi.io/v1beta2
kind: KafkaUser
metadata:
name: altum-consumer
namespace: ordinis-prod
labels:
strimzi.io/cluster: ordinis-kafka
spec:
authentication:
type: scram-sha-512
authorization:
type: simple
acls:
- resource:
type: topic
name: ordinis.cuod.events.public
patternType: literal
operations: [Read, Describe]
- resource:
type: group
name: altum-consumer-
patternType: prefix
operations: [Read]
```
Strimzi user-operator создаст Secret `altum-consumer` с полями `username`,
`password`. Используй их в Kafka client config.
Креденшелы выдаются per-consumer service. Не share между сервисами —
ACL и audit за каждым.
## Schema события
@@ -143,18 +117,17 @@ each businessKey изолирован.
## At-least-once семантика
Outbox pattern гарантирует **at-least-once** delivery:
1. Writer пишет запись в БД + outbox table в одной транзакции.
2. OutboxPoller (scheduled @1s) читает unpublished rows, отправляет в Kafka.
3. После Kafka ACK — отмечает row как published (`published_at` set).
Ordinis использует **outbox pattern** — изменение БД и публикация в Kafka
атомарны через transactional outbox. Гарантия: **at-least-once**.
Что это значит для consumer'а:
- Можешь получить **дубликат** события если poller crashed после ACK но до
`published_at` write. Idempotency у consumer'а — must.
- При network partition между Kafka и poller — сообщения накапливаются в
outbox (alert `OrdinisOutboxLagHigh` срабатывает > 100 events).
- Можешь получить **дубликат** события (rare, при partial failures на
стороне Ordinis). Idempotency у consumer'а — must.
- При недоступности Kafka события накапливаются в Ordinis side и
доставляются после восстановления — **events не теряются**.
- Order — гарантирован per partition key (`dictionary:businessKey`),
не глобально.
## Idempotency
@@ -167,13 +140,12 @@ if not seen:
redis.setex(f"ordinis:event:...", 86400, "1")
```
## DLQ (Dead-letter queue)
## Persistent failures (Ordinis side)
Если событие не публикуется > 1000 retry попыток (network issues,
Kafka unavailable) — переходит в DLQ table в Ordinis. Не теряется, но
требует ручного разбора.
Симптом: alert `OrdinisOutboxDLQNonEmpty` → check `/api/v1/admin/audit?action=outbox.dlq`.
Если событие не публикуется > 1000 retry попыток (multi-day Kafka outage),
оно складывается в DLQ внутри Ordinis. Не теряется, но требует ручного
разбора Ordinis team. Consumer не видит таких событий до их
re-publishing — но это очень редкий сценарий.
## Топик для discovery
+7 -18
View File
@@ -50,8 +50,7 @@ Webhook URLs валидируются перед отправкой:
- **Запрещено:** non-HTTPS scheme. Только `https://`.
- **Запрещено:** URL > 2048 chars.
Метрика `nsi_webhook_ssrf_rejected_total` increments при попытках. См.
prometheus rule `OrdinisWebhookSsrfRejected`.
При нарушении registration возвращает 422 + список причин.
## Payload
@@ -155,10 +154,11 @@ Consumer должен ответить `2xx` в течение 10 секунд:
| 4 | через 10 мин |
| 5 | через 1ч |
| 6-10 | каждые 6ч |
| 1000+ | DLQ → ручной разбор |
| После всех попыток | Ordinis team помечает delivery как failed для ручного разбора |
После 1000 неудач delivery попадает в DLQ (`/api/v1/admin/webhooks/deliveries/dlq`).
Alert: `OrdinisWebhookDLQNonEmpty`.
Если consumer недоступен > 1000 retry попыток — Ordinis team будет
расследовать. Долгосрочное unavailability стоит coordinate (planned
maintenance window) чтобы не накапливать orphan deliveries.
## Test ping
@@ -196,19 +196,8 @@ Response:
сложить в очередь.
3. **HMAC verify первым** — до парсинга JSON.
4. **Metrics** — track received / processed / errors для своего side.
Сравнивай с Ordinis metrics при расследовании.
5. **Whitelist Ordinis IP** на firewall если строгие правила. IP cluster.142
= `192.168.100.142`, но в production будет ingress IP. Спросить у Ordinis
team при registration.
## Alerting (Ordinis side)
| Alert | Trigger |
|---|---|
| `OrdinisWebhookHighFailureRate` | failure rate > 50% за 5 мин |
| `OrdinisWebhookDLQNonEmpty` | events в DLQ > 0 |
| `OrdinisWebhookDispatcherIdle` | 0 deliveries при > 0 active subscriptions |
| `OrdinisWebhookSsrfRejected` | SSRF guard сработал > 0 раз за 15 мин |
5. **Whitelist Ordinis IP** на firewall если строгие правила. Точный
адрес запросить у Ordinis team при registration.
## Дальше
+18 -16
View File
@@ -67,7 +67,7 @@ PUBLIC scope, ReferenceValidator возвращает тот же error
(`x_references_target_not_found`) что и на real not-found. Ordinis **не leak'ает**
existence of out-of-scope dictionary через differential errors.
## Orphan reference scanner
## Orphan references
Возможна ситуация когда referenced record **закрывается**, а referencing
record остаётся:
@@ -78,19 +78,20 @@ record остаётся:
3. spacecraft.RESURS-P-3 теперь имеет dangling FK
```
Periodic scanner (`OrphanReferenceScanner`, runs каждые 5 мин) находит
такие orphan'ы и emit'ит metric:
Ordinis периодически детектирует такие orphan'ы серверной стороной — данные
**не корректируются автоматически** (может быть intentional state до
update referencing записей), но Ordinis team получает уведомление и
может coordinate fix с data stewards.
```
nsi_orphan_references_total{source_dict="spacecraft",target_dict="satellite_type"} 1
```
Alert `OrdinisOrphanReferences` срабатывает при > 0 за 10 мин.
Consumer-side defensiveness:
- При получении event `record.closed` для записи на которую ты ссылаешься —
invalidate свой cache + либо fetch свежую версию referencing
записи (если она тоже обновилась), либо handle dangling FK явно.
## Cascade rules — roadmap (v2)
Сегодня закрытие источника НЕ closes referencing automatically. Это
дolжно явно делаться через bulk close либо individual updates.
дoлжно явно делаться через bulk close либо individual updates.
В v2 планируется schema extension `x-references-on-close`:
@@ -106,21 +107,22 @@ Alert `OrdinisOrphanReferences` срабатывает при > 0 за 10 мин
Опции:
- **block** — закрытие источника невозможно пока есть active referencing (default).
- **warn** — закрытие проходит, в response warning + metric `nsi_cascade_warnings_total`.
- **cascade** — все referencing закрываются в той же транзакции.
- **warn** — закрытие проходит, в response warning.
- **cascade** — все referencing закрываются в той же транзакции, отдельные
events emit'ятся per record.
См. design doc `dict-relationships-v2` (внутренний artifact в
`~/.gstack/projects/claude/`) для полного scope.
Конкретный timeline и финальная семантика будут опубликованы перед
deployment v2 — следи за анонсами от Ordinis team.
## Lineage discovery — roadmap (v2)
Сегодня нет endpoint для "что depends on X". Admin должен вручную проходить
schemas чтобы найти ссылки. В v2 планируется:
Сегодня нет endpoint для "что depends on X". В v2 планируется:
- `GET /api/v1/dictionaries/{name}/dependents` — словари которые ссылаются.
- `GET /api/v1/records/{dict}/{key}/dependents` — конкретные записи.
См. design выше для подробностей.
Это позволит consumer'ам и UI узнавать связи без чтения всех schemas
руками. Spec будет опубликован перед deployment v2.
## Best practices
+1 -21
View File
@@ -1,4 +1,4 @@
title: Ordinis НСИ ЦУОД ОДХ
title: Ordinis НСИ ЦУОД ОДХ — Integrator Guide
href: index.md
items:
@@ -34,23 +34,3 @@ items:
items:
- name: Альтум — заказ съёмки
href: integration/altum-imaging-order.md
- name: Operations
items:
- name: Runbook (индекс)
href: ops/README.md
- name: SLO
href: ops/slo.md
- name: Outbox DLQ
href: ops/outbox-dlq.md
- name: Database migrations
href: ops/db-migrations.md
- name: Vault unseal
href: ops/vault-unseal.md
- name: Kafka restart
href: ops/kafka-restart.md
- name: Архитектура (legacy)
items:
- name: API integration (старая страница, см. /integration/api/)
href: tech/api-integration.md