# Документация AgentBouncer

> Проверяйте подписанные запросы AI-агентов и применяйте политики доступа проекта.

Версия документации: 0.1.1

Canonical portal: https://agentbouncer.io/ru/docs

Canonical Markdown: https://agentbouncer.io/ru/docs.md

## Содержание документации

- [Обзор AgentBouncer](#overview): Проверяйте подписанные запросы ИИ-агентов и применяйте политики доступа конкретного проекта.
- [Быстрый старт](#quickstart): Создайте проект, скопируйте API-ключ и защитите конечную точку.
- [Ключевые понятия](#core-concepts): Узнайте, что такое проекты, API-ключи, ключи подписи, провайдеры и политики.
- [Создание проектов и управление ими](#projects): Настройте границу доверия для приложения или MCP-сервера.
- [API-ключи проекта](#api-keys): Аутентифицируйте сервер при обращении к API AgentBouncer.
- [Интеграция с REST API](#rest-api): Вызывайте AgentBouncer из любого языка или серверного фреймворка.
- [SDK для JavaScript и TypeScript](#sdk): Проверяйте подписанные запросы и применяйте итоговое решение политики AgentBouncer.
- [Next.js App Router](#nextjs): Защитите маршрут Next.js, сохранив внешний URL запроса.
- [Next.js без SDK](#manual-nextjs): Отправляйте минимальную полезную нагрузку проверки непосредственно в REST API.
- [Интеграция с Express](#express): Защищайте маршруты Express с помощью REST API проверки.
- [Защита инструментов MCP](#mcp): Применяйте решения AgentBouncer до выполнения операций MCP.
- [Ключи подписи проекта](#project-signing-keys): Создавайте идентификаторы для агентов, непосредственно принадлежащих вашему проекту.
- [Регистрация провайдера агентов](#providers): Опубликуйте идентификатор провайдера и каталог открытых ключей.
- [Режимы политик](#policies): Выберите, как проверенный идентификатор и данные о доверии влияют на доступ.
- [Пользовательские политики](#custom-policies): Создавайте детальные разрешающие и запрещающие правила.
- [Ответ проверки](#response): Изучите поля, возвращаемые API проверки.
- [Причины и ошибки](#errors): Распространённые причины результатов проверки и решений политики.
- [События проверки](#events): Отслеживайте реальный трафик и расследуйте решения об авторизации.
- [Рекомендации по безопасности](#security): Развёртывайте AgentBouncer без утечки секретов и ослабления проверки.
- [Устранение неполадок](#troubleshooting): Диагностируйте проблемы с подписью, URL, каталогом ключей и политикой.
- [Контрольный список перед запуском](#production-checklist): Проверьте интеграцию перед включением блокирующих политик.

<a id="overview"></a>

## Обзор AgentBouncer

Проверяйте подписанные запросы ИИ-агентов и применяйте политики доступа конкретного проекта.

AgentBouncer проверяет подписи HTTP-сообщений, отправленные ИИ-агентами, определяет открытый ключ подписанта, оценивает политику вашего проекта и возвращает итоговое решение о доступе.

> **Самое важное правило интеграции**
>
> Используйте allowed как итоговое решение об авторизации. Поле verified сообщает результат криптографической проверки, а allowed — фактическое решение политики проекта.

| verified | allowed | Значение |
| --- | --- | --- |
| true | true | Подпись действительна, и запрос разрешён. |
| true | false | Подпись действительна, но политика проекта запретила доступ. |
| false | true | Запрос не прошёл проверку, но был разрешён режимом MONITOR_ONLY или другой разрешающей настройкой. |
| false | false | Запрос не прошёл проверку, и доступ был запрещён. |

- Проверяйте подписи HTTP-сообщений по стандарту RFC 9421.
- Получайте открытые ключи провайдеров и ключи, принадлежащие проекту.
- Применяйте уровни доверия, оценки доверия, действия, инструменты и пользовательские правила.
- Защищайте инструменты MCP, API, операции оформления заказа и автономные рабочие процессы.
- Сохраняйте события проверки для аналитики и расследования инцидентов.

<a id="quickstart"></a>

## Быстрый старт

Создайте проект, скопируйте API-ключ и защитите конечную точку.

1. **Создайте проект** — Откройте панель управления AgentBouncer, создайте проект и укажите origin или хост сервиса, который вы хотите защитить.
2. **Начните с режима мониторинга** — На первом этапе интеграции используйте MONITOR_ONLY. Запросы продолжат выполняться, а AgentBouncer будет фиксировать, какие из них были бы заблокированы.
3. **Скопируйте API-ключ проекта** — API-ключ аутентифицирует ваш сервер при обращении к API проверки AgentBouncer. Он не является ключом подписи агента.
4. **Передайте исходный URL запроса и заголовки подписи** — URL должен совпадать с URL, который подписал агент. Сохраняйте заголовки Signature, Signature-Input и Signature-Agent.
5. **Авторизуйте запрос с помощью allowed** — Отклоняйте запрос только в том случае, если allowed имеет значение false.

### Проверка запроса через REST

```bash

curl -X POST https://agentbouncer.io/api/v1/verify \
  -H "Authorization: Bearer ab_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://merchant.example/api/mcp/weather",
    "method": "POST",
    "headers": {
      "signature": "sig1=:BASE64_SIGNATURE:",
      "signature-input": "sig1=(\"@authority\" \"signature-agent\");created=176000;expires=176000030;keyid=\"KEY_ID\";tag=\"web-bot-auth\"",
      "signature-agent": "\"https://agent.example\"",
      "content-type": "application/json"
    },
    "expectedTag": "web-bot-auth",
    "action": "tools:call",
    "tool": "weather"
  }'

```

### Применение итогового решения

```typescript

const verification = await verifyWithAgentBouncer(request);

if (!verification.allowed) {
  return Response.json(
    {
      ok: false,
      error: "agent_access_denied",
      verification,
    },
    {
      status: verification.verified ? 403 : 401,
    }
  );
}

// Продолжите выполнение защищённой операции.
return runProtectedTool();

```

<a id="core-concepts"></a>

## Ключевые понятия

Узнайте, что такое проекты, API-ключи, ключи подписи, провайдеры и политики.

| Понятие | Назначение |
| --- | --- |
| Проект | Защищённое приложение, API, MCP-сервер, origin или граница доверия. |
| API-ключ проекта | Аутентифицирует ваш сервер при обращении к /api/v1/verify. |
| Ключ подписи проекта | Идентифицирует один из ваших собственных агентов и подписывает его запросы. |
| Провайдер | Зарегистрированный внешний провайдер ИИ-агентов с идентификатором подписи и каталогом открытых ключей. |
| Ключ провайдера | Открытый ключ, найденный в каталоге подписей HTTP-сообщений провайдера. |
| Событие проверки | Неизменяемая запись о результате криптографической проверки, решении политики, подписанте, риске и метаданных запроса. |
| Политика | Правила, преобразующие информацию о проверке и доверии в allowed=true или allowed=false. |

> **API-ключи и ключи подписи — это разные ключи**
>
> API-ключ проекта разрешает вашему серверу обращаться к AgentBouncer. Ключ подписи хранится у агента и используется для подписания исходного HTTP-запроса.

<a id="projects"></a>

## Создание проектов и управление ими

Настройте границу доверия для приложения или MCP-сервера.

1. **Создайте проект** — Выберите понятное название, например «Production MCP», «Checkout API» или «Внутрення автоматизация».
2. **Укажите origin** — Используйте публичный origin, к которому обращаются агенты. URL обратного прокси и внутренних развёртываний не должны заменять внешний подписанный URL.
3. **Выберите политику** — Начните с MONITOR_ONLY, изучите события проверки, а затем перейдите к BLOCK_UNKNOWN или более строгой политике.
4. **При необходимости создайте ключи подписи проекта** — Используйте ключи проекта для внутренних агентов, клиентов разработки и интеграций, принадлежащих проекту.
5. **Просматривайте события** — Проверяйте поля verified, allowed, reason, тип подписанта, провайдера, ключ проекта, действие, инструмент, риск и сведения о политике.

> **Рекомендуемые границы проектов**
>
> Используйте отдельные проекты для production- и development-сред. Также создавайте отдельные проекты для приложений с существенно различающимися требованиями к доверию или авторизации.

<a id="api-keys"></a>

## API-ключи проекта

Аутентифицируйте сервер при обращении к API AgentBouncer.

API-ключи проекта имеют префикс ab_live_ и должны храниться только на сервере. Никогда не размещайте их в браузерном JavaScript, публичных репозиториях или клиентских переменных окружения.

### .env.local

```bash

AGENTBOUNCER_API_KEY=ab_live_YOUR_API_KEY
AGENTBOUNCER_PUBLIC_ORIGIN=https://merchant.example
AGENTBOUNCER_VERIFY_URL=https://agentbouncer.io/api/v1/verify

```

- Храните production-ключи в менеджере секретов вашего хостинг-провайдера.
- Ротируйте ключ, если он случайно попал в журнал или репозиторий.
- Не отправляйте API-ключ входящему ИИ-агенту.
- Не путайте API-ключ AgentBouncer с приватным JWK подписи.

<a id="rest-api"></a>

## Интеграция с REST API

Вызывайте AgentBouncer из любого языка или серверного фреймворка.

### POST /api/v1/verify

```http

POST /api/v1/verify HTTP/1.1
Host: agentbouncer.io
Authorization: Bearer ab_live_YOUR_API_KEY
Content-Type: application/json

{
  "url": "https://merchant.example/api/mcp/weather",
  "method": "POST",
  "headers": {
    "signature": "sig1=:...:",
    "signature-input": "sig1=(...);created=...;expires=...;keyid=\"...\";tag=\"web-bot-auth\"",
    "signature-agent": "\"https://agent.example\""
  },
  "expectedTag": "web-bot-auth",
  "action": "tools:call",
  "tool": "weather",
  "userAgent": "ExampleAgent/1.0"
}

```

| Поле | Обязательность | Описание |
| --- | --- | --- |
| url | Да | Точный внешний URL, запрос к которому был подписан. |
| method | Рекомендуется | Исходный HTTP-метод. По умолчанию используется GET. |
| headers | Рекомендуется | Исходные заголовки запроса, используемые для проверки подписи. Поля подписи также можно передать через signature, signatureInput и signatureAgent. |
| signature | Условно | Значение заголовка Signature. Обязательно, если оно не включено в headers. |
| signatureInput | Условно | Значение заголовка Signature-Input. Обязательно, если оно не включено в headers. |
| signatureAgent | Нет | Значение структурированного поля Signature-Agent. Его также можно включить в headers. |
| expectedTag | Нет | Тег назначения, ожидаемый защищённой конечной точкой. |
| action | Нет | Семантическая операция, например tools:call, products:read, checkout или orders:write. |
| tool | Нет | Инструмент MCP или возможность приложения, к которой выполняется обращение. |
| userAgent | Нет | Исходный User-Agent отправителя запроса для аналитики. |

> **Сохраняйте подписанный URL**
>
> Не заменяйте публичный целевой URL адресом конечной точки проверки AgentBouncer, внутренним URL контейнера или URL обратного прокси.

<a id="sdk"></a>

## SDK для JavaScript и TypeScript

Проверяйте подписанные запросы и применяйте итоговое решение политики AgentBouncer.

### Установка

```bash

npm install @agentbouncer/sdk

```

### Создание клиента

```typescript

import {
  createAgentBouncer,
} from "@agentbouncer/sdk";

export const agentBouncer =
  createAgentBouncer({
    apiKey:
      process.env.AGENTBOUNCER_API_KEY!,
    publicOrigin:
      process.env.AGENTBOUNCER_PUBLIC_ORIGIN,
    verifyUrl:
      process.env.AGENTBOUNCER_VERIFY_URL,
  });

```

> **Конструктор класса**
>
> SDK также экспортирует AgentBouncer. Вызовы new AgentBouncer(options) и createAgentBouncer(options) создают один и тот же тип клиента.

> **Настройте публичный origin при работе за прокси**
>
> Если request.url содержит внутренний URL развёртывания или обратного прокси, задайте в publicOrigin внешний origin, который подписывают агенты. В качестве альтернативы можно передавать targetUrl при каждом вызове verify().

### verify()

```typescript

const verification =
  await agentBouncer.verify({
    request,
    expectedTag: "web-bot-auth",
    action: "tools:call",
    tool: "weather",
  });

if (!verification.allowed) {
  return new Response(
    "Доступ агента запрещён",
    {
      status: verification.verified
        ? 403
        : 401,
    }
  );
}

// Продолжите выполнение защищённой операции.

```

### require()

```typescript

import {
  AgentBouncerDeniedError,
  AgentBouncerError,
} from "@agentbouncer/sdk";

try {
  const verification =
    await agentBouncer.require({
      request,
      expectedTag: "web-bot-auth",
      action: "tools:call",
      tool: "weather",
    });

  return runWeatherTool({
    agent: verification.agent,
  });
} catch (error) {
  if (
    error instanceof
    AgentBouncerDeniedError
  ) {
    return new Response(
      "Доступ агента запрещён",
      {
        status:
          error.verification.verified
            ? 403
            : 401,
      }
    );
  }

  if (
    error instanceof AgentBouncerError
  ) {
    return new Response(
      "Сервис проверки агента недоступен",
      {
        status: 503,
      }
    );
  }

  throw error;
}

```

> **Семантика методов SDK**
>
> Метод verify() возвращает полный результат проверки. Метод require() выбрасывает AgentBouncerDeniedError, если allowed имеет значение false. Ошибки сети, тайм-ауты, ошибки аутентификации API и некоректные ответы вызывают AgentBouncerError.

<a id="nextjs"></a>

## Next.js App Router

Защитите маршрут Next.js, сохранив внешний URL запроса.

### src/lib/agentbouncer-client.ts

```typescript

import {
  createAgentBouncer,
} from "@agentbouncer/sdk";

const apiKey =
  process.env.AGENTBOUNCER_API_KEY;

const publicOrigin =
  process.env.AGENTBOUNCER_PUBLIC_ORIGIN;

if (!apiKey) {
  throw new Error(
    "Не задан AGENTBOUNCER_API_KEY"
  );
}

if (!publicOrigin) {
  throw new Error(
    "Не задан AGENTBOUNCER_PUBLIC_ORIGIN"
  );
}

export const agentBouncer =
  createAgentBouncer({
    apiKey,
    publicOrigin,
    verifyUrl:
      process.env.AGENTBOUNCER_VERIFY_URL,
  });

```

### src/app/api/mcp/weather/route.ts

```typescript

import {
  AgentBouncerDeniedError,
  AgentBouncerError,
} from "@agentbouncer/sdk";
import {
  NextRequest,
  NextResponse,
} from "next/server";
import {
  agentBouncer,
} from "@/lib/agentbouncer-client";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

export async function POST(
  req: NextRequest
) {
  const requestId = crypto.randomUUID();

  try {
    const verification =
      await agentBouncer.require({
        request: req,
        expectedTag: "web-bot-auth",
        action: "tools:call",
        tool: "weather",
      });

    const body = await req
      .json()
      .catch(() => ({}));

    const city =
      typeof body.city === "string"
        ? body.city
        : "Berlin";

    return NextResponse.json({
      ok: true,
      city,
      weather: {
        temperatureC: 21,
        conditions: "Ясно",
      },
      agent: verification.agent,
      requestId,
    });
  } catch (error) {
    if (
      error instanceof
      AgentBouncerDeniedError
    ) {
      return NextResponse.json(
        {
          ok: false,
          error: "mcp_access_denied",
          reason:
            error.verification.reason,
          requestId,
        },
        {
          status:
            error.verification.verified
              ? 403
              : 401,
        }
      );
    }

    console.error(
      "Не удалось выполнить проверку AgentBouncer",
      {
        requestId,
        error,
      }
    );

    return NextResponse.json(
      {
        ok: false,
        error:
          "agentbouncer_unavailable",
        message:
          error instanceof
          AgentBouncerError
            ? error.message
            : "Не удалось проверить запрос.",
        requestId,
      },
      {
        status: 503,
      }
    );
  }
}

```

> **Не используйте старую проверку доступа**
>
> Не используйте if (!verification.verified || !verification.allowed). Такая проверка ошибочно блокирует запросы, разрешённые режимом MONITOR_ONLY или настройкой allowUnsigned.

<a id="manual-nextjs"></a>

## Next.js без SDK

Отправляйте минимальную полезную нагрузку проверки непосредственно в REST API.

### Минимальный сбор заголовков

```typescript

import type {
  NextRequest,
} from "next/server";
import {
  pickVerificationHeaders,
} from "./pick-verification-headers";

function resolvePublicTargetUrl(
  req: NextRequest
) {
  const publicOrigin =
    process.env
      .AGENTBOUNCER_PUBLIC_ORIGIN;

  if (!publicOrigin) {
    return req.nextUrl.toString();
  }

  return new URL(
    `${req.nextUrl.pathname}${req.nextUrl.search}`,
    publicOrigin
  ).toString();
}

export async function verifyAgentRequest(
  req: NextRequest,
  options?: {
    expectedTag?: string;
    action?: string;
    tool?: string;
  }
) {
  const targetUrl =
    resolvePublicTargetUrl(req);

  const response = await fetch(
    process.env.AGENTBOUNCER_VERIFY_URL ??
      "https://agentbouncer.io/api/v1/verify",
    {
      method: "POST",
      headers: {
        Authorization:
          `Bearer ${process.env.AGENTBOUNCER_API_KEY}`,
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: JSON.stringify({
        url: targetUrl,
        method: req.method,
        headers:
          pickVerificationHeaders(req),
        expectedTag:
          options?.expectedTag ?? null,
        action:
          options?.action ?? null,
        tool:
          options?.tool ?? null,
        userAgent:
          req.headers.get("user-agent"),
      }),
      cache: "no-store",
    }
  );

  const result = await response
    .json()
    .catch(() => null);

  if (!response.ok) {
    throw new Error(
      result?.detail ||
        result?.reason ||
        `AgentBouncer вернул HTTP ${response.status}.`
    );
  }

  if (
    !result ||
    typeof result.verified !== "boolean" ||
    typeof result.allowed !== "boolean"
  ) {
    throw new Error(
      "AgentBouncer вернул некоректный ответ."
    );
  }

  return result;
}

```

### Вызов конечной точки проверки

```typescript

import type { NextRequest } from "next/server";
import { pickVerificationHeaders } from "./pick-verification-headers";

export async function verifyAgentRequest(
  req: NextRequest,
  options?: {
    expectedTag?: string;
    action?: string;
    tool?: string;
  }
) {
  const targetUrl = req.nextUrl.toString();

  const response = await fetch(
    process.env.AGENTBOUNCER_VERIFY_URL ??
      "https://agentbouncer.io/api/v1/verify",
    {
      method: "POST",
      headers: {
        Authorization:
          `Bearer ${process.env.AGENTBOUNCER_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        url: targetUrl,
        method: req.method,
        headers: pickVerificationHeaders(req),
        expectedTag: options?.expectedTag ?? null,
        action: options?.action ?? null,
        tool: options?.tool ?? null,
        userAgent: req.headers.get("user-agent"),
      }),
      cache: "no-store",
    }
  );

  const result = await response.json();

  if (!response.ok && response.status === 401) {
    throw new Error(
      "Не удалось пройти аутентификацию API AgentBouncer."
    );
  }

  return result;
}

```

<a id="express"></a>

## Интеграция с Express

Защищайте маршруты Express с помощью REST API проверки.

```typescript

import type {
  NextFunction,
  Request,
  Response,
} from "express";

export async function requireVerifiedAgent(
  req: Request,
  res: Response,
  next: NextFunction
) {
  try {
    const protocol =
      req.headers["x-forwarded-proto"] || req.protocol;

    const host =
      req.headers["x-forwarded-host"] ||
      req.headers.host;

    const targetUrl =
      `${protocol}://${host}${req.originalUrl}`;

    const verifyResponse = await fetch(
      "https://agentbouncer.io/api/v1/verify",
      {
        method: "POST",
        headers: {
          Authorization:
            `Bearer ${process.env.AGENTBOUNCER_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          url: targetUrl,
          method: req.method,
          headers: {
            signature: req.headers.signature,
            "signature-input":
              req.headers["signature-input"],
            "signature-agent":
              req.headers["signature-agent"],
            "content-type":
              req.headers["content-type"],
            "content-digest":
              req.headers["content-digest"],
          },
          expectedTag: "web-bot-auth",
          action: "api:call",
        }),
      }
    );

    const verification = await verifyResponse.json();

    if (!verification.allowed) {
      res.status(verification.verified ? 403 : 401).json({
        ok: false,
        error: "agent_access_denied",
        verification,
      });

      return;
    }

    res.locals.agentVerification = verification;
    next();
  } catch (error) {
    next(error);
  }
}

```

<a id="mcp"></a>

## Защита инструментов MCP

Применяйте решения AgentBouncer до выполнения операций MCP.

Укажите для action значение tools:call, а для tool — имя инструмента MCP. Благодаря этому события проверки и правила пользовательской политики будут относиться к конкретной запрашиваемой операции.

### Шаблон авторизации MCP

```typescript

const verification = await agentBouncer.verify({
  request,
  expectedTag: "web-bot-auth",
  action: "tools:call",
  tool: toolName,
});

if (!verification.allowed) {
  return {
    isError: true,
    content: [
      {
        type: "text",
        text: "Этому агенту запрещён вызов инструмента.",
      },
    ],
  };
}

switch (toolName) {
  case "weather":
    return getWeather(toolArguments);

  case "catalog":
    return searchCatalog(toolArguments);

  case "checkout":
    return createCheckout(toolArguments);

  default:
    throw new Error("Неизвестный инструмент MCP");
}

```

| Операция | Рекомендуемое действие | Рекомендуемый инструмент |
| --- | --- | --- |
| Чтение ресурса MCP | resources:read | имя ресурса |
| Получение списка инструментов | tools:list | * |
| Вызов инструмента | tools:call | имя инструмента |
| Чтение товаров | products:read | catalog |
| Создание оформления заказа | checkout:create | checkout |
| Отправка заказа | orders:create | orders |

<a id="project-signing-keys"></a>

## Ключи подписи проекта

Создавайте идентификаторы для агентов, непосредственно принадлежащих вашему проекту.

1. **Выпустите ключ проекта** — Откройте вкладку Agent keys проекта и создайте ключ с понятным названием, например «Production MCP agent» или «Internal CRM».
2. **Сохраните приватный JWK** — Приватный ключ показывается только один раз. Сохраните его в менеджере секретов агента.
3. **Сохраните публичный идентификатор** — AgentBouncer хранит открытый JWK, идентификатор ключа, идентификатор Signature-Agent, области доступа и конфигурацию политики.
4. **Подписывайте запросы** — Используйте приватный JWK для создания заголовков Signature и Signature-Input для точного целевого URL.
5. **Отзывайте скомпрометированные ключи** — Немедленно удалите или отзовите ключ проекта, если его приватный JWK был раскрыт.

### Пример переменных окружения

```bash

AGENT_KEY_ID=YOUR_KEY_ID
AGENT_SIGNATURE_AGENT=https://agentbouncer.io/project-agents/PROJECT_ID/KEY_ID
AGENT_PRIVATE_JWK='{"kty":"OKP","crv":"Ed25519","x":"...","d":"..."}'

```

<a id="providers"></a>

## Регистрация провайдера агентов

Опубликуйте идентификатор провайдера и каталог открытых ключей.

1. **Создайте провайдера** — Выберите постоянный slug, название провайдера, веб-сайт, origin Signature-Agent и URL каталога.
2. **Опубликуйте открытый JWKS** — Разместите открытые ключи в настроенном каталоге подписей HTTP-сообщений.
3. **Обновите ключи** — Запросите у AgentBouncer загрузку и проверку каталога.
4. **Подтвердите домен провайдера** — Опубликуйте DNS TXT-запись AgentBouncer или файл подтверждения.
5. **Подписывайте production-запросы** — Используйте приватный ключ, соответствующий активному открытому ключу в каталоге.

### Пример документа JWKS

```json

{
  "keys": [
    {
      "kty": "OKP",
      "crv": "Ed25519",
      "kid": "provider-key-2026-01",
      "alg": "EdDSA",
      "use": "sig",
      "x": "PUBLIC_KEY_MATERIAL"
    }
  ]
}

```

### Рекомендуемый публичный адрес

```text

https://agent.example/.well-known/http-message-signatures-directory/jwks.json

```

> **Никогда не публикуйте приватный JWK**
>
> Каталог провайдера должен содержать только публичные свойства JWK. Приватное свойство d должно оставаться секретным.

<a id="policies"></a>

## Режимы политик

Выберите, как проверенный идентификатор и данные о доверии влияют на доступ.

| Режим | Поведение |
| --- | --- |
| MONITOR_ONLY | Регистрирует ошибки проверки и политики, но разрешает запрос. |
| BLOCK_UNKNOWN | Блокирует неподписанные запросы и неизвестные идентификаторы. |
| ALLOW_TIER_1_ONLY | Разрешает доступ только провайдерам с наивысшим уровнем доверия. |
| ALLOW_TIER_1_AND_2 | Разрешает доступ доверенным провайдерам уровней Tier 1 и Tier 2. |
| CUSTOM | Оценивает правила провайдера, уровня доверия, ключа проекта, действия и инструмента. Совпавшие правила DENY имеют приоритет над совпавшими правилами ALLOW; если совпадений нет, применяется defaultEffect. |

> **Рекомендуемый порядок внедрения**
>
> Начните с MONITOR_ONLY, изучите реальный трафик, определите действия и инструменты, протестируйте ключи проекта и только после этого включайте блокировку.

<a id="custom-policies"></a>

## Пользовательские политики

Создавайте детальные разрешающие и запрещающие правила.

### Пример пользовательской политики

```json

{
  "version": 1,
  "defaultEffect": "DENY",
  "rules": [
    {
      "id": "rule_allow_tier_1_read",
      "name": "Разрешить Tier 1 чтение каталога",
      "enabled": true,
      "effect": "ALLOW",
      "subject": {
        "type": "PROVIDER_TIER",
        "tiers": ["TIER_1"]
      },
      "actions": ["products:read"],
      "tools": ["catalog"]
    },
    {
      "id": "rule_deny_external_checkout",
      "name": "Запретить внешнее оформление заказа",
      "enabled": true,
      "effect": "DENY",
      "subject": {
        "type": "ANY_PROVIDER"
      },
      "actions": ["checkout:create"],
      "tools": ["checkout"]
    }
  ]
}

```

- Правила DENY имеют приоритет над правилами ALLOW.
- Пустой массив actions соответствует любому действию.
- Пустой массив tools соответствует любому инструменту.
- Подстановочный символ * соответствует любому действию или инструменту.
- Если ни одно правило не совпало, применяется defaultEffect.
- Используйте PROJECT_KEY для выбора конкретных ключей внутренних агентов.

<a id="response"></a>

## Ответ проверки

Изучите поля, возвращаемые API проверки.

```json

{
  "verified": true,
  "allowed": true,
  "reason": null,
  "checks": {
    "signature": "valid",
    "timestamp": "valid",
    "replay": "unknown",
    "provider": "known",
    "policy": "matched"
  },
  "agent": {
    "type": "PROJECT_KEY",
    "provider": null,
    "providerSlug": null,
    "projectKey": {
      "id": "key_id",
      "name": "Production MCP agent",
      "scopes": []
    },
    "signatureAgent": "https://agentbouncer.io/project-agents/...",
    "keyid": "..."
  },
  "request": {
    "method": "POST",
    "path": "/api/mcp/weather",
    "tag": "web-bot-auth",
    "expectedTag": "web-bot-auth",
    "action": "tools:call",
    "tool": "weather"
  },
  "risk": {
    "score": 5,
    "level": "low"
  },
  "policy": {
    "mode": "CUSTOM",
    "wouldBlockReason": null,
    "matchedRule": {
      "id": "rule_allow_weather",
      "name": "Разрешить погодного агента",
      "effect": "ALLOW"
    },
    "defaultEffectApplied": false
  }
}

```

> **Обнаружение повторных запросов**
>
> Действительная подпись сама по себе не доказывает, что запрос не был воспроизведён повторно. Пока не включено хранилище защиты от повторов или проверка nonce, поле checks.replay возвращает unknown.

| Поле | Значение |
| --- | --- |
| verified | Указывает, была ли успешно выполнена криптографическая проверка идентификатора. |
| allowed | Итоговое решение об авторизации после применения политики проекта. |
| reason | Фактическая причина отказа или null, если доступ разрешён. |
| checks | Отдельные результаты проверки подписи, временной метки, повтора, провайдера, ключа проекта и политики. Значение unknown означает, что проверка не выполнялась или для неё не хватило информации. |
| agent | Определённый идентификатор провайдера или ключа проекта. |
| risk | Рассчитанная оценка и уровень риска. |
| policy | Режим политики, совпавшее правило и гипотетическая причина блокировки. |

<a id="errors"></a>

## Причины и ошибки

Распространённые причины результатов проверки и решений политики.

| Причина | Описание |
| --- | --- |
| unauthorized | API-ключ проекта AgentBouncer отсутствует или недействителен. |
| invalid_request | В полезной нагрузке проверки отсутствует обязательное поле. |
| invalid_url | Поле url не является допустимым абсолютным целевым URL. |
| no_signature | Отсутствует заголовок Signature или Signature-Input. |
| no_keyid | Заголовок Signature-Input не содержит keyid. |
| unknown_keyid | Активный ключ проекта или провайдера не найден. |
| not_yet_valid | Время создания подписи находится в будущем. |
| expired | Срок действия подписи истёк. |
| window_too_long | Период действия подписи от created до expires превышает допустимый максимум. |
| bad_signature | Криптографическая проверка завершилась неудачно. |
| unknown_provider | Провайдер ключа не распознан. |
| provider_blocked | Провайдер приостановлен, заблокирован, признан мошенническим или отозван. |
| provider_not_trusted | Проект требует доверенного провайдера. |
| unsupported_tag | Тег назначения подписи не поддерживается. |
| tag_mismatch | Фактический тег подписи не соответствует expectedTag. |
| tier_too_low | Провайдер не соответствует требуемому уровню доверия. |
| trust_score_too_low | Оценка доверия провайдера ниже порогового значения проекта. |
| project_keys_disabled | Ключи подписи, принадлежащие проекту, отключены. |
| custom_policy_denied | Совпавшее пользовательское правило DENY отклонило запрос. |
| custom_policy_no_match | Ни одно пользовательское правило не совпало, а эффект по умолчанию установлен в DENY. |

<a id="events"></a>

## События проверки

Отслеживайте реальный трафик и расследуйте решения об авторизации.

- Фильтруйте события по дню, неделе, месяцу, всему периоду или произвольному диапазону дат.
- Сравнивайте проверенные запросы с разрешёнными запросами.
- При обозначении события как принятого или отклонённого используйте allowed, а не verified.
- Проверяйте уровень и статус провайдера, оценку доверия, оценку злоупотреблений, риск, действие и инструмент.
- Просматривайте wouldBlockReason для запросов, разрешённых режимом MONITOR_ONLY.
- Экспортируйте данные событий в CSV для автономного анализа.

| verified | allowed | Рекомендуемая метка интерфейса |
| --- | --- | --- |
| true | true | Разрешён |
| true | false | Запрещён политикой |
| false | true | Разрешён в режиме мониторинга |
| false | false | Проверка не пройдена |

<a id="security"></a>

## Рекомендации по безопасности

Развёртывайте AgentBouncer без утечки секретов и ослабления проверки.

- Храните API-ключи AgentBouncer на сервере.
- Храните приватные JWK подписи в менеджере секретов.
- Публикуйте только открытые свойства JWK.
- Используйте HTTPS для целевых URL, идентификаторов Signature-Agent и каталогов ключей.
- Сохраняйте исходный внешний URL и HTTP-метод.
- Не передавайте Authorization, Cookie, Set-Cookie, x-api-key или учётные данные прокси в полезной нагрузке проверки.
- Исключайте заголовки обратного прокси и наблюдаемости, если они не подписываются намеренно.
- Используйте короткие периоды действия подписи.
- Отзывайте скомпрометированные ключи проекта и провайдера.
- Перед принудительным применением изменений политики сначала тестируйте их в режиме MONITOR_ONLY.

> **Целостность тела запроса**
>
> AgentBouncer проверяет подпись HTTP-сообщения по охватываемым компонентам запроса, переданным в API проверки. Приложения, использующие подписанный Content-Digest, также должны убедиться, что дайджест соответствует телу, полученному защищённой конечной точкой.

<a id="troubleshooting"></a>

## Устранение неполадок

Диагностируйте проблемы с подписью, URL, каталогом ключей и политикой.

| Проблема | Что проверить |
| --- | --- |
| Некоректный заголовок Signature | Убедитесь, что Signature, Signature-Input и Signature-Agent переданы ровно по одному разу. |
| bad_signature | Сравните подписанный URL, метод, authority, охватываемые компоненты, открытый ключ и дайджест содержимого. |
| unknown_keyid | Убедитесь, что ключ проекта активен, или обновите каталог открытых ключей провайдера. |
| tag_mismatch | Сравните expectedTag с тегом в Signature-Input. |
| custom_policy_no_match | Проверьте тип субъекта, идентификатор ключа проекта, уровень провайдера, действие, инструмент и defaultEffect. |
| MONITOR_ONLY продолжает блокировать запросы | Убедитесь, что приложение проверяет только verification.allowed. |
| Неверный @authority | Используйте внешний хост, который подписал агент, а не внутренний хост развёртывания. |

### Правильная проверка авторизации

```typescript

if (!verification.allowed) {
  return Response.json(
    {
      ok: false,
      error: "agent_access_denied",
      verification,
    },
    {
      status: verification.verified ? 403 : 401,
    }
  );
}

```

### Неправильная проверка авторизации

```typescript

// Неправильно: блокирует запросы в режиме MONITOR_ONLY.
if (!verification.verified || !verification.allowed) {
  return new Response("Доступ запрещён", {
    status: 403,
  });
}

```

<a id="production-checklist"></a>

## Контрольный список перед запуском

Проверьте интеграцию перед включением блокирующих политик.

- API-ключ AgentBouncer хранится только на сервере.
- Production-проект использует правильный публичный origin.
- Приложение отправляет точный подписанный целевой URL.
- Заголовки подписи передаются ровно по одному разу.
- Конфиденциальные и инфраструктурные заголовки исключены.
- Проверка авторизации использует allowed.
- Действия и имена инструментов MCP заполнены.
- Трафик MONITOR_ONLY был проанализирован.
- Известные провайдеры и ключи проекта определяются правильно.
- Пользовательские политики имеют намеренно выбранный эффект по умолчанию.
- Приватные ключи подписи хранятся в менеджере секретов.
- Процедуры отзыва и ротации ключей документированы.
- Экспорт событий проверки протестирован.
