NODEDC_PLATFORM/docs/ADR_L2_OWNED_EXTERNAL_CONNE...

239 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.

# 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 не попадают.