feat(foundry): add managed data consumers and agent settings

This commit is contained in:
Codex
2026-07-19 15:03:54 +03:00
parent 5f583caa05
commit aac44d057f
35 changed files with 4534 additions and 243 deletions
+109 -18
View File
@@ -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