feat(foundry): add managed data consumers and agent settings
This commit is contained in:
@@ -134,6 +134,29 @@ Pill navigation для верхней панели и компактного п
|
||||
|
||||
В каноническом `ApplicationShell` шапка фиксирована. Её три оси не двигаются при открытии navigation/content и не зависят от ширины предметного контента.
|
||||
|
||||
## UserProfileMenu
|
||||
|
||||
Каноническая выпадашка профиля для правой группы `AppHeader`. Она использует
|
||||
общий portal `Dropdown`, показывает identity cover с аватаром, именем и
|
||||
подписью и принимает список application-owned действий.
|
||||
|
||||
Компонент владеет только геометрией, темизацией, закрытием по outside pointer и
|
||||
Escape. Переход в профиль, открытие настроек, logout, permissions и продуктовые
|
||||
правила остаются в приложении. Меню не должно содержать скрытый MCP или ACL
|
||||
контракт.
|
||||
|
||||
## FeatureSettingsWindow
|
||||
|
||||
Большое модальное окно настроек по принятой механике нового Engine: identity
|
||||
текущего модуля слева, сгруппированная навигация features и scrollable content
|
||||
справа. Компонент переиспользует `Window`, поэтому не создаёт собственный
|
||||
portal, backdrop или focus trap.
|
||||
|
||||
`FeatureSettingsWindow` хранит только controlled active section. Формы,
|
||||
setup-команды, сохранение, API и права принадлежат приложению. Цвет и материал
|
||||
берутся из активного Design Profile; отдельная Engine/Foundry тема внутри
|
||||
компонента запрещена.
|
||||
|
||||
## ApplicationShell и ApplicationPanel
|
||||
|
||||
Общий шаблон приложения по механике Launcher/Hub: фиксированный `AppHeader`, центральный stage, левая navigation panel и отдельное правое content window.
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
`NDC Foundry Binding` is a deploy/control-plane operation. Realtime facts do
|
||||
not pass through the node: Foundry persists an approved page-slot binding and
|
||||
its same-origin BFF reads snapshot + patch from External Data Plane.
|
||||
its server-owned consumer reads snapshot and durable patch from External Data
|
||||
Plane. The same-origin BFF serves the persisted projection and bounded history
|
||||
without exposing the reader grant.
|
||||
|
||||
## Private workload API
|
||||
|
||||
@@ -40,10 +42,42 @@ No authorization claim is accepted from request headers or workflow data.
|
||||
|
||||
Before persisting a binding, Foundry verifies that the target-specific EDP
|
||||
reader grant exists, is active, permits the selected product and exposes a
|
||||
`snapshot+patch` product. The reader grant filename remains
|
||||
`sha256(applicationId/pageId/bindingId)`. If it is not ready, POST fails with
|
||||
`409 data_product_reader_grant_not_ready`; an unusable visual binding is never
|
||||
saved.
|
||||
`snapshot+patch` product. The workload API remains fail-closed: if the grant is
|
||||
not ready, POST fails with `409 data_product_reader_grant_not_ready`; an
|
||||
unusable visual binding is never saved.
|
||||
|
||||
The Foundry MCP consumer lifecycle owns managed grant creation. Its safe
|
||||
`plan` asks EDP to resolve unique active writer coverage by Data Product id.
|
||||
The response contains no provider, tenant or connection. Exact `apply`
|
||||
generates an opaque target token inside the persistent Foundry runtime and
|
||||
sends only its SHA-256 digest in a request signed by a dedicated Foundry
|
||||
Ed25519 service identity. EDP persists a durable, explicitly revocable reader
|
||||
binding. The signing private key is runner-managed and distinct from Engine;
|
||||
EDP mounts only its public trust copy. Legacy root-owned deployment grants are
|
||||
read-only compatibility fallback, not the normal lifecycle.
|
||||
|
||||
The canonical Map entity-stream slot is `points`. Other templates may define
|
||||
their own typed slots, but the workload grant must name them explicitly.
|
||||
|
||||
## Server-owned consumer lifecycle
|
||||
|
||||
An approved binding is the only addressable consumer target. Foundry persists
|
||||
consumer state under the runtime volume, keyed by application/page/binding; it
|
||||
stores the binding identity, Data Product version, policy version, cursor,
|
||||
snapshot generation, safe subjects, presentation status and reconnect
|
||||
metadata. It never stores the reader capability value or EDP endpoint.
|
||||
|
||||
Foundry MCP exposes exact `plan`/`apply`, safe `status`, bounded `accept` and
|
||||
non-destructive `rollback` for this target. A missing legacy consumer record is
|
||||
bootstrapped lazily from the already approved binding; a missing target grant
|
||||
appears as the explicit `ensure-target-scoped-reader-grant` plan effect. An
|
||||
existing Map Page therefore does not require a new provider workflow, native
|
||||
n8n credential or manual pin migration.
|
||||
|
||||
Snapshot replacement and patch application are atomic per target. Cursor is
|
||||
committed before browser fan-out. Patch replay is idempotent; a gap causes
|
||||
snapshot rebase. One active upstream stream is shared by every viewer of the
|
||||
same target and is closed after the final viewer releases it. Temporary source
|
||||
or network failure preserves subjects. Stale and removal use the versioned
|
||||
provider-neutral policy registry: removal is allowed only by authoritative
|
||||
snapshot absence or canonical tombstone/revocation.
|
||||
|
||||
+15
-7
@@ -86,13 +86,21 @@ semantic types, field projection и `slotId` (`points` для live point entitie
|
||||
При открытии Application page browser делает same-origin запрос к Foundry:
|
||||
|
||||
```text
|
||||
Application/Page/Binding → Foundry BFF → External Data Plane snapshot → SSE patch
|
||||
Application/Page/Binding → Foundry BFF → External Data Plane snapshot/history → SSE patch
|
||||
```
|
||||
|
||||
Foundry сопоставляет target с root-owned opaque reader grant в закрытом
|
||||
deployment directory. Browser, manifest и Cesium adapter не получают provider
|
||||
Foundry сопоставляет target с opaque reader grant в закрытом persistent
|
||||
runtime. При первом exact consumer apply Foundry генерирует token локально и
|
||||
передаёт EDP только его digest в запросе с отдельной service-подписью; EDP сам
|
||||
разрешает unique active writer scope и fail-closed отклоняет ambiguity. Legacy
|
||||
root-owned deployment directory используется только как совместимый fallback.
|
||||
Browser, manifest и Cesium adapter не получают provider
|
||||
endpoint, tenant/connection scope, reader token или raw provider payload.
|
||||
Сначала приходит snapshot, затем только patch events с cursor; при пропуске
|
||||
cursor BFF требует resync snapshot. Renderer держит отдельный `CustomDataSource`
|
||||
на binding и обновляет stable entity id без пересоздания viewer. History и
|
||||
sampling остаются политикой Data Plane, а не Map Template.
|
||||
Сначала server-owned Foundry consumer коммитит snapshot, затем применяет patch
|
||||
events с exact cursor и только после commit делает fan-out всем viewers. При
|
||||
пропуске cursor требуется snapshot rebase; несколько вкладок одного binding не
|
||||
создают несколько upstream EDP subscriptions. Timeline использует provider-neutral
|
||||
history route (`from/to/resolution/sourceIds/cursor`) через тот же BFF и не
|
||||
занимает общую L2 execution queue. Renderer держит отдельный `CustomDataSource`
|
||||
на binding и обновляет stable entity id без пересоздания viewer. Sampling и
|
||||
retention остаются политикой Data Plane, а не Map Template.
|
||||
|
||||
+109
-18
@@ -13,9 +13,10 @@ MCP не вводит новую оркестрацию. Доступ выдаё
|
||||
| Просматривать Page Library и экземпляры Applications | Менять канонический Page Library или дизайн-компоненты |
|
||||
| Создавать application instance | Удалять application instance через MCP |
|
||||
| Изменять название, slug и описание instance | Создавать свободный canvas или произвольный React-интерфейс |
|
||||
| Добавлять повторные instances зарегистрированной страницы | Вызывать Engine, провайдера карт или внешний API из Foundry MCP |
|
||||
| Добавлять повторные instances зарегистрированной страницы | Вызывать Engine, provider API или произвольный внешний endpoint из Foundry MCP |
|
||||
| Создавать/обновлять provider-neutral map pin bindings | Хранить provider tokens, transport payload или credentials в manifest |
|
||||
| Связывать versioned data product с approved Map entity-stream slot | Хранить provider ID, tenant/connection, endpoint или credential в data binding |
|
||||
| Управлять server-owned consumer только для persisted approved binding | Передавать reader capability, EDP URL или raw provider payload через MCP |
|
||||
|
||||
Каждая запись требует `idempotencyKey`. Операция сохраняется в persistent runtime volume и повторный вызов с тем же ключом и тем же входом вернёт исходный результат без дублирования. Повтор ключа с иным входом завершается конфликтом.
|
||||
|
||||
@@ -28,15 +29,22 @@ MCP не вводит новую оркестрацию. Доступ выдаё
|
||||
Обязательные HTTP-заголовки каждого вызова:
|
||||
|
||||
```http
|
||||
Authorization: Bearer <short-lived server-issued Foundry capability>
|
||||
Authorization: Bearer <Foundry capability or Foundry Agent credential>
|
||||
MCP-Protocol-Version: 2025-06-18
|
||||
```
|
||||
|
||||
Capability выдаётся только сервером Foundry после штатной entitlement-проверки
|
||||
AI Workspace. Она подписана существующим внутренним service credential,
|
||||
привязана к `actorId` и `ownerKey`, живёт не более 10 минут и не даёт worker
|
||||
доступа к самому platform credential. Заголовки с actor/owner от worker не
|
||||
принимаются: контекст извлекается только из подписанной capability.
|
||||
Endpoint поддерживает два существующих контура входа, не смешивая их:
|
||||
|
||||
- AI Workspace получает короткоживущую Foundry capability после штатной
|
||||
entitlement-проверки. Она подписана существующим внутренним service
|
||||
credential, привязана к `actorId` и `ownerKey`, живёт не более 10 минут и не
|
||||
раскрывает platform credential worker-у;
|
||||
- внешний Codex получает отдельный durable Foundry Agent credential через
|
||||
настройки текущего пользователя Foundry. Credential действует до явного
|
||||
`revoke` и не является AI Workspace capability.
|
||||
|
||||
Заголовки с actor/owner от клиента не принимаются: пользовательский контекст
|
||||
извлекается только из проверенной capability или server-side записи Agent.
|
||||
|
||||
Доступные инструменты:
|
||||
|
||||
@@ -48,6 +56,11 @@ AI Workspace. Она подписана существующим внутрен
|
||||
- `foundry_add_page_instance`
|
||||
- `foundry_upsert_map_pin_binding`
|
||||
- `foundry_upsert_map_data_product_binding`
|
||||
- `foundry_plan_map_data_product_consumer`
|
||||
- `foundry_apply_map_data_product_consumer`
|
||||
- `foundry_get_map_data_product_consumer_status`
|
||||
- `foundry_accept_map_data_product_consumer`
|
||||
- `foundry_rollback_map_data_product_consumer`
|
||||
|
||||
`foundry_upsert_map_pin_binding` хранит только визуальную, provider-neutral привязку `elevated-spike`: стабильный id, subject, координаты, semantic status и ссылку на источник сущности. Поток живых данных не передаётся в MCP по одной позиции: визуальная привязка и поток данных будут связываться следующими contract/ontology слоями.
|
||||
|
||||
@@ -57,23 +70,97 @@ types и допустимую field projection. Endpoint, provider, tenant, conn
|
||||
token, credential и raw payload валидатор отклоняет. Page runtime получает
|
||||
scoped snapshot/patch поток через Platform, но не через Foundry MCP.
|
||||
|
||||
Пять consumer-инструментов управляют тем же runtime, который обслуживает Map
|
||||
Page. `plan` проверяет persisted binding, Data Product version/delivery,
|
||||
versioned policy и безопасно показывает `readerGrantAction=ensure|reuse`.
|
||||
Если grant отсутствует, EDP сам разрешает единственный active writer scope по
|
||||
Data Product id; provider/tenant/connection в Foundry не возвращаются. `apply`
|
||||
принимает exact `planId`, подписанно передаёт EDP только SHA-256 digest нового
|
||||
target-scoped token, коммитит snapshot и включает consumer; `status` возвращает только safe cursor,
|
||||
счётчики subject/status/reconnect и количество viewers/upstream streams;
|
||||
`accept` берёт bounded diagnostic lease; `rollback` останавливает consumer, но
|
||||
сохраняет последний safe snapshot. Capability value, grant path, internal URL и
|
||||
fact attributes в MCP diagnostics не возвращаются.
|
||||
|
||||
## Runtime data-product boundary
|
||||
|
||||
Для Map Page Foundry предоставляет только same-origin runtime routes:
|
||||
|
||||
```text
|
||||
GET /api/applications/:applicationId/pages/:pageId/data-bindings/:bindingId/snapshot
|
||||
GET /api/applications/:applicationId/pages/:pageId/data-bindings/:bindingId/history?from=:iso&to=:iso&resolutionMs=:ms&sourceIds=:ids&limit=:n&cursor=:opaque
|
||||
GET /api/applications/:applicationId/pages/:pageId/data-bindings/:bindingId/stream?after=:cursor
|
||||
```
|
||||
|
||||
Маршрут разрешает persisted binding, а затем серверно находит opaque EDP reader
|
||||
grant по `sha256(applicationId/pageId/bindingId)`. Grant находится в
|
||||
root-owned read-only directory, передаётся только как `Authorization` во
|
||||
внутренний External Data Plane и никогда не попадает в browser, manifest,
|
||||
MCP или лог. В browser отдаётся только canonical data-product envelope:
|
||||
snapshot, safe `nodedc.data-product.patch/v1` upserts и cursor. `Last-Event-ID`
|
||||
Маршрут разрешает persisted binding, а затем server-owned consumer находит
|
||||
opaque EDP reader grant по `sha256(applicationId/pageId/bindingId)`. Нормальный
|
||||
grant создаётся внутри persistent private runtime Foundry; runner монтирует
|
||||
отдельный Ed25519 private key только для подписи digest-only provisioner
|
||||
request, а EDP получает только public trust. Старый root-owned read-only grant
|
||||
directory остаётся fallback для уже выданных grant. Сам token передаётся
|
||||
только как `Authorization` во внутренний External Data Plane и никогда не
|
||||
попадает в browser, manifest, MCP или лог. В browser отдаётся только safe Foundry projection канонического data-product envelope:
|
||||
snapshot, bounded `nodedc.data-product.history/v1`, safe
|
||||
`nodedc.data-product.patch/v1` upserts и cursor. History query проходит через
|
||||
тот же exact binding/read grant и не создаёт L2 execution на каждого viewer.
|
||||
`Last-Event-ID`
|
||||
авторитетнее старого `after` при автоматическом SSE reconnect.
|
||||
|
||||
Consumer state хранится отдельно от application manifest в persistent runtime
|
||||
volume. Snapshot atomically заменяет subject projection, включая явный empty
|
||||
snapshot. Patch применяется только от exact previous cursor; новый cursor и
|
||||
semantic state сначала сохраняются, затем fan-out отправляется browsers.
|
||||
Повторный cursor идемпотентно игнорируется, gap вызывает snapshot rebase.
|
||||
Несколько viewers одного binding делят один upstream EDP stream; закрытие
|
||||
последнего viewer закрывает subscription, но не влияет на независимый producer
|
||||
в Engine L2.
|
||||
|
||||
Freshness и remove принадлежат versioned provider-neutral policy из
|
||||
`registry/data-product-consumer-policies.json`. Stale вычисляется по
|
||||
`observedAt`; terminal statuses сохраняются. Временная transport/credential
|
||||
ошибка не удаляет subject. Удаление допустимо только при отсутствии в
|
||||
авторитетном snapshot rebase или по canonical `tombstone`/`revoked` operation.
|
||||
Renderer получает стабильный `sourceId + semanticType`, persisted coordinates
|
||||
и Foundry-owned `presentationStatus`; provider identity на стиль и lifecycle не
|
||||
влияет.
|
||||
|
||||
## Внешний Codex: Foundry + отдельная Ontology MCP
|
||||
|
||||
В профиле Foundry раздел `Настройки → Codex Agent API` создаёт Agent текущего
|
||||
пользователя и выдаёт одноразовую setup-команду. Команда устанавливает в Codex
|
||||
две независимые MCP-конфигурации:
|
||||
|
||||
```text
|
||||
nodedc_module_foundry → POST /api/mcp
|
||||
nodedc_ontology → POST /api/ontology-mcp
|
||||
```
|
||||
|
||||
Это одна операция установки, но не одна MCP. Foundry credential принимается
|
||||
только `/api/mcp`, Ontology credential — только `/api/ontology-mcp`; их
|
||||
перекрёстное использование отклоняется. Ontology route после своей проверки
|
||||
проксирует MCP JSON-RPC во внутренний `Ontology Core /mcp` с уже существующим
|
||||
server-only `NODEDC_INTERNAL_ACCESS_TOKEN`. Foundry не импортирует ontology
|
||||
tools в свой список и не становится маршрутизатором платформенных MCP.
|
||||
|
||||
Agent credentials не имеют календарного срока жизни: они валидны до явного
|
||||
отзыва Agent. Setup code одноразовый и живёт 15 минут. Raw credentials
|
||||
возвращаются только в момент redeem; в Foundry runtime сохраняются только
|
||||
SHA-256 digests, device metadata и bounded audit. Один `revoke` закрывает обе
|
||||
credentials конкретного Agent.
|
||||
|
||||
Первый срез намеренно ограничен текущим аутентифицированным пользователем:
|
||||
Codex может создавать и настраивать его Applications средствами Foundry MCP и
|
||||
читать общую Ontology через отдельную read-only MCP. Sharing, роли,
|
||||
межпользовательская видимость, release/publication и новые product ACL в этот
|
||||
срез не входят.
|
||||
|
||||
Установщик идемпотентно обновляет только блоки `nodedc_module_foundry` и
|
||||
`nodedc_ontology` в `~/.codex/config.toml`, сохраняет остальные MCP (включая
|
||||
Engine и Ops), делает backup конфигурации и устанавливает skill
|
||||
`foundry-context`. Перед успешным завершением он выполняет `initialize` и
|
||||
`tools/list` для обеих MCP. После установки Codex Desktop нужно полностью
|
||||
перезапустить.
|
||||
|
||||
## Entitlement для AI Workspace
|
||||
|
||||
`POST /api/ai-workspace/entitlements`
|
||||
@@ -118,6 +205,8 @@ FOUNDRY_PUBLIC_URL=https://<future-foundry-domain>
|
||||
FOUNDRY_MCP_URL=https://<future-foundry-domain>/api/mcp
|
||||
# Existing NODE.DC server-to-server credential; never send it to a browser or worker.
|
||||
NODEDC_INTERNAL_ACCESS_TOKEN=<existing-platform-service-value>
|
||||
# Internal-only Ontology Core address; never expose the Core directly.
|
||||
NODEDC_ONTOLOGY_CORE_URL=http://ontology-core:8080
|
||||
FOUNDRY_MCP_CAPABILITY_TTL_MS=600000
|
||||
# Optional exceptional identities, not the default dcctouch superadmin grant.
|
||||
FOUNDRY_ALLOWED_OWNER_IDS=<comma-separated-extra-ids>
|
||||
@@ -126,10 +215,12 @@ FOUNDRY_ALLOW_ALL_AUTHENTICATED=false
|
||||
FOUNDRY_MCP_ALLOWED_ORIGINS=https://<ai-workspace-domain>
|
||||
```
|
||||
|
||||
Foundry не требует у пользователя новый secret. `NODEDC_INTERNAL_ACCESS_TOKEN` —
|
||||
уже существующий server-to-server credential платформы: он находится только в
|
||||
server environment и используется и для Launcher/Authentiк handoff, и для
|
||||
внутренней entitlement-проверки. Worker получает лишь короткоживущую capability.
|
||||
Foundry не требует у пользователя вводить platform secret.
|
||||
`NODEDC_INTERNAL_ACCESS_TOKEN` — уже существующий server-to-server credential
|
||||
платформы: он находится только в server environment и используется для
|
||||
Launcher/Authentiк handoff, внутренней entitlement-проверки и server-side
|
||||
Ontology Core proxy. AI Workspace worker получает лишь короткоживущую
|
||||
capability; внешний Codex — отдельные Agent credentials, но не internal token.
|
||||
|
||||
После появления домена в deployment AI Workspace добавляется только штатная настройка adapter; новый worker, отдельная оркестрация или специальный bridge не нужны:
|
||||
|
||||
@@ -141,7 +232,7 @@ AI_WORKSPACE_ENTITLEMENT_ADAPTERS_JSON='{"module-foundry":{"url":"https://<futur
|
||||
|
||||
## Deployment package
|
||||
|
||||
Foundry подготовлен к отдельному deployment как stateless server + один named persistent volume. Runtime volume содержит Applications, Design Profiles, idempotency audit и media uploads. Он является единственным источником изменяемого Foundry state; Git не используется для runtime-данных и tile cache.
|
||||
Foundry подготовлен к отдельному deployment как stateless server + один named persistent volume. Runtime volume содержит Applications, Design Profiles, idempotency audit, data-product consumer state/cursors, media uploads, Agent/device records, setup-code digests и Agent audit. Он является единственным источником изменяемого Foundry state; Git не используется для runtime-данных и tile cache. Raw Foundry/Ontology Agent credentials и EDP reader capabilities в volume не записываются.
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
|
||||
Reference in New Issue
Block a user