docs(platform): align connector, EDP, and deploy canon

This commit is contained in:
Codex
2026-07-17 18:09:55 +03:00
parent 2dd6e33a54
commit 31e078d6e5
8 changed files with 831 additions and 414 deletions
+119 -16
View File
@@ -97,8 +97,15 @@ Engine-owned activator may select it after exact MCP schema acceptance.
The paired Engine activation is built by
`build-engine-n8n-private-extension-artifact.mjs`. It deliberately does not
copy the dirty Engine `docker-compose.yml` and does not build an image. Instead
it installs a narrowly scoped Compose override plus a strict transition
copy from or write generated files into the dirty Engine worktree, and it does
not build an image. A fresh transition id is mandatory and previously issued
ids are rejected:
```bash
node infra/deploy-runner/build-engine-n8n-private-extension-artifact.mjs 20260717-004
```
The builder emits a narrowly scoped Compose override plus a strict transition
descriptor. On apply, the runner validates the staged release again, verifies
that the running n8n container and the NAS-local `2.3.2` tag resolve to the
same immutable image ID, and extracts the package into the root-owned,
@@ -110,10 +117,15 @@ read-only Engine release tree:
The override sets `N8N_USER_FOLDER=/home/node`, which is required because the
actual Engine service runs as root while the canonical community package path
is below `/home/node/.n8n`. It enables loading but disables reinstall, mounts
only the exact release read-only, and uses both Compose `pull_policy: never`
and `docker compose up --pull never`. No registry access, lifecycle script,
database `installed_packages` row or custom-extension loader is involved.
is below `/home/node/.n8n`. The live runtime contract remains the successfully
deployed generation-003 contract and does not set `NODE_PATH`. Only the
runner's isolated `node -e` package-loader probe temporarily initializes the
dependency tree bundled inside the exact n8n base image; this reproduces n8n's
own loader without redefining the live Compose state. The override enables
loading but disables reinstall, mounts only the exact release read-only, and
uses both Compose `pull_policy: never` and `docker compose up --pull never`.
No registry access, lifecycle script, database `installed_packages` row or
custom-extension loader is involved.
The runner pins the exact Engine service topology observed in source and
rejects an added worker/webhook generation. Only the single actual `n8n`
@@ -140,20 +152,106 @@ PYTHONDONTWRITEBYTECODE=1 \
```
For `platform` artifacts, the allowlist includes the versioned Ontology Core,
the legacy Gelios experiment and the provider-neutral External Data Plane
sources. An External Data Plane artifact builds only its image and starts
`external-data-plane-postgres` plus `external-data-plane`; it never contains a
provider credential, provider endpoint, collection schedule or command
transport. Database credentials remain root-owned live `.env.synology`
the frozen legacy Gelios compatibility service, and the provider-neutral
External Data Plane sources. The Gelios service remains reproducible only to
protect its existing database/workflow; it is not a template for a provider
integration. An External Data Plane artifact builds only its image and
force-recreates only `external-data-plane`; the already healthy
`external-data-plane-postgres` container and its Timescale volume are an
independent deploy prerequisite and are never selected by an EDP application
artifact. The reviewed Compose source pins the Timescale image, named volume
and target, internal database-only network, absence of database host ports,
healthy dependency, localhost-only EDP bind and the two read-only
provisioner/trust mounts in the reviewed Compose source. The runner does not
reinterpret version-dependent `docker compose config` JSON as a second deploy
schema. Its canonical enforcement remains the artifact/path allowlist plus
hard-coded build command, selected service set, runtime-secret preparation and
health acceptance. Post-apply acceptance also requires
`database=ready`; a first-rollout failure removes only the candidate EDP
container without volumes and restores the source overlay. It never contains a
provider credential, provider endpoint,
collection schedule or command
transport. Its contract payload is an exact provider-neutral runtime subset;
`providers/*`, mappings, fixtures and tests are excluded and do not trigger an
EDP rebuild. Database credentials remain root-owned live `.env.synology`
configuration and must not reuse `NODEDC_INTERNAL_ACCESS_TOKEN`.
The External Data Plane writer-provisioner credential is different: on the
first relevant Platform apply, the root-owned runner creates
On the first relevant Platform apply, the root-owned runner creates
`/volume1/docker/nodedc-platform/secrets/external-data-plane-provisioner/token`
atomically in a dedicated UID/GID `11006` directory (directory `0500`, token
`0400`). It is never an `.env` value or an artifact member; Compose mounts it
read-only only into External Data Plane and, when implemented, its dedicated
Engine provisioner running under the same restricted identity.
`0400`). It is never an `.env` value or an artifact member and is mounted
read-only only into External Data Plane.
Manual one-time binding issuance and digest-only managed writer ensure are
independently disabled by default. The target control-plane operation generates
and stores the capability inside native NDC L2 Credentials and sends only its
digest to EDP; users and MCP consumers receive only an opaque compatible
reference/status. The runner-owned bearer above authenticates only legacy
plaintext issuance and is intentionally rejected by managed ensure/revoke.
Managed requests use a deployment Ed25519 Engine service key; EDP mounts only
the public-key trust directory read-only. On every reviewed Engine apply and on
an EDP runtime apply, this runner creates or validates one matching Ed25519 pair:
the Engine-only private key is `root:root 0400`, while the EDP trust copy is
`root:11006 0440`. Public-only crash state, key mismatch, a non-Ed25519 key,
symlinks and permissive modes fail closed. `plan` discloses both paths without
printing key material. The private key must not be broadened into an L2 graph,
MCP surface, artifact or shared-token boundary.
The reviewed Engine source candidate has dedicated server-derived MCP
plan/apply handling for the exact `ndcDataProductWriterApi` + Data Product
Publish tuple. It is separate from the generic HTTP safe-ref path and accepts no
caller-provided provider/scope/credential identity, capability, generation or
service URL. The production managed flag remains false during staging. In the
explicitly confirmed activation window it is set to true immediately before
the Platform EDP artifact; runner acceptance then requires EDP `/healthz` to
report managed provisioning `enabled`. At that point the old Engine still has
no signer mount, so the endpoint remains usable only after the separately
accepted Engine artifact. Root/UI transfer is emergency
self-hosted diagnostics only, not the user journey or acceptance
path. A provider-specific daemon remains forbidden.
Build the narrow Engine source artifact with:
```bash
node infra/deploy-runner/build-engine-data-product-publish-grant-artifact.mjs \
engine-data-product-publish-grant-YYYYMMDD-NNN
```
The builder includes exactly the pinned provider-security catalog, the
Publish-grant service, Engine Agent scope/gateway wiring, the existing n8n
adapter and the reviewed additive backend runtime overlay. It deliberately
excludes base Compose, frontend/dist, runtime data, tests, native credentials,
the NDC L2 process and the legacy generic credential-sink route/core. The
runner fixes the runtime action to `nodedc-backend` with `--no-deps` and
`--pull never`; n8n, nginx app, database services and volumes are not selected.
Acceptance requires backend health, the active immutable backend identity and
the exact additive mount inventory. Any failure restores the touched source and
recreates only the previous verified backend runtime.
Existing Engine Agents created before Publish-grant support store the original
nine scopes as the complete developer profile. They must not require a new
agent, setup command or device credential when the profile gains a server-owned
capability. Build the compatibility artifact that introduces the durable named
`full-developer` profile with:
```bash
node infra/deploy-runner/build-engine-agent-full-grant-migration-artifact.mjs \
engine-agent-full-grant-migration-YYYYMMDD-NNN
```
This follow-up slice contains exactly
`nodedc-source/server/engineAgents/store.js`. The runner pins its predecessor to
the successfully applied Publish generation, requires the installed Publish
overlay and active immutable backend, recreates only `nodedc-backend`, and
proves the exact candidate SHA, named profile and current expanded runtime
scope view. Store schema v1 is migrated atomically to v2 only when a grant
contains the complete legacy nine-scope developer bundle. From then on the
profile name is the authorization authority and its scope list is derived on
every read, so capabilities deliberately added to `full-developer` immediately
apply to existing full grants without token or store migrations. Partial grants
become `custom` and remain exact; they are never elevated. The artifact does not
touch UI, n8n, credentials, agent tokens, workflow graphs, databases or runtime
payloads.
`module-foundry` is an independent, authenticated application component. Its
artifact contains source and compose infrastructure only; its live
@@ -190,12 +288,17 @@ verified; it does not alter NAS routes, VPN, DNS, or Tailscale.
Install or update the root-owned live runner on Synology:
```bash
# First verify that no deploy process is active and state/deploy.lock is absent.
sudo install -o root -g root -m 0755 \
/volume1/docker/nodedc-deploy/runner-install/nodedc-deploy \
/usr/local/sbin/nodedc-deploy
sudo /usr/local/sbin/nodedc-deploy verify-install
```
Runner promotion is a standalone admin step, never an app-overlay artifact and
never part of a running apply. A Python process already executing the old file
keeps the old code in memory; always invoke a fresh verified process afterward.
Normal service deploys must still use explicit artifacts:
```bash
+67 -7
View File
@@ -202,7 +202,13 @@ TASKER_SYNC_SOURCE=1 ./infra/synology/deploy-current.sh
Если emergency-fix был сделан прямо на Synology в этих env-файлах, перенести sanitized-значение в `.env.synology.example`/docs, а секрет оставить только в live env.
### Gelios: сбор всех units доверенного подключения
### Gelios: frozen legacy compatibility contour
`gelios-postgres` и `gelios-gateway` — уже существующий self-hosted контур с
собственным persistent Timescale volume. Он остаётся полностью воспроизводимым
в source/deploy, но не развивается и не используется как шаблон для новых
провайдеров. Его удаление или миграция требуют отдельного подтверждённого плана;
обычный Platform/EDP deploy не удаляет volume и не изменяет старый workflow.
`GELIOS_UNIT_SCOPE=all` означает **все текущие и будущие units только одной уже
настроенной пары `GELIOS_TENANT_ID` + `GELIOS_CONNECTION_ID`**. Это не wildcard
@@ -210,17 +216,71 @@ TASKER_SYNC_SOURCE=1 ./infra/synology/deploy-current.sh
Пропавший из очередного ответа unit не удаляется: его stable provider ID и
`last_seen_at` сохраняются; видимость на карте — отдельная логика витрины.
Перед каноническим `nodedc-deploy apply` включить политику на Synology (скрипт
создаёт backup и не выводит секреты):
Перед узким legacy apply включить политику на Synology (скрипт создаёт backup и
не выводит секреты):
```bash
sudo bash /volume1/docker/nodedc-deploy/inbox/prepare-gelios-all-units-env.sh
```
Затем применить узкий Platform-артефакт, собранный с `--gateway-only`, через
`nodedc-deploy`. В его plan должны быть только `gelios-postgres` и
`gelios-gateway`; общий `docker-compose` в такой архив не входит. После apply
Gateway начинает принимать весь состав units этого подключения.
В plan такого Platform-артефакта должны быть только `gelios-postgres` и
`gelios-gateway`; общий compose в узкий архив не входит.
## EDP publish grant: platform-owned credential
`NDC Data Product Writer API` — историческое имя native credential type для
внутреннего scoped EDP publish grant. Это не Gelios access/refresh token и не
право записи в Gelios.
Целевой путь полностью служебный: control plane NDC по разрешённому connection
profile генерирует capability внутри native NDC L2 Credentials, передаёт EDP
только digest для scoped binding и возвращает MCP только opaque compatible
reference/status.
Пользователь не читает, не копирует и не вводит этот внутренний секрет.
EDP уже имеет localhost-only digest-only managed endpoint. Managed endpoint и
legacy plaintext provisioning имеют раздельные флаги и по умолчанию выключены.
Runner-owned provisioner bearer относится только к legacy plaintext routes и
на managed ensure/revoke намеренно не действует. Managed caller contract —
Ed25519-signed Engine service request с exact audience, method, request target,
raw-body SHA-256, timestamp и одноразовым nonce; EDP хранит только public key и
fail-closed replay cache. Ни bearer, ни signing private key нельзя открывать
workflow, MCP или переиспользовать как shared credential. В source-кандидате
Engine добавлен отдельный server-derived plan/apply для exact пары
`ndcDataProductWriterApi` + custom Publish node; он не расширяет generic HTTP
safe-ref policy и не принимает provider/scope/credential identity, capability,
generation или service URL от MCP. До отдельного Engine deploy и runtime proof
текущий deployed MCP этого контракта не имеет. Поэтому отсутствие
совместимого reference — платформенный runtime gap, а не действие пользователя.
Ручной перенос capability из отдельно включаемого legacy endpoint через root/UI допустим только как аварийная
диагностика self-hosted контура; он не является acceptance-путём и не должен
закрепляться в пользовательской автоматизации.
Compose монтирует read-only trust directory
`/volume1/docker/nodedc-platform/trust/engine-managed-provisioner` в EDP; в нём
ожидается один regular, non-symlink файл `public-key.pem` с Ed25519 SPKI public
key. Файл не должен быть group/world-writable и должен читаться runtime UID
`11006`. Private key в этот каталог, Platform artifact, `.env` или EDP
контейнер не попадает. Canonical deploy runner теперь атомарно создаёт или
проверяет matching Ed25519 pair перед relevant Engine/EDP Compose apply: private
key остаётся `root:root 0400`, public trust — `root:11006 0440`; public-only
crash state, mismatch, symlink и неверный тип ключа отклоняются до запуска.
`EXTERNAL_DATA_PLANE_MANAGED_PROVISIONING_ENABLED` остаётся `false` на staging.
В отдельно подтверждённом activation window его переводят в `true`
непосредственно перед Platform EDP artifact; runner принимает deploy только
если `/healthz` явно вернул managed status `enabled`. Старый Engine в этот
момент ещё не имеет signer mount, поэтому usable caller появляется лишь после
следующего отдельно принятого Engine artifact.
EDP application artifact пересобирает и force-recreate только
`external-data-plane`. Уже работающий `external-data-plane-postgres` и его
Timescale volume runner не выбирает и не перезапускает; healthy database —
обязательная deploy prerequisite.
Canonical service/key/audience заданы в `.env.synology.example`; wire headers и
canonical signing payload описаны в `packages/external-provider-contract/README.md`.
До появления opaque compatible reference и успешного manual publish proof
Schedule Trigger не включать. Frozen legacy workflow и его credentials этот
процесс не изменяет.
## AI Hub relay-only deploy