Публичный API

NOTE

Статус: Mixed — часть контракта уже live (см. секцию «Live coverage»), остальное остаётся target. Документ описывает целевую сервисную границу и одновременно фиксирует, какие endpoint’ы реально wired на момент последнего обновления. Правила маркировки — в 50-processes/documentation-standard.md.

Этот README описывает сервисную границу public-api. Читайте его, когда нужно понять внешний контракт системы, какие endpoint’ы уже работают и как REST, RPC и WebSocket сходятся к одному набору бизнес-сценариев.

Live coverage (HEAD 64bce78, 2026-05-09)

Auth-fast-path через nginx auth_request /_internal/auth/api-key/verify (ADR-0051) полностью wired. Все пять endpoint’ов ниже валидируются через одно subrequest, кэшируются 30 секунд и получают trusted X-Tr-* headers перед api-server.

Метод + путьBCScopeИсточник
GET /v1/whoamicabinet/apikeyswhoami (sentinel)backend/internal/core/cabinet/apikeys/di.go
GET /v1/productscatalog/canonicalproducts:readbackend/internal/core/catalog/canonical/api/public-http/handler.go
GET /v1/products/{id_or_viewable}catalog/canonicalproducts:readsame
POST /v1/products/resolvecatalog/canonicalproducts:readsame
POST /v1/search/proposalssearch/proposalsearch:read (через RequireScopeOrLegacyJWT transition bridge)backend/internal/core/search/proposal/infra/http/handler.go

In-process BearerMiddleware удалён в Public API RBAC Phase 2 — токен валидируется только в auth-service / verify-handler, остальные процессы знают об identity через trusted headers.

Все остальные endpoint’ы из OpenAPI-спеки ниже остаются target — handler не зарегистрирован, scope в plannedScopes отвергается на POST /api/cabinet/keys и admin CreateKey через D9 active-vs-planned гейт. Активация: реализовать handler + перевести scope из plannedScopes в activeScopes (backend/internal/core/cabinet/apikeys/domain/scope.go).

Как пользоваться клиенту

Первый поддержанный сценарий — смета из supplier SKU или уже известных Tracium product refs (TR-XXXXXXX / canonical UUID) + количество.

Рабочий порядок:

  1. Проверить API-ключ: GET /v1/whoami.
  2. Если на руках supplier SKU, сопоставить строки через POST /v1/products/resolve.
  3. Отправить canonical refs и количества в POST /v1/search/proposals. Для первого рабочего сценария по уже накопленной базе использовать mode=reference: он читает cached-наблюдения и не требует customer supplier credentials. mode=live нужен позже, когда у customer заведены свои учётные данные поставщиков для live-запросов.
  4. По items_outcome[] понять, по каким строкам есть предложения.
  5. Через proposals_by_item[query_item_id] найти предложения в proposals[].
  6. В каждом proposal смотреть supplier_ref, seller_ref, price, stock, delivery, canonical_ref, offer_ref, position.
  7. Если нужны описания и характеристики товара, взять уникальные canonical_ref из выбранных предложений и вызвать GET /v1/products/{id_or_viewable}.

POST /v1/products/resolve сейчас live для exact selector supplier + supplier_sku. Поля для будущего resolver’а по manufacturer, mpn, query, characteristics[] уже есть в контракте, но пока возвращают unsupported_query. Нечёткий подбор аналогов по похожим характеристикам зарезервирован отдельным target endpoint’ом POST /v1/products/analog-search.

Подробный сценарий с curl-примерами: cookbook.md.

Назначение

Внешний API для клиентов системы: REST, Connect/gRPC/gRPC-Web и WebSocket поверх единых доменных сценариев и общего auth/freshness semantics.

Auth boundary зафиксирован в ADR-0051: пользовательский JWT-cookie обслуживает cabinet/admin UI, а public system-to-system вызовы используют opaque API tokens Authorization: Bearer trk_* со scope-проверкой.

Статус документа

Область действия

Входит:

  • HTTP/JSON endpoints.
  • RPC-сервисы через Connect/gRPC/gRPC-Web.
  • WebSocket подписки и push-обновления.
  • Auth middleware, visibility application и freshness metadata.

Не входит:

  • Сама бизнес-логика каталога, pricing, search и estimate.
  • Административные endpoints.

Публичный контракт

Вход

Выход

  • Вызовы во внутренние сервисы catalog-core, search, pricing, meta-search, enrichment, visibility.
  • Подписки на Kafka-топики для realtime-обновлений в WS.
  • Постановка enrichment/discovery jobs.

Внутренняя архитектура

В целевом состоянии это transport/API edge-слой с трёмя адаптерами доступа: REST, RPC и WS. Он не хранит доменную логику, а приводит внешний контракт к внутренним use case’ам и поддерживает единые semantics ошибок, freshness и авторизации.

Зависимости

  • Все ядерные сервисы и visibility.
  • Kafka для WS подписок.
  • Redis для rate limit и кэшей, если потребуется.
  • OIDC/auth subsystem.

Хранилище

  • Собственного основного domain-storage не предполагает.
  • Может использовать Redis и служебные persistence-структуры для WS/subscription management.

Конфигурация

Env varDefaultОписание
PUBLIC_API_HTTP_PORT8080listener для REST/RPC
PUBLIC_API_WS_PATH/ws/v1путь WS endpoint
PUBLIC_API_AUTH_OIDC_ISSUEROIDC issuer
PUBLIC_API_WS_MAX_CONNECTIONS_PER_CUSTOMER10лимит соединений

Локальный запуск

Web-контур со всем nginx + auth-service + api-server поднимается командой:

make local-prod-up

Доступен по https://api.tracium.dev:4444. Live-coverage endpoint’ы можно проверить напрямую (требуется выпустить API-ключ через /api/cabinet/keys после JWT-cookie логина):

curl -H "Authorization: Bearer trk_live_..." https://api.tracium.dev:4444/v1/whoami
curl -H "Authorization: Bearer trk_live_..." https://api.tracium.dev:4444/v1/products

Для последовательной отладки сценария сметчика используйте клиентскую консольную утилиту. Она принимает любой заранее подготовленный estimate-request.v1 JSON, вызывает реальные публичные endpoint’ы и печатает в stdout структурированные JSON-события request/response.

По умолчанию утилита проходит полный клиентский маршрут:

  1. GET /v1/whoami — проверяет ключ и скоупы.
  2. POST /v1/products/resolve — отдельно показывает, как позиции входной сметы резолвятся в товары.
  3. POST /v1/estimates/proposals — подбирает предложения по списку позиций.
  4. GET /v1/products/{id_or_viewable} — загружает карточки выбранных товаров и первых кандидатов, чтобы в логе были описания, характеристики, цены, остатки, условия доставки и supplier offers.
TRACIUM_API_KEY=trk_live_... \
python3 scripts/public_api_estimate_e2e.py \
  --input /path/to/estimate-request.json \
  --base-url https://api.tracium.dev:4444 \
  --insecure \
  --llm-assisted \
  --output-log /tmp/tracium-estimate-trace.jsonl

Загрузку карточек можно ограничить, чтобы не получить слишком большой лог:

python3 scripts/public_api_estimate_e2e.py \
  --input /path/to/estimate-request.json \
  --details-candidate-limit 2 \
  --details-limit 30 \
  --output-log /tmp/tracium-estimate-trace.jsonl

Для быстрой проверки можно ограничить вход:

python3 scripts/public_api_estimate_e2e.py \
  --input /path/to/estimate-request.json \
  --line-limit 3 \
  --resolve-limit 3

Если утилита запускается внутри docker-compose сети и обращается напрямую к api-server, а не через nginx/auth-service, можно включить локальные trusted headers:

docker run --rm --network tracium_network \
  -v "$PWD:/workspace" -w /workspace python:3.12-alpine \
  python scripts/public_api_estimate_e2e.py \
    --input /workspace/tmp/estimate-request.json \
    --base-url http://api:8080 \
    --local-trusted \
    --line-limit 10 \
    --details-candidate-limit 2 \
    --details-limit 10 \
    --jsonl

Этот режим нужен только для локальной диагностики прямого api-server; внешний клиент должен использовать обычный Authorization: Bearer trk_live_....

Endpoint’ы, оставшиеся в target-зоне OpenAPI-спеки, возвращают 404 (handler не зарегистрирован) или отвергаются на этапе issuing key через D9 active-vs-planned гейт.

Тестирование

  • Live endpoint’ы покрыты:
    • backend/internal/core/cabinet/apikeys/api/http/v1/whoami_handler_test.go/v1/whoami unit + integration.
    • backend/internal/core/catalog/canonical/api/public-http/handler_test.go — products read и resolver.
    • backend/internal/core/search/proposal/infra/http/handler_test.go — search proposals.
    • backend/internal/platform/apikeyauth/require_scope_test.go + legacy_jwt_fallback_test.go — scope-проверки.
    • backend/internal/core/cabinet/apikeys/api/http/verify/... — verify-handler integration (nginx subrequest contract).
  • Контрактная проверка spec ↔ код: make backend-openapilint-public (на 2026-05-09 — 70 MissingInCode by design — это target-spec; alarm только при появлении новых drift’ов).
  • Для целевых endpoint’ов нужны unit-тесты handlers/adapters, integration-тесты auth/freshness semantics и контрактная проверка OpenAPI/proto/AsyncAPI.

Наблюдаемость

В целевом состоянии сервис должен публиковать метрики:

  • public_api_requests_total{protocol,endpoint,status}
  • public_api_request_duration_seconds{protocol,endpoint}
  • public_api_ws_connections_active
  • public_api_ws_events_dropped_total{reason}

Сейчас observability ограничена базовыми health/readiness и логами локального контейнера.

Открытые вопросы / TODO

  • Добавить performance-бенчмарки по протоколам.
  • Определить mTLS-стратегию для B2B gRPC.
  • Автоматизировать генерацию контрактных схем из кода или наоборот.

Связанные документы