239 lines
16 KiB
Markdown
239 lines
16 KiB
Markdown
# ADR: внешние коннекторы принадлежат NDC L2 workflow
|
||
|
||
Статус: **accepted**.
|
||
Дата: 2026-07-16.
|
||
Владелец решения: NODE.DC Platform.
|
||
|
||
## Контекст
|
||
|
||
NODE.DC состоит из двух разных слоёв:
|
||
|
||
- платформенный слой даёт NDC L1, NDC L2, Ontology, credentials, Data Products
|
||
и Foundry-boundaries;
|
||
- пользовательские автоматизации собирают из этих возможностей конкретную
|
||
бизнес-логику.
|
||
|
||
Новый account, provider credential, tenant, расписание или интерфейс не должны создавать
|
||
новый platform service и не должны требовать изменения NODE.DC source. Команда
|
||
NODE.DC подключается только для нового поставщика либо для расширения
|
||
подтверждённых capabilities и ontology уже поддержанного поставщика.
|
||
|
||
Gelios — первый живой источник и acceptance-кейс этого канона, а не отдельная
|
||
архитектурная ветка. Документ заменяет provider-runtime решения из
|
||
`ADR_EXTERNAL_PROVIDER_DATA_PLANE.md` и `ADR_GELIOS_DATA_PLANE.md`; те
|
||
документы остаются только историей решения.
|
||
|
||
## Решение
|
||
|
||
### 1. Пакет поставщика создаётся один раз
|
||
|
||
Каждый поддержанный поставщик получает один версионируемый **provider package**.
|
||
Он является знанием платформы о внешнем API и содержит:
|
||
|
||
- стабильный `providerId` и версии подтверждённого API;
|
||
- credential contract без значения секрета;
|
||
- каталог safe-read capabilities, pagination/rate-limit metadata и response
|
||
shapes;
|
||
- mappings из provider fields в версии Ontology;
|
||
- совместимые Data Products и scrubbed contract fixtures;
|
||
- явно отделённый каталог команд без активного command transport.
|
||
|
||
Пакет не является runtime service, scheduler, базой данных или customer
|
||
configuration. Он расширяется только когда фактически подтверждён новый API,
|
||
новый тип данных или новая ontology revision.
|
||
|
||
Одна учётная запись поставщика задаётся данными, а не кодом:
|
||
|
||
`provider package + credential reference + connection profile + NDC L2 workflow instance`
|
||
|
||
Поэтому второй account или другая provider credential pair создаёт ещё один credential/profile и
|
||
workflow instance. Платформенный source, Data Plane и Foundry при этом не
|
||
меняются.
|
||
|
||
### 2. NDC L1 проектирует, NDC L2 workflow исполняет
|
||
|
||
NDC L1 получает пользовательское намерение, проверяет доступные capabilities и
|
||
Ontology, проектирует или изменяет разрешённый NDC L2 workflow через NDC MCP и
|
||
анализирует execution evidence.
|
||
|
||
Один изолированный NDC L2 workflow представляет один connection instance и
|
||
владеет исполняемой механикой:
|
||
|
||
- safe-read вызовами provider API через credential reference;
|
||
- pagination, cursor, retry, rate limit, batch size и collection cadence;
|
||
- проверкой response shape и разбиением ответа на items;
|
||
- mapping к точной ontology revision;
|
||
- idempotency, watermark и публикацией canonical facts;
|
||
- private workflow state, когда он действительно нужен.
|
||
|
||
Provider-specific mapping сначала реализуется штатными workflow nodes и малым
|
||
Code-преобразователем. Это позволяет проверить реальный API без преждевременного
|
||
создания custom nodes. В custom NDC nodes переносится только повторившаяся и
|
||
доказанная **платформенная boundary-механика**, а не уникальная логика
|
||
поставщика.
|
||
|
||
### 3. Credentials являются данными connection instance
|
||
|
||
Gelios выдаёт ровно два provider secrets: access token и refresh token. Текущий
|
||
HTTP request binding `httpBearerAuth` использует access token. Автоматический
|
||
refresh в принятом runtime не доказан, поэтому provider package фиксирует его
|
||
как `operator_managed`; ни один из token values не попадает в graph, package,
|
||
Ontology, Data Plane, Foundry, execution logs, MCP или Ops. Новый account или
|
||
provider-issued token pair означает новые native credential records и
|
||
connection instance, но не новую Platform-сущность.
|
||
|
||
Локальное имя credential (например, с суффиксом `read access`) не является
|
||
provider scope. В target-контракте Read-классификацию задаёт allowlisted
|
||
method/endpoint в capability catalog и Engine workflow policy; тот же access
|
||
token нельзя называть отдельным «read token» или «write token» только из-за
|
||
label. Сейчас deployed Engine safe-ref policy ограничивает generic HTTP
|
||
credential только по host, но ещё не связывает его с package/version и exact
|
||
method/path. Поэтому Gelios `GET /api/v1/units` пока является декларативной
|
||
capability, а не завершённой runtime-security гарантией; canonical acceptance
|
||
требует capability-bound Engine policy и её MCP proof.
|
||
|
||
Writer capability для публикации Data Product — отдельный внутренний native NDC
|
||
L2 credential, не третий Gelios token. Generic Engine/Platform control plane
|
||
генерирует capability внутри native credential boundary, сохраняет её там же и
|
||
передаёт EDP только SHA-256 digest вместе с точным provider, connection и
|
||
набором Data Products. Пользователь и MCP получают только opaque
|
||
reference/status. Capability не записывается в graph, provider package, env,
|
||
file, Ops или trace и не копируется через пользовательский UI.
|
||
|
||
Широкий credential sink, resolver/daemon с произвольной записью secret и любой
|
||
provider-specific credential service запрещены. Нужна одна узкая операция
|
||
`ensure data-product publish grant`: scope выводится из granted L1→L2 target,
|
||
зарегистрированного connection profile и разрешённого Data Product; issuance,
|
||
rotation и native binding должны быть idempotent, CAS/crash-safe и auditable.
|
||
Engine генерирует capability внутри credential boundary и передаёт EDP только
|
||
SHA-256 digest через idempotent binding key + generation; EDP не возвращает
|
||
plaintext. Новая generation создаётся до переключения node reference, а старая
|
||
отзывается только после успешного bind/acceptance.
|
||
Старые ручные EDP `POST/rotate` endpoints, которые возвращают capability один
|
||
раз, включаются отдельным legacy-флагом и не входят в canonical acceptance.
|
||
Digest-only managed endpoint имеет независимый флаг и принимает только
|
||
Ed25519-signed Engine service requests: exact audience/method/request target/raw
|
||
body hash входят в подпись, timestamp ограничен по skew, nonce защищён bounded
|
||
fail-closed replay cache. Legacy provisioner bearer на managed routes не
|
||
действует. EDP получает только deployment public key; matching private key
|
||
остаётся только внутри Engine server boundary. Endpoint остаётся выключенным,
|
||
пока key provisioning, Engine signer и exact native credential binding policy
|
||
не пройдут runtime acceptance.
|
||
В deployed Engine MCP этой операции пока нет — это текущий platform gap перед
|
||
canonical publish proof, а не действие пользователя. Это решение не заявляет
|
||
автоматический refresh Gelios: до отдельного runtime proof он остаётся
|
||
`operator_managed`.
|
||
|
||
### 4. External Data Plane нейтрален к поставщику
|
||
|
||
Platform предоставляет один versioned External Data Plane. Он принимает
|
||
canonical facts через scoped writer binding, хранит current/history
|
||
projections и публикует snapshot/patch contracts. В нём запрещены:
|
||
|
||
- ветвления по provider, customer, account, entity или renderer;
|
||
- provider field mapping и бизнес-фильтрация;
|
||
- provider credential, endpoint, schedule или command transport;
|
||
- caller-supplied tenant/connection scope.
|
||
|
||
`NDC Data Product Publish` отправляет только
|
||
`nodedc.data-product.publish/v1`. External Data Plane materializes immutable
|
||
scope из writer binding, проверяет разрешённый Data Product и его ontology
|
||
revision, затем сохраняет batch.
|
||
|
||
EDP runtime импортирует только package subpath
|
||
`@nodedc/external-provider-contract/data-plane`. Его deploy artifact и image
|
||
содержат только provider-neutral wire validators; `providers/gelios`, mappings,
|
||
fixtures и tests туда не входят. Изменение или добавление provider package не
|
||
пересобирает и не перезапускает EDP: каталог поставщиков разворачивается через
|
||
Engine/Ontology/control-plane путь отдельно.
|
||
|
||
Collection cadence, history cadence и presentation cadence независимы:
|
||
|
||
- NDC L2 забирает источник с частотой, нужной бизнес-задаче;
|
||
- current projection принимает каждое валидное изменение;
|
||
- declarative history policy может хранить все точки или sampling;
|
||
- Foundry читает snapshot+patch и отдельно ограничивает частоту render.
|
||
|
||
Для разной частоты БД и интерфейса не создаются второй provider sink, отдельный
|
||
gateway или параллельный прямой push в renderer.
|
||
|
||
### 5. Полнота означает все сущности разрешённой capability
|
||
|
||
Connection profile выбирает safe-read capabilities, а не зашитый в Platform
|
||
список entity IDs. Если разрешённый endpoint возвращает все доступные credential
|
||
сущности, NDC L2 обрабатывает каждый валидный item. Новый объект появляется
|
||
автоматически.
|
||
|
||
Пользовательская фильтрация, слои и видимость находятся после сбора — в
|
||
automation/Data Product/Foundry. Технические ограничения допустимы только как
|
||
pagination, quota, batch size, backpressure и защита от повреждённого ответа.
|
||
|
||
### 6. Custom NDC nodes ограничены платформенными границами
|
||
|
||
Первый канонический набор:
|
||
|
||
- `NDC Data Product Publish` — NDC L2 → External Data Plane;
|
||
- `NDC Data Product Read` — scoped snapshot/patch read;
|
||
- `NDC Foundry Binding` — control-plane связь Data Product с
|
||
`Application → Page → typed slot`.
|
||
|
||
Эти nodes не содержат provider ID, tenant ID, connection ID, endpoint, token,
|
||
mapping или cadence. Runtime package сохраняет технический namespace
|
||
`n8n-nodes-ndc.*`, но пользовательские документы и интерфейсы используют
|
||
только терминологию NDC.
|
||
|
||
`NDC Foundry Binding` не транспортирует каждый realtime tick. Он создаёт
|
||
постоянную связь интерфейса с Data Product; Foundry затем читает его
|
||
snapshot+patch contract.
|
||
|
||
### 7. Реальный Gelios acceptance-кейс
|
||
|
||
Первая версия provider package должна доказать путь:
|
||
|
||
`Gelios safe read → все доступные units → map.moving_object facts →
|
||
fleet.positions.current.v1 → Foundry map`
|
||
|
||
Canonical product использует ontology revision
|
||
`ontology.map.moving_object.v1` и только объявленные snake_case поля. NDC L2
|
||
не передаёт caller scope и не собирает собственный batch envelope вокруг
|
||
`NDC Data Product Publish`.
|
||
|
||
Acceptance выполняется по порядку:
|
||
|
||
1. Platform выполняет generic `ensure data-product publish grant`, выпускает
|
||
scoped EDP binding и атомарно сохраняет capability в native NDC L2
|
||
Credentials;
|
||
2. NDC MCP видит только совместимый opaque writer credential reference/status;
|
||
3. применить подтверждённый graph patch без legacy intake;
|
||
4. validate/preflight;
|
||
5. один успешный manual run и проверка execution/trace/Data Product;
|
||
6. только затем отдельным изменением добавить Schedule Trigger;
|
||
7. после доказанного snapshot+patch подключить Foundry binding.
|
||
|
||
### 8. Capacity и topology
|
||
|
||
Количество одновременно активных NDC L2 проектов ограничивается фактическими
|
||
ресурсами железа и профилем нагрузки. Пока capacity проверяется оператором и не
|
||
автоматизируется. Канон не объявляет отдельные worker/webhook generations или
|
||
high-load topology, которых ещё нет в принятом runtime.
|
||
|
||
## Последствия
|
||
|
||
- `services/<provider>-gateway` не является частью канона и не создаётся для
|
||
новых provider integrations. Существующий self-hosted Gelios Gateway и его
|
||
Timescale volume остаются frozen compatibility contour, воспроизводятся из
|
||
source/deploy и не изменяются новым L2 pilot без отдельного решения.
|
||
- Новый account или provider-issued access/refresh pair — configuration change,
|
||
а не platform release.
|
||
- Новый provider или неподдержанная capability — versioned provider/Ontology
|
||
change с contract tests.
|
||
- Существующий legacy L1/SDK workflow является frozen compatibility boundary:
|
||
новый L2 pilot его не редактирует, не отзывает его credential и не ставит его
|
||
вывод условием acceptance.
|
||
- Legacy `/internal/data-plane/v1/intake` остаётся выключенным migration-only
|
||
route и не используется новыми NDC L2 workflows.
|
||
- Команды устройствам остаются отдельным red-domain контуром с явным
|
||
подтверждением, scope, idempotency и аудитом.
|
||
- Foundry развивается после доказанного Data Product path; provider API и
|
||
credentials в Foundry не попадают.
|