Skip to content

Latest commit

 

History

History
63 lines (40 loc) · 13.7 KB

API_CONTRACT_GUARD.md

File metadata and controls

63 lines (40 loc) · 13.7 KB

Проверяемый контракт API

Реализовано 2026-09-25. OpenAPI 3.0.3, HTTP namespace остаётся /api/v1. Документ описывает все операции, включая явные и автоматически зарегистрированные HEAD, liveness и endpoint самой спецификации; точный состав фиксирует снимок tests/fixtures/api-operations.json. Других протоколов и префиксов, кроме /api/v1, /health и /console/, сервер не публикует.

Получение спецификации

GET /api/v1/openapi.json требует действующий Bearer и возвращает документ конкретной сборки. После npm run build идентичный JSON находится в packages/contracts/dist/openapi.json; доступ к работающему серверу и credentials для этого не нужны. Сборка не читает deployment-конфигурацию и не записывает секреты в артефакт.

Для UI/SDK спецификация не обязательна при каждом запросе: portable TypeScript SDK использует существующие runtime-парсеры DTO. Внешние языки могут использовать JSON для генерации клиентов, но должны отдельно поддержать streaming, Range/recovery и Bearer. Сгенерированный клиент не освобождает от проверки hashes и статуса upload после потери ответа.

Что проверяется автоматически

packages/contracts/src/operation-policy.ts описывает operationId, tag, авторизацию, ресурс/ownership и правило повтора. Wire-схемы остаются в native/catalog/identity/service modules. openapi-compose.ts объединяет их, отклоняет отсутствие policy, дубли operationId и policy без схемы, а также дополняет HEAD, ошибки и транспортные заголовки.

apps/api/src/contract-guard.ts собирает реальный Fastify onRoute inventory до регистрации handlers. Перед готовностью сервера сверяются обе стороны: любой неописанный маршрут либо отсутствующий handler вызывают API route drift. Guard включён для writer и reader. Маршруты рядом с /api/v1 (/v2/, /lfs/, /npm/) исключаются по префиксам включённых модулей процесса (ADR 0079, п. 5.7), а не ручным списком: маршрут выключенного протокола был бы неописанным, а префикс включённого модуля без маршрутов — тоже API route drift. Маршруты модулей со службой routes под /api/ext/<id>/v<major>/ сверяются с объявленными модулем в обе стороны (неописанный и незарегистрированный маршрут — API route drift: module routes), сводный документ GET /api/ext/openapi.json — точное исключение; учитываются только модули, работающие в процессе (ADR 0083, N12, «Y6»). Сбой не открывает частично зарегистрированный сервер для listen; стартующий процесс закрывает ресурсы через обычный startup cleanup.

Исключены только GET/HEAD семи статических файлов /console/, перечисленных в guard по точному пути. Общего исключения /console/* нет. CORS OPTIONS обслуживаются onRequest, не регистрируют отдельный endpoint; они сохраняют собственные origin/method/header проверки и тесты. Неизвестные пути/default 404 — поведение транспортного слоя, не контрактный endpoint.

Тесты проверяют:

  • Снимок method/path/operationId в tests/fixtures/api-operations.json: переименование не проходит незаметно. Изменение снимка требует обычного review совместимости.
  • Все path parameters, ссылки на auth schemes, metadata доступа, компилируемость request/response JSON schemas, отсутствие тела у HEAD/204/304/416 и идентичность экспортированного JSON.
  • Через настоящие HTTP-сокеты — границы аутентификации всех операций, в том числе HEAD с ранним отказом.
  • Все изменяющие операции на reader возвращают 405/read_only до application work.
  • Реальные PostgreSQL/HTTP ответы uploads, jobs, каталога, services/keys, ошибки CAS/revoke, bytes по ID, пакету и пути файла, Range/ETag соответствуют опубликованным схемам.
  • Новая неописанная runtime route останавливает readiness; лишняя или отсутствующая route проваливает сверку.
  • Полная спецификация и серверный inventory не попадают в браузерный bundle через SDK.

Это проверка инвентаризации и контрактных сценариев, а не доказательство всех комбинаций ACL или всех возможных JSON-ответов. Прежние domain, storage, SDK, ACL и отказные тесты сохраняются. Метаданные спецификации не исполняют авторизацию: её источником остаются use cases и транзакционные проверки managed credential.

Расширения OpenAPI

Поле операции Значение
operationId Стабильное имя; прежние 15 service IDs сохранены
x-arkvory-authorization kind: public/authenticated/account-session/administrator/service-bootstrap/bootstrap-or-own-key/repository-discovery/service-administration/pending-or-active-key либо repository
actions, legacy в repository authorization Все требуемые managed actions; прежние coarse read/write, не wildcard
resource path.repository либо job.repository, разрешаемый по заданию
owner Дополнительная проверка upload/job/reference owner, когда требуется
x-arkvory-gateway writer либо writer-or-reader; наличие схемы не разрешает запись на reader
x-arkvory-retry Семантика повтора, описанная ниже; не инструкция бесконечно повторять запрос
x-arkvory-route Точный зарегистрированный шаблон для inventory, включая :repository
x-arkvory-streaming Предел объекта/запроса, checksum/recovery либо Range и способ разрешения объекта

read — чтение можно повторять с ограниченным бюджетом; это не snapshot. never-automatic — потеря ответа требует отдельного решения вызывающего клиента. idempotent — целевое состояние допускает повтор; auth/expiry, аудит и конкурентные изменения всё равно учитываются. Например повтор logout с уже отозванной сессией может получить 401.

idempotency-key — сохранять тот же ключ и то же тело; secret выданного service key повторно не возвращается. compare-and-swap — после 409 перечитать состояние; нельзя автоматически менять expectedRevision ради успешного overwrite. reconcile-upload — сначала состояние upload/parts, затем безопасное продолжение; whole PUT нельзя продолжить с середины. reconcile-job — проверить задание; новый credential может переавторизовать queued/failed job по действующим правилам. 401/403 не повторяются автоматически, 503 требует учесть Retry-After и неопределённость результата mutations.

Защищённые операции принимают только Authorization: Bearer; других схем (API-key header, Basic, ключ в query string) нет. Permissions не помещаются в OAuth scopes.

Потоки и HEAD

GET bytes описывает 200/206, binary content, ETag, Content-Length, Content-Range и Accept-Ranges; 304/416 без тела. HEAD не имеет тела, учитывает If-None-Match и игнорирует Range, поэтому 206/416 для него не объявлены. Автоматические HEAD метаданных имеют те же проверки доступа, что GET.

immutableBytes относится к опубликованным bytes. packages/content и asset/content разрешают каталог заново на каждом запросе: latest и путь файла могут указывать на новую версию или ревизию, поэтому сам URL нельзя считать неизменяемым. Для закрепления объекта используйте If-Range с полученным ETag либо скачивание по artifact ID. Идентичность пакета и путь файла передаются query-параметрами, без catch-all сегментов пути.

Размер объекта ограничен capabilities.limits.maxObjectBytes (по умолчанию предел multipart-раскладки, около 10 TiB); размер части задаётся сессией (partBytes, 8 MiB–1 GiB). Незавершённый HTTP request не подтверждает частичную запись. Error schema едина: code/message/requestId; HEAD передаёт только status/headers. Существующие форматы и HTTP-методы не изменены.

Добавление операции

Добавить use case и server handler, wire-схемы и строку operation policy; определить стабильное имя, реальные права и retry. Обновить SDK и карту API, затем осмысленно обновить снимок operations. Запустить npm run check, npm test, PostgreSQL integrations и относящиеся ACL/отказные сценарии. Не расширять static exclusions ради прохождения проверки.

Сам inventory guard не требует таблиц; версию runtime определяет packages/infrastructure/src/schema-version.ts (52 в ядре 0.6.3). Service-administration policies задают action, точную область resource и bootstrapAlternative; это metadata, не замена транзакционной авторизации. Наличие событий, управления ключами и HA проверяется по текущей реализации и гейтам, а не по списку будущих этапов первоначального guard: CURRENT_STATE, CI.