docs(platform): align connector, EDP, and deploy canon
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
исполняемый connector code. `src/index.mjs` даёт dependency-free проверку
|
||||
минимальных v1 contracts; она запускается через `npm run check`.
|
||||
|
||||
Каждый L2 connector instance должен поставлять совместимые versioned
|
||||
Каждый NDC L2 connector instance должен поставлять совместимые versioned
|
||||
артефакты:
|
||||
|
||||
```text
|
||||
@@ -21,16 +21,47 @@ command-catalog (metadata only until a red command gateway is approved)
|
||||
```
|
||||
|
||||
`provider-manifest` — декларативный template-артефакт: provider ID, ontology
|
||||
revision, L2 template version, capability catalog и data-product IDs. Он не
|
||||
revision, NDC L2 template version, capability catalog и data-product IDs. Он не
|
||||
является tenant connection, не содержит endpoint/URL, credential reference,
|
||||
secret, ручной scope или executable provider code. Concrete non-secret
|
||||
connection profile принадлежит и версионируется вместе с конкретным L2
|
||||
workflow; Engine только привязывает к нему opaque credential reference и grant.
|
||||
connection profile принадлежит и версионируется вместе с конкретным NDC L2
|
||||
workflow; caller привязывает только opaque provider credential reference, а
|
||||
внутренний workload grant разрешает control plane.
|
||||
|
||||
## Versioned provider packages
|
||||
|
||||
`providers/<provider>/<major>` — устанавливаемая contract/data единица, а не
|
||||
runtime service и не custom node. Она объединяет manifest, auth-mode metadata,
|
||||
capability catalog, field policies, collection profiles, Data Product
|
||||
definitions, semantic mapping contracts, provider-neutral NDC L2 template
|
||||
descriptor и synthetic fixtures. Package не содержит tenant/account state,
|
||||
credential values или исполняемый provider daemon.
|
||||
|
||||
`instantiateL2Connection` создаёт произвольное число connection instances из
|
||||
одного immutable package. Каждый instance получает собственные tenant,
|
||||
connection и opaque provider credential reference, но не требует изменения
|
||||
Platform source. Внутренние workload bindings объявляются отдельно как
|
||||
`control_plane_managed` и не являются caller input. Канонический collection
|
||||
scope — `all_visible_to_credential`: выбранная
|
||||
read-capability передаёт все entities, которые provider возвращает для
|
||||
привязанного credential. Unit allowlist и provider-group filter на collection
|
||||
boundary запрещены; сортировка и видимость принадлежат Data Product consumer и
|
||||
Foundry.
|
||||
|
||||
Первый production-shaped package — `providers/gelios/v1`. Он фиксирует реальный
|
||||
Gelios REST `GET /api/v1/units` transport contract и точный output
|
||||
`fleet.positions.current.v1@1.0.0` с revision
|
||||
`ontology.map.moving_object.v1`. Все Data Product fields используют snake_case.
|
||||
Его optional `tokenLifecycle` точно описывает два provider-issued artifacts —
|
||||
`access` и `refresh`: request использует `access`, а refresh пока имеет режим
|
||||
`operator_managed`. Это metadata без secret values и без заявления о
|
||||
реализованном автоматическом refresh.
|
||||
|
||||
## Проверяемые v1 contracts
|
||||
|
||||
- `Connection` — provider instance, tenant scope и *ссылка* на credential в
|
||||
Engine. Любые token/secret/password-like поля запрещены.
|
||||
- `Connection` — provider instance, tenant scope и *ссылка* на provider
|
||||
credential в NDC L2 Credentials. Любые token/secret/password-like поля
|
||||
запрещены; publisher представлен system-managed declaration/status без ref.
|
||||
- `Collection Profile` — явная policy сбора. `manual` не может скрыто содержать
|
||||
polling interval; `realtime` требует interval не чаще одного раза в секунду.
|
||||
- `Data Product` — нормализованный versioned output с semantic types, полями и
|
||||
@@ -38,18 +69,18 @@ workflow; Engine только привязывает к нему opaque credenti
|
||||
- `Intake Batch` — canonical **scoped** record, который External Data Plane
|
||||
валидирует и сохраняет: source, contract revision, idempotency, restricted
|
||||
raw envelope и canonical facts. Его `source` содержит `providerId`,
|
||||
`tenantId` и `connectionId`. Writer-bound L2 request намеренно не является
|
||||
`tenantId` и `connectionId`. Writer-bound NDC L2 request намеренно не является
|
||||
готовым `Intake Batch`: Data Plane сначала materializes scope и лишь затем
|
||||
применяет этот contract. В canonical record нет provider field mapping,
|
||||
entity allowlist, token или renderer data. Inline `raw.payload` в v1
|
||||
запрещён: если нужна provenance-ссылка, connector передаёт restricted
|
||||
`raw.ref` вместе с hash. Отдельный raw-vault может быть добавлен только
|
||||
отдельным ADR и не становится частью L2 → Data Plane wire boundary.
|
||||
отдельным ADR и не становится частью NDC L2 → Data Plane wire boundary.
|
||||
- `NDC Foundry Binding` — адресует data product только в конкретную цепочку
|
||||
`Foundry Application → Page → approved slot`; `templateId` можно сохранить
|
||||
как дополнительную типизацию, но он не заменяет `applicationId` и `pageId`.
|
||||
В binding запрещены provider transport, endpoint и credential reference.
|
||||
- `Provider Manifest` — статическое описание L2 connector template, capability
|
||||
- `Provider Manifest` — статическое описание NDC L2 connector template, capability
|
||||
catalog и ontology/data-product contracts. Оно не может содержать tenant,
|
||||
connection, credential, secret или provider transport.
|
||||
|
||||
@@ -94,13 +125,14 @@ PostgreSQL `integer` `0..2147483647`. Эти ограничения нельзя
|
||||
|
||||
Manifest, connection/collection profiles, data-product definition и Foundry
|
||||
binding используют fail-closed allowed-key schemas. Неизвестные поля, а также
|
||||
secret-like имена или значения (включая `ndc_edpwb_`/`ndc_edprb_`) отклоняются
|
||||
secret-like имена или значения (включая `ndc_edpwb_`, `ndc_edprb_` и
|
||||
`ndc_edppr_`) отклоняются
|
||||
на общей границе.
|
||||
|
||||
Все private custom nodes NODE.DC поставляются package
|
||||
`platform/packages/n8n-nodes-ndc`. Их display name обязан начинаться с `NDC `,
|
||||
а runtime type — с `n8n-nodes-ndc.`; package называется строго
|
||||
`n8n-nodes-ndc`. Provider-specific adapters остаются L2 workflow logic и не
|
||||
`n8n-nodes-ndc`. Provider-specific adapters остаются NDC L2 workflow logic и не
|
||||
становятся custom nodes или ветками Data Plane. Эти инварианты проверяются
|
||||
package test.
|
||||
|
||||
@@ -110,76 +142,72 @@ declarative provider artifact и не runtime transport: команда соде
|
||||
application/page/binding/data-product projection и idempotency key, а право на
|
||||
операцию извлекается Foundry из отдельного opaque workload grant.
|
||||
|
||||
## Engine opaque credential sink
|
||||
## Native NDC L2 Credentials
|
||||
|
||||
`src/engine-credential-sink.mjs` задаёт dependency-free server-to-server v1
|
||||
границу для доставки трёх workload capabilities в Engine Credentials:
|
||||
Provider auth и внутренние workload capabilities используют один общий
|
||||
секретный boundary — native Credentials NDC L2, — но являются разными
|
||||
credential domains. После сохранения ядро владеет secret, а graph и MCP
|
||||
используют только opaque reference.
|
||||
|
||||
- `external-data-plane.writer` → точная нода
|
||||
`n8n-nodes-ndc.ndcDataProductPublish` / `ndcDataProductWriterApi`;
|
||||
- `external-data-plane.reader` → точная нода
|
||||
`n8n-nodes-ndc.ndcDataProductRead` / `ndcDataProductReaderApi`;
|
||||
- `foundry.binding` → точная нода
|
||||
`n8n-nodes-ndc.ndcFoundryBinding` / `ndcFoundryBindingApi`.
|
||||
Gelios выдаёт ровно access token и refresh token. Текущий credential type
|
||||
`httpBearerAuth` использует access token для HTTP request. Автоматический обмен
|
||||
refresh → access в текущем runtime не доказан и поэтому не заявлен: package
|
||||
фиксирует `refreshMode: operator_managed`. Название credential с текстом вроде
|
||||
`read access` является лишь локальной меткой; read-классификацию задают
|
||||
разрешённые endpoint/method в capability catalog и workflow policy, а не scope
|
||||
самого access token. В deployed Engine exact method/path policy ещё не
|
||||
подключена: текущая HTTP safe-ref граница проверяет host. Поэтому catalog
|
||||
classification остаётся декларативной до capability-bound Engine/MCP proof.
|
||||
|
||||
Provision request фиксирует `workflowId`, `workflowRevision`, `nodeId`, runtime
|
||||
node type, credential type, grant ID, expiry и issuer policy hash. Aggregate
|
||||
`transaction.policyHash` детерминированно считается по всему secret-free
|
||||
descriptor; подмена любой цели или policy ломает валидацию. Единственное поле,
|
||||
которое переносит plaintext capability, — `bindings[].material.value`; request
|
||||
нельзя писать в логи, traces, очередь или audit.
|
||||
Для общего Data Product transport используются `ndcDataProductWriterApi`,
|
||||
`ndcDataProductReaderApi` и `ndcFoundryBindingApi`. Это внутренние NDC
|
||||
capabilities, не Gelios tokens и не часть Gelios token lifecycle. Их значения
|
||||
не являются частью provider package и не передаются между workflow nodes как
|
||||
данные.
|
||||
|
||||
Provision и rollback envelopes действуют не более 15 минут: sink отклоняет
|
||||
истёкшие запросы и допускает максимум 60 секунд положительного clock skew.
|
||||
Receipt повторно проверяет freshness относительно собственного `processedAt`,
|
||||
чтобы старый запрос нельзя было применить или подтвердить через replay.
|
||||
Connection caller передаёт только provider credential reference. Publisher
|
||||
role остаётся credential binding для `NDC Data Product Publish`, но template
|
||||
маркирует его `management: control_plane_managed`: ни writer secret, ни writer
|
||||
reference не входят в connection parameters. Instantiation возвращает
|
||||
декларативный `systemBindings.publisher` с desired state и unresolved status;
|
||||
дальше его разрешает trusted control plane.
|
||||
|
||||
Каждый binding содержит `capabilityDigest = sha256(material.value)`: Engine
|
||||
самостоятельно хеширует полученный plaintext и сравнивает digest. Aggregate
|
||||
`policyHash` включает этот digest и issuer identity, а provision transaction
|
||||
несёт обязательную Ed25519 attestation. Engine принимает её только по
|
||||
allowlisted `serviceId:keyId`; заменить capability и пересчитать обычный hash
|
||||
без приватного issuer key невозможно.
|
||||
Canonical managed EDP writer capability генерируется и сохраняется внутри
|
||||
Engine credential boundary, а EDP получает только digest. Старые manual
|
||||
writer/reader и Foundry issuance paths могут возвращать capability один раз
|
||||
trusted control-plane caller и остаются отдельно закрытыми compatibility
|
||||
границами; MCP и пользователь получают только opaque reference/status.
|
||||
Запрещены password input
|
||||
как пользовательский acceptance-путь, graph parameters, connection/profile
|
||||
files, env, Ops, logs и traces.
|
||||
|
||||
Sink обязан выполнять `rollback-all`: сначала проверить весь request и точное
|
||||
состояние graph, затем создать credentials в staging, атомарно привязать весь
|
||||
набор и только после commit вернуть opaque `credentialRef`. При любой ошибке
|
||||
новые credentials удаляются, а прежние bindings остаются без изменений.
|
||||
`rollback-failed` означает карантин и ручное восстановление, но никогда не
|
||||
возвращает частичные credential refs.
|
||||
Broad credential sink/resolver/daemon и provider-specific provisioning
|
||||
запрещены. Требуется узкий generic `ensure data-product publish grant` contract
|
||||
с server-derived scope, idempotency, CAS/crash recovery и audit. Текущий
|
||||
deployed Engine MCP этой операции ещё не имеет; root/UI transfer допустим
|
||||
только как emergency self-hosted diagnostics, не как user journey.
|
||||
|
||||
Receipt, rollback request/receipt и audit имеют отдельные strict schemas. Они
|
||||
не способны вернуть capability material; audit хранит только hash opaque
|
||||
credential reference. Explicit rollback адресует committed transaction через
|
||||
`transactionId`, `policyHash` и hash committed receipt, поэтому не может
|
||||
случайно откатить другой набор. Реализация sink принадлежит Engine и не даёт
|
||||
Platform/Codex доступа к Engine core, runtime files или plaintext credential
|
||||
storage.
|
||||
|
||||
## Engine private-extension management
|
||||
## NDC L2 private-extension management
|
||||
|
||||
`src/engine-private-extension.mjs` задаёт строгую Platform-side границу для
|
||||
Engine-owned активации проверенного `n8n-nodes-ndc` release. Контракт не
|
||||
устанавливает package и не меняет Engine: он фиксирует async
|
||||
NDC L2-owned активации проверенного `n8n-nodes-ndc` release. Контракт не
|
||||
устанавливает package и не меняет NDC L2: он фиксирует async
|
||||
`plan -> apply receipt -> operation/status` protocol, где `apply` обязан
|
||||
быстро вернуть `queued` и `operationId`, а долгий recreate/acceptance
|
||||
отслеживается отдельно.
|
||||
|
||||
Активация и rollback требуют отдельной глобальной capability
|
||||
`engine.private-extension.manage`. Обычные L1/L2 grants её не дают. Чтение
|
||||
`engine.private-extension.manage`. Обычные NDC L1/NDC L2 grants её не дают. Чтение
|
||||
состояния допускает `engine.private-extension.read` или manage-capability.
|
||||
Запрос выбирает только allowlisted package, digest-bound `releaseId` и
|
||||
`packageSha256`; caller не передаёт host path, package bytes, Compose service,
|
||||
shell command или credential material. Plan живёт не более 15 минут, является
|
||||
single-use и применяется только с тем же idempotency key и plan hash.
|
||||
|
||||
Runtime transition для n8n 2.3.2 зафиксирован как community-package loader по
|
||||
`/home/node/.n8n/nodes/node_modules/n8n-nodes-ndc`, а не как
|
||||
`N8N_CUSTOM_EXTENSIONS` или `CUSTOM.*` loader:
|
||||
Runtime transition использует platform-managed community-package loader для
|
||||
immutable `n8n-nodes-ndc` release; private runtime paths и environment settings
|
||||
не являются частью публичного provider contract:
|
||||
|
||||
- `N8N_COMMUNITY_PACKAGES_ENABLED=true`,
|
||||
`N8N_COMMUNITY_PACKAGES_PREVENT_LOADING=false`,
|
||||
`N8N_REINSTALL_MISSING_PACKAGES=false`;
|
||||
- Deploy/Run quiesced and execution queue drained before the version switch;
|
||||
- read-only mount and atomic current/recovery state;
|
||||
- force-recreate main, every worker and every webhook instance as one version
|
||||
@@ -189,24 +217,25 @@ Runtime transition для n8n 2.3.2 зафиксирован как community-pa
|
||||
|
||||
Любая ошибка после switch запускает automatic rollback и повторный
|
||||
force-recreate/acceptance. Ошибка самого rollback переводит runtime в
|
||||
`quarantined`. Immutable release и существующие Engine Credentials
|
||||
`quarantined`. Immutable release и существующие NDC L2 Credentials
|
||||
сохраняются. Для первой активации предыдущим проверенным состоянием является
|
||||
`n8n-nodes-ndc.inactive/v1`: rollback в этот baseline удаляет package из
|
||||
loader surface, но не удаляет credentials.
|
||||
|
||||
Public Ops gateway уже умеет прозрачно передавать эти операции через
|
||||
`/engine/mcp`, если Engine реализует соответствующие MCP tools. Так как
|
||||
`/engine/mcp`, если NDC L2 реализует соответствующие MCP tools. Так как
|
||||
gateway имеет 30-second upstream timeout, side effect остаётся асинхронным;
|
||||
отдельный public REST proxy для management boundary не требуется.
|
||||
|
||||
Пример `examples/gelios-positions-current.v1.mjs` — provider-specific fixture
|
||||
без customer, tenant identity или credential material.
|
||||
`providers/gelios/v1/fixtures` содержит synthetic provider response и ожидаемый
|
||||
publish contract без customer, tenant identity или credential material.
|
||||
|
||||
## Ownership
|
||||
|
||||
- `platform/packages/external-provider-contract` — общий контракт и schemas.
|
||||
- NDC Agent L2 — provider API adapter: fetch, pagination, batching,
|
||||
semantic mapping, collection profile и ссылка на credential в Engine.
|
||||
- NDC L2 — provider API adapter: fetch, pagination, batching,
|
||||
semantic mapping, collection profile и ссылка на credential в NDC L2
|
||||
Credentials.
|
||||
- Platform External Data Plane — provider-neutral intake, raw retention,
|
||||
canonical facts, current/history projections и scoped read products. Он не
|
||||
знает provider fields, customer/business filters или renderer rules.
|
||||
@@ -222,46 +251,100 @@ gateway имеет 30-second upstream timeout, side effect остаётся ас
|
||||
## Mandatory connection boundary
|
||||
|
||||
`connection` принадлежит одному tenant/client context и содержит только
|
||||
ссылку на секрет, утверждённый capability scope, collection profile, field
|
||||
policy и retention policy. Во всех runtime records Data Plane сохраняет минимум
|
||||
ссылку на provider secret, утверждённый capability scope, collection profile,
|
||||
field policy и retention policy. Во всех runtime records Data Plane сохраняет минимум
|
||||
`tenant_id`, `connection_id`, `provider_id`, `observed_at`, `received_at` и
|
||||
provenance/version там, где это применимо.
|
||||
|
||||
Writer token — отдельный EDP runtime credential, а не поле `Connection`,
|
||||
profile, provider manifest или L2 graph.
|
||||
Provider credential reference текущего Gelios request адресует access token;
|
||||
наличие provider-issued refresh token описывается `tokenLifecycle`, но его value
|
||||
не входит в `Connection`. Writer capability — отдельный внутренний EDP runtime
|
||||
credential, а не Gelios token и не caller-supplied поле `Connection`, profile,
|
||||
provider manifest или NDC L2 graph. Connection artifact содержит только
|
||||
system-managed publisher declaration/status без credential reference.
|
||||
|
||||
## Scoped writer binding
|
||||
|
||||
Writer binding — EDP-owned runtime security state, а не versioned artifact
|
||||
provider manifest или connection profile. Он фиксирует `tenantId`,
|
||||
`connectionId`, `providerId`, `allowedDataProductIds`, active/revoked state и
|
||||
`expiresAt`. L2 не может редактировать binding или задавать его scope;
|
||||
`expiresAt`. NDC L2 не может редактировать binding или задавать его scope;
|
||||
изменение любого scope-поля либо TTL создаёт новый binding, а прежний binding
|
||||
можно только rotate/revoke.
|
||||
|
||||
Новый L2 вызывает `POST /internal/data-plane/v1/intake/writer-bound` с
|
||||
`Authorization: Bearer <writer-token>` и unscoped envelope. В `source`
|
||||
разрешён только `providerId`; `tenantId`, `connectionId` и
|
||||
`x-nodedc-*-id` headers запрещены. EDP проверяет token, binding, provider и
|
||||
`contract.dataProductId`, затем materializes immutable canonical scope и
|
||||
сохраняет обычный `Intake Batch`. Caller-provided scope отклоняется, а не
|
||||
доверяется и не объединяется с binding.
|
||||
Binding создаёт, сохраняет и привязывает только trusted control plane. Caller
|
||||
connection instance не принимает `writerCredentialRef`; publisher role в L2
|
||||
template является обязательным `control_plane_managed` binding requirement.
|
||||
|
||||
Plaintext writer token возвращается только trusted provisioner при создании
|
||||
или rotation. Provisioner помещает его непосредственно в opaque Engine
|
||||
credential, доступный назначенному L2. EDP хранит только hash token и binding
|
||||
metadata; token запрещён в connection/profile/manifest, L2 graph, logs/traces,
|
||||
raw payload, Foundry и UI.
|
||||
Новый NDC L2 вызывает
|
||||
`POST /internal/data-plane/v1/data-products/:dataProductId/publish` с opaque
|
||||
writer capability и envelope `nodedc.data-product.publish/v1`. В body есть
|
||||
только batch identity и canonical facts; `source`, provider, tenant,
|
||||
connection, ontology revision, product version и persistence policy запрещены.
|
||||
EDP проверяет binding и path `dataProductId`, materializes immutable scope и
|
||||
contract из server-owned registries, затем сохраняет canonical batch.
|
||||
Caller-provided scope отклоняется, а не доверяется и не объединяется с
|
||||
binding.
|
||||
|
||||
Provisioning routes принимают только отдельный secret file, созданный
|
||||
root-owned deploy runner:
|
||||
`/volume1/docker/nodedc-platform/secrets/external-data-plane-provisioner/token`.
|
||||
Он read-only монтируется только в EDP и позднее — в dedicated Engine
|
||||
provisioner, работающий под выделенным UID/GID `11006`; это не `.env` value,
|
||||
не `NODEDC_INTERNAL_ACCESS_TOKEN` и не provider credential. Пока generic Engine
|
||||
provisioner не развёрнут, `EXTERNAL_DATA_PLANE_PROVISIONING_ENABLED=false`, а
|
||||
create/rotate/revoke routes отвечают `503` и не делают fallback к shared
|
||||
internal bearer.
|
||||
Plaintext writer capability запрещена в provider package, connection/profile,
|
||||
NDC L2 graph, files, environment, logs/traces, raw payload, Foundry и MCP.
|
||||
В canonical пути она генерируется и сохраняется внутри native Engine credential
|
||||
boundary; EDP получает только SHA-256 digest, а workflow — только opaque
|
||||
reference/status. Старые ручные EDP create/rotate endpoints с one-time
|
||||
plaintext response остаются отдельно выключенным compatibility-механизмом и не
|
||||
являются пользовательским или acceptance-путём.
|
||||
|
||||
Legacy manual one-time issuance использует отдельный EDP-only runner-managed
|
||||
bearer. Digest-only managed ensure этот bearer не принимает: Engine подписывает
|
||||
каждый control-plane запрос отдельным Ed25519 service key, EDP хранит только
|
||||
публичный ключ. Оба пути имеют независимые флаги и по умолчанию выключены.
|
||||
Generic `ensure data-product publish grant` генерирует и сохраняет capability в
|
||||
native Engine credential boundary, передаёт EDP её digest и возвращает только
|
||||
opaque ref/status. Shared internal bearer и Gelios access/refresh token для
|
||||
этого запрещены. Deployed Engine MCP умеет читать
|
||||
opaque refs и bind только HTTP Request credentials; native
|
||||
`ndcDataProductWriterApi` к custom Publish node этим policy не привязывается.
|
||||
Поэтому ensure + exact private-node bind остаются реальным platform gap.
|
||||
Root/UI перенос — только аварийная диагностика.
|
||||
|
||||
Control-plane EDP boundary — idempotent
|
||||
`PUT /internal/data-plane/v1/writer-bindings/by-key/:bindingKey`. Engine создаёт
|
||||
capability внутри native credential boundary и отправляет только её SHA-256
|
||||
`capabilityDigest`, immutable scope, `generation` и expiry. Одинаковый
|
||||
`bindingKey + generation + request hash` возвращает тот же binding без нового
|
||||
secret; несовпадающий retry получает `409`. Rotation использует новую
|
||||
generation/credential, поэтому старый binding можно оставить активным до
|
||||
успешного node bind и затем явно revoke.
|
||||
Managed revoke выполняется идемпотентно по точным `bindingKey + generation`
|
||||
через managed-only route; для него не открывается legacy plaintext API.
|
||||
Managed route включается только через
|
||||
`EXTERNAL_DATA_PLANE_MANAGED_PROVISIONING_ENABLED`; legacy plaintext routes —
|
||||
через отдельный `EXTERNAL_DATA_PLANE_PROVISIONING_ENABLED`.
|
||||
|
||||
Managed auth wire contract —
|
||||
`nodedc.external-data-plane.managed-provisioner-request/v1`. Engine отправляет
|
||||
ровно по одному header:
|
||||
`x-nodedc-engine-service-id`, `x-nodedc-engine-key-id`,
|
||||
`x-nodedc-request-audience`, `x-nodedc-request-timestamp`,
|
||||
`x-nodedc-request-nonce`, `x-nodedc-content-sha256` и
|
||||
`x-nodedc-request-signature`. Timestamp — canonical UTC ISO с миллисекундами,
|
||||
nonce и signature — unpadded canonical base64url, body hash — lowercase hex
|
||||
SHA-256 от точных переданных bytes. Подписываются UTF-8 bytes результата
|
||||
`JSON.stringify` объекта с полями строго в порядке `schemaVersion`, `audience`,
|
||||
`serviceId`, `keyId`, `method`, `path`, `timestamp`, `nonce`, `bodySha256`.
|
||||
`method` и request target `path` (включая query) должны совпадать побайтно.
|
||||
Canonical identity: service `nodedc-engine`, key
|
||||
`engine-edp-managed-provisioner-v1`, audience
|
||||
`nodedc-external-data-plane.managed-provisioning.v1`. EDP проверяет bounded
|
||||
clock skew и одноразовый nonce в bounded fail-closed replay cache; bearer
|
||||
fallback для managed routes отсутствует, любой `Authorization` header на них
|
||||
отклоняется.
|
||||
|
||||
EDP импортирует только `@nodedc/external-provider-contract/data-plane`.
|
||||
Artifact/image allowlist для этого subpath содержит contract version, intake,
|
||||
publish/snapshot/patch validators и scrub policy; provider packages, mappings,
|
||||
fixtures и tests физически не входят в EDP runtime и не являются причиной его
|
||||
restart.
|
||||
|
||||
## Legacy intake migration
|
||||
|
||||
@@ -271,22 +354,22 @@ existing writers. Он принимает только canonical scoped `Intake
|
||||
`x-nodedc-tenant-id` и `x-nodedc-connection-id`. Writer token этот route не
|
||||
заменяет.
|
||||
|
||||
Для миграции: создать writer binding, один раз записать выданный token в Engine
|
||||
credential, переключить L2 на `/intake/writer-bound`, удалить tenant/connection
|
||||
из body и headers, проверить успешный intake, затем удалить у L2 legacy
|
||||
credential. Новый или migrated writer не должен fallback-иться на legacy route
|
||||
после ошибки writer-bound intake. Когда migrated все writers, legacy route и
|
||||
его shared-token access удаляются.
|
||||
Для миграции: Platform обеспечивает scoped writer binding и native credential,
|
||||
затем через Engine MCP новый workflow branch переключается на Data Product
|
||||
publish route, удаляет tenant/connection из body и headers и проверяет
|
||||
успешный publish. Существующий legacy workflow/credential остаётся неизменным,
|
||||
пока владелец отдельно не решит его вывести; новый writer не должен
|
||||
fallback-иться на legacy route после ошибки publish.
|
||||
|
||||
## Intake boundary
|
||||
|
||||
L2 отправляет в общий Data Plane `Intake Batch`, а не SQL-запрос в общие
|
||||
NDC L2 отправляет в общий Data Plane publish request, а не SQL-запрос в общие
|
||||
таблицы. Platform проверяет boundary и сохраняет raw/history/current
|
||||
projections; semantic mapping остаётся в L2. Список конкретных source IDs не
|
||||
projections; semantic mapping остаётся в NDC L2. Список конкретных source IDs не
|
||||
является частью connection scope: scope выбирает read-capability API, а
|
||||
visibility решает data-product consumer.
|
||||
|
||||
Inline `raw.payload` запрещён: L2 не может записать в Data Plane полный ответ
|
||||
Inline `raw.payload` запрещён: NDC L2 не может записать в Data Plane полный ответ
|
||||
provider-а или произвольную строку. Пока отдельный raw-vault не утверждён,
|
||||
batch либо не содержит `raw`, либо содержит только restricted `raw.ref` и hash.
|
||||
Secret-like keys и распознаваемые bearer/JWT/writer-token values в любом участке
|
||||
@@ -299,4 +382,6 @@ service делает sweep expired envelopes при старте и по рас
|
||||
Capabilities классифицируются как `read`, `metadata`, `write`, `destructive`
|
||||
или `unknown`. Только `read` и согласованные `metadata` могут попасть в
|
||||
collector. `write` и `destructive` остаются каталогизированными, но не имеют
|
||||
transport route в read adapter.
|
||||
transport route в read adapter. Эта классификация относится к разрешённому
|
||||
method/endpoint workflow, а не утверждает, что provider access token имеет
|
||||
read-only scope.
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# Gelios provider package v1
|
||||
|
||||
Это versioned contract/data package для первого живого provider connection в
|
||||
NODE.DC. Он не является runtime service, custom node, account configuration или
|
||||
хранилищем telemetry. Provider-specific request и mapping исполняются внутри
|
||||
изолированного NDC L2 workflow; Platform получает только provider-neutral Data
|
||||
Product.
|
||||
|
||||
Package фиксирует:
|
||||
|
||||
- manifest `gelios.provider.v1@1.0.0`;
|
||||
- точный provider token lifecycle: Gelios выдаёт `access` и `refresh`, а
|
||||
HTTP request использует `access`;
|
||||
- safe-read capability `gelios.units.current.read`
|
||||
(`GET https://api.geliospro.com/api/v1/units` with an opaque bearer
|
||||
credential);
|
||||
- dynamic scope `all_visible_to_credential` без unit/group allowlist;
|
||||
- realtime profile с интервалом 10 секунд и manual profile;
|
||||
- fail-closed field policy: неизвестные и dynamic fields отбрасываются до
|
||||
классификации, hardware IDs, phone/address, raw params и sensor payloads не
|
||||
публикуются;
|
||||
- mapping в `map.moving_object`;
|
||||
- стабильный source ID `gelios-unit-<sourceUnitId>` с точным сохранением
|
||||
строкового provider ID;
|
||||
- explicit derivation rules для `position_valid`, `operational_status` и
|
||||
`quality_flags`, включая `no_position` и stale threshold 60 секунд;
|
||||
- точный Data Product `fleet.positions.current.v1@1.0.0`, revision
|
||||
`ontology.map.moving_object.v1`, с 13 разрешёнными snake_case fields;
|
||||
- provider-neutral NDC L2 template descriptor из пяти boundary steps:
|
||||
trigger → request → extract → map → `NDC Data Product Publish`;
|
||||
- publish credential binding с `management: control_plane_managed`, без
|
||||
caller-supplied writer reference;
|
||||
- synthetic source/publish fixture, включая видимый объект без координат.
|
||||
|
||||
## Authentication boundary
|
||||
|
||||
Gelios выдаёт ровно два provider secret artifact: access token и refresh
|
||||
token. Это не отдельные read/write tokens. Текущий NDC L2 HTTP binding типа
|
||||
`httpBearerAuth` подставляет access token в `Authorization`; автоматический
|
||||
refresh этим runtime пока не доказан, поэтому package явно фиксирует
|
||||
`refreshMode: operator_managed`. Ни access, ни refresh value не попадает в
|
||||
package, graph, fixture, MCP, Ops или trace.
|
||||
|
||||
Название native credential, например `Gelios — Robot2B — read access`, является
|
||||
только операторской меткой. `read` в `gelios.units.current.read` —
|
||||
классификация разрешённого workflow endpoint `GET /api/v1/units`, а не scope
|
||||
access token. Метка credential не создаёт отдельный «read token» и не сужает
|
||||
права, которые фактически выдал Gelios.
|
||||
|
||||
Unit без last message не исчезает: mapping использует collection receive time
|
||||
как `observedAt` fallback и публикует subject без geometry с
|
||||
`position_valid=false`/`operational_status=no_position`.
|
||||
|
||||
Префикс source ID для v1 — строго `gelios-unit-`. Сокращённый `unit-` не
|
||||
является каноническим: он создал бы второй entity key рядом с уже сохранённым
|
||||
Gelios subject. Значение provider ID после префикса преобразуется только в
|
||||
строку; ведущие нули и остальные значимые символы не нормализуются.
|
||||
|
||||
`fleet.positions.current.v1` ограничен 5000 current entities. Если credential
|
||||
видит больше, NDC L2 не должен отбрасывать «лишние» units: package/profile
|
||||
требует остановить Deploy до новой partitioned Data Product revision. Полное
|
||||
автоматическое enforcement этого правила остаётся gate будущего
|
||||
package-to-graph compiler/runtime validator.
|
||||
|
||||
Один package обслуживает любое число accounts. Для каждого account вызывается
|
||||
`instantiateL2Connection` с новым `tenantId`, `connectionId`, provider
|
||||
credential reference. Это единственная credential reference, которую передаёт
|
||||
caller: она адресует Gelios access binding в native NDC L2 Credentials.
|
||||
Внутреннюю capability для публикации в External Data Plane выдаёт и сохраняет
|
||||
control plane; caller не передаёт writer secret или `writerCredentialRef`.
|
||||
Результат instantiation содержит только provider ref и декларативный
|
||||
`systemBindings.publisher` со статусом `unresolved`, который Platform должна
|
||||
разрешить в native credential binding. Эта capability не выдаётся Gelios, не
|
||||
является третьим Gelios token и не описывает права provider account. Добавление
|
||||
account не меняет Platform source.
|
||||
|
||||
Расширение Gelios происходит новой версией package: сначала подтверждаются
|
||||
provider capability/fields, затем обновляются ontology, field policy, mapping,
|
||||
fixtures и tests. Пользовательские workflow, которые уже покрываются
|
||||
существующим catalog, не требуют участия разработчика Platform.
|
||||
|
||||
Технический runtime type publish boundary остаётся
|
||||
`n8n-nodes-ndc.ndcDataProductPublish`; это идентификатор общего NDC package, а
|
||||
не Gelios-specific node.
|
||||
|
||||
## Текущая готовность
|
||||
|
||||
Package, ontology links и synthetic fixtures проверены в source. Production
|
||||
Engine L2 target использует зарегистрированный Gelios Bearer credential на
|
||||
точном safe-read endpoint и managed `ndcDataProductWriterApi` generation g3;
|
||||
оба binding записаны без раскрытия secret values. Canonical graph содержит
|
||||
provider read, extraction, mapping и native `NDC Data Product Publish`.
|
||||
|
||||
Fresh production execution `881722` завершился успешно и опубликовал
|
||||
`fleet.positions.current.v1`; managed writer g3 принят и имеет state `current`.
|
||||
Это доказывает manual provider read → canonical Data Product path. Внутренняя
|
||||
writer capability остаётся NODE.DC capability и не может быть заменена access
|
||||
или refresh token Gelios.
|
||||
|
||||
Не доказаны: автоматический refresh, Schedule Trigger 10 секунд, несколько
|
||||
последовательных scheduled cycles, retry/backoff, sampled history/retention на
|
||||
continuous path и Foundry snapshot/patch → moving pins E2E. Provider package
|
||||
остаётся declarative: MCP package discovery и package/profile → L2 graph
|
||||
compiler пока отсутствуют, поэтому первый graph собран через общие Engine MCP
|
||||
graph tools.
|
||||
Reference in New Issue
Block a user