
Единый проводной протокол с приоритетом безопасности (security-first) для доступа к инструментам и координации агентов. UAP устраняет уязвимость CVE-2025-49596 и уязвимости отравления инструментов MCP, используя подписанные Ed25519 карточки возможностей (CapabilityCards), обязательный mTLS, аутентификацию через Keycloak IdP и эфемерную Docker‑песочницу для каждого вызова.
██╗ ██╗ █████╗ ██████╗
██║ ██║██╔══██╗██╔══██╗
██║ ██║███████║██████╔╝
██║ ██║██╔══██║██╔═══╝
╚██████╔╝██║ ██║██║
╚═════╝ ╚═╝ ╚═╝╚═╝
Universal Agent Protocol
Единый сетевой протокол, в котором безопасность — на первом месте, для доступа к инструментам, координации агентов и структурированного RPC. Разработан для закрытия CVE-2025-49596 и класса уязвимостей, связанных с отравлением инструментов (tool-poisoning) и выходом из песочницы (sandbox-escape), которые MCP оставил структурно открытыми.
git clone https://github.com/RajSidwadkar/UAP-protocol
cd UAP-protocol
npm install
npx tsx scripts/generate-keypair.ts
docker compose -f docker-compose.dev.yml up -d
curl http://localhost:3000/health
# {"status":"ok","version":"1.0.0"}
Это запускает Keycloak на :8080 и шлюз UAP на :3000. Шлюз обеспечивает mTLS, проверяет CapabilityCard, подписанные Ed25519, и выполняет каждый вызов инструмента в эфемерном Docker-контейнере. Кроме сгенерированной ключевой пары никакая настройка не требуется.
graph TD
A[Client / Agent SDK] -->|mTLS + JWT + card_sig| B[UAP Gateway<br/>Fastify · port 3000]
B --> C{8-Stage Pipeline}
C --> C1[1 · Frame parse<br/>UapMessageFactory]
C1 --> C2[2 · Schema validate<br/>AJV against schema_ref]
C2 --> C3[3 · Token verify<br/>Keycloak JWKS · max 15 min]
C3 --> C4[4 · Scope enforce<br/>PermissionEnforcer]
C4 --> C5[5 · Card verify<br/>Ed25519 signature check]
C5 --> C6[6 · Sandbox execute<br/>Docker · CapDrop ALL · 128 MB]
C6 --> C7[7 · Audit emit<br/>AuditEventBus · non-blocking]
C7 --> C8[8 · Response<br/>UapResponseEnvelope]
B --> R[(Service Registry<br/>InMemory · Redis)]
B --> KC[(Keycloak IdP<br/>OAuth 2.1 · PKCE)]
B --> OT[(OpenTelemetry<br/>W3C TraceContext)]
B --> AU[(Audit Log<br/>append-only NDJSON)]
Гексагональные слои — в домене нет зависимостей ввода-вывода. Каждый инфраструктурный компонент находится за типизированным порт-интерфейсом. Замените Docker на WASM, Keycloak на Auth0, Pino на SIEM-экспортёр — влияние на домен нулевое.
| Слой | Содержимое | Зависимости |
|---|---|---|
| Домен | UapEnvelope, CapabilityCard, AuditEvent, Task | Нет — только стандартная библиотека |
| Приложение | InvokeToolUseCase, DelegateTaskUseCase, ValidateCardUseCase | Только порты домена |
| Инфраструктура | KeycloakAuthAdapter, DockerSandboxAdapter, Ed25519SignerAdapter, PinoAuditAdapter | Порты приложения + внешние библиотеки |
| Транспорт | Парсер кадров UAP-RPC, шлюз Fastify, обработчики SSE/WebSocket | Инфраструктура + сериализаторы домена |
| Возможность | UAP | MCP | A2A | JSON-RPC 2.0 |
|---|---|---|---|---|
| Обязательная аутентификация | ✅ mTLS + JWT всегда | ❌ Необязательна | ⚠️ Только API-ключ | ❌ Нет |
| Целостность метаданных инструментов | ✅ Полезная нагрузка, подписанная Ed25519 | ❌ Возможно изменение после публикации | ❌ Без подписи | ❌ Без подписи |
| Модель песочницы | ✅ Эфемерный Docker на каждый вызов | ⚠️ Только на уровне сервера | ❌ Нет | ❌ Нет |
| Обнаружение агентов | ✅ «Звезда» · N подключений | ❌ Прямой HTTP N² | ⚠️ На основе DNS | ❌ Нет |
| Журнал аудита | ✅ Встроенный в протокол журнал append-only | ❌ Нет | ❌ Нет | ❌ Нет |
| Распределённая трассировка | ✅ Обязательный W3C TraceContext | ❌ Нет | ❌ Нет | ❌ Нет |
| Проверка схемы | ✅ AJV на транспортном уровне | ⚠️ Необязательный Zod | ❌ Нет | ❌ Нет |
| Максимальный срок жизни токена | ✅ Принудительно 15 минут | ❌ Не контролируется | ❌ Не контролируется | ❌ N/A |
| Семантика пакетных операций | ✅ последовательно · параллельно · транзакционно | ❌ Не определена | ❌ Нет | ⚠️ Неоднозначна |
| Бинарные полезные нагрузки | ✅ Нативные кадры | ❌ Только Base64 | ❌ Только Base64 | ❌ Только Base64 |
| Модель IdP | ✅ Только внешний IdP | ❌ Сервер сам является OAuth-провайдером | ⚠️ Зависит от реализации | ❌ Нет |
| Мостовые адаптеры | ✅ Мосты MCP + A2A выходят в Phase 2 | ❌ Моста нет | ❌ Моста нет | ❌ Моста нет |
| Горизонтальное масштабирование | ✅ Без состояния · любой балансировщик нагрузки | ❌ Липкие сессии | ⚠️ Зависит от реализации | ❌ Нет |
CVE-2025-49596 (отравление инструментов через неподписанные метаданные). Описания инструментов MCP можно менять после публикации. Злоумышленник может внедрить вредоносные инструкции в имена или описания инструментов после развёртывания — и у клиента нет способа обнаружить подмену. UAP помещает все метаданные инструментов в полезную нагрузку CapabilityCard, подписанную Ed25519. Любое изменение после подписания делает подпись недействительной, поэтому шлюз отклоняет карту до выполнения каких-либо действий.
CWE-284 / класс sandbox-escape. MCP выполняет инструменты на уровне процесса сервера. Path traversal или внедрение процесса в любом инструменте открывают файловую систему и сеть хоста. UAP создаёт новый Docker-контейнер для каждого вызова: rootfs только для чтения, CapDrop: ALL, лимит ОЗУ 128 МБ, сеть отключена по умолчанию. Контейнер уничтожается сразу после ответа. Между вызовами не остаётся постоянной поверхности атаки.
CWE-287 / аутентификация confused-deputy. MCP выступает собственным OAuth-провайдером, то есть одновременно является и сервером ресурсов, и сервером авторизации. Это классический паттерн confused-deputy. UAP разделяет эти роли: шлюз — чистый сервер ресурсов, а Keycloak (или любой внешний IdP) — единственный источник полномочий. JWT проверяются через удалённую точку JWKS и жёстко ограничены 15 минутами.
Все сообщения UAP используют один и тот же конверт. Блок auth содержит JWT с ограниченной областью действия и ссылку на подписанную CapabilityCard агента. Шлюз проверяет оба элемента до того, как запрос попадёт в какой-либо прикладной код.
{
"uap": {
"version": "1.0",
"type": "tool_call",
"id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
"trace": {
"traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
},
"auth": {
"token": "eyJhbGciOiJSUzI1NiJ9...",
"scope": ["tool:read"],
"card_sig": "ed25519:a1b2c3d4..."
}
},
"method": "tools/invoke",
"schema_ref": "uap:tool.invoke/v1",
"params": {
"tool_id": "db:query",
"input": { "sql": "SELECT 1" }
},
"ack": true
}
UAP-protocol/
├── packages/
│ ├── gateway/ @uap/gateway — Fastify-based UAP Gateway
│ ├── sdk-ts/ @uap/sdk-ts — TypeScript client + server SDK
│ └── sdk-py/ uap-sdk — Python async SDK (httpx + FastAPI)
├── scripts/
│ ├── generate-keypair.ts — Ed25519 keypair for local dev
│ └── keycloak-bootstrap.ts — Idempotent Keycloak realm setup
└── docker/
└── sandbox/ — Base image for zero-trust tool execution
import { UapClient, ClientCredentialsTokenProvider } from "@uap/sdk-ts";
const client = new UapClient({
gatewayUrl: "https://gateway.example.com",
tokenProvider: new ClientCredentialsTokenProvider({
tokenUrl: process.env.TOKEN_URL!,
clientId: process.env.CLIENT_ID!,
clientSecret: process.env.CLIENT_SECRET!,
}),
});