Публичный 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.
| Метод + путь | BC | Scope | Источник |
|---|---|---|---|
GET /v1/whoami | cabinet/apikeys | whoami (sentinel) | backend/internal/core/cabinet/apikeys/di.go |
GET /v1/products | catalog/canonical | products:read | backend/internal/core/catalog/canonical/api/public-http/handler.go |
GET /v1/products/{id_or_viewable} | catalog/canonical | products:read | same |
POST /v1/products/resolve | catalog/canonical | products:read | same |
POST /v1/search/proposals | search/proposal | search: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) + количество.
Рабочий порядок:
- Проверить API-ключ:
GET /v1/whoami. - Если на руках supplier SKU, сопоставить строки через
POST /v1/products/resolve. - Отправить canonical refs и количества в
POST /v1/search/proposals. Для первого рабочего сценария по уже накопленной базе использоватьmode=reference: он читает cached-наблюдения и не требует customer supplier credentials.mode=liveнужен позже, когда у customer заведены свои учётные данные поставщиков для live-запросов. - По
items_outcome[]понять, по каким строкам есть предложения. - Через
proposals_by_item[query_item_id]найти предложения вproposals[]. - В каждом
proposalсмотретьsupplier_ref,seller_ref,price,stock,delivery,canonical_ref,offer_ref,position. - Если нужны описания и характеристики товара, взять уникальные
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-проверкой.
Статус документа
- Тип знания:
mixed — partial live + target service boundary - Статус реализации: auth fast-path и read-side endpoint’ы из «Live coverage» wired через api-server; остальные endpoint’ы из OpenAPI-спеки остаются target
- Текущее место кода:
backend/cmd/api-server— основной runtime, регистрирует все live/v1/*через GroupRegistrar’ы соответствующих BCbackend/internal/core/cabinet/apikeys— auth-фундамент (api_keys storage, verify-handler, /v1/whoami)backend/internal/core/catalog/canonical/api/public-http— products read endpointsbackend/internal/core/search/proposal/infra/http— search proposalsbackend/internal/platform/apikeyauth—Subject+RequireScope+ trusted-headers parser
- Что читать дальше: интерактивный контракт — Swagger UI, исходники —
../../20-architecture/schemas/api/index.md,../../20-architecture/schemas/api/public-api.openapi.yaml,../../20-architecture/schemas/api/grpc/public_api.proto,../../20-architecture/adr/0015-multi-protocol-public-api.md,../../20-architecture/adr/0051-public-api-auth-and-api-key-rbac.md
Область действия
Входит:
- HTTP/JSON endpoints.
- RPC-сервисы через Connect/gRPC/gRPC-Web.
- WebSocket подписки и push-обновления.
- Auth middleware, visibility application и freshness metadata.
Не входит:
- Сама бизнес-логика каталога, pricing, search и estimate.
- Административные endpoints.
Публичный контракт
Вход
- REST: canonical external URLs live under
/v1/*:GET /v1/products,GET /v1/products/{id_or_viewable},POST /v1/products/resolve,POST /v1/search/proposals,POST /v1/pricing,POST /v1/estimatesи связанные endpoints. В OpenAPI path keys omit/v1, потому чтоservers.urlуже содержит base path/v1. - RPC: сервисы из
../../20-architecture/schemas/api/grpc/public_api.proto. - WebSocket: подписочная модель из
../../20-architecture/schemas/api/asyncapi/public-ws.yaml.
Выход
- Вызовы во внутренние сервисы
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 var | Default | Описание |
|---|---|---|
PUBLIC_API_HTTP_PORT | 8080 | listener для REST/RPC |
PUBLIC_API_WS_PATH | /ws/v1 | путь WS endpoint |
PUBLIC_API_AUTH_OIDC_ISSUER | — | OIDC issuer |
PUBLIC_API_WS_MAX_CONNECTIONS_PER_CUSTOMER | 10 | лимит соединений |
Локальный запуск
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.
По умолчанию утилита проходит полный клиентский маршрут:
GET /v1/whoami— проверяет ключ и скоупы.POST /v1/products/resolve— отдельно показывает, как позиции входной сметы резолвятся в товары.POST /v1/estimates/proposals— подбирает предложения по списку позиций.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/whoamiunit + 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_activepublic_api_ws_events_dropped_total{reason}
Сейчас observability ограничена базовыми health/readiness и логами локального контейнера.
Открытые вопросы / TODO
- Добавить performance-бенчмарки по протоколам.
- Определить mTLS-стратегию для B2B gRPC.
- Автоматизировать генерацию контрактных схем из кода или наоборот.