Карта API Arkvory
Управление установленным кодом: GET/HEAD /api/v1/system/updates и POST /api/v1/system/updates/requests. Surface administration, глобальный administrator, команды check/configure/apply с revision и UUID. SDK: client.updates. Контракт, права и восстановление.
Резервные копии экземпляра (B2, ADR 0056): /api/v1/backup/*, surface administration, системные права backup.read/backup.manage (администратор учётных записей или файловый ключ владельца/bootstrap; ключи репозиториев — 403). Подробности — раздел 1a. Схема БД введена в 26; актуальная — 34 (SCHEMA_VERSION).
Области ответственности, уровни видимости, каталог операций текущего credential и SDK для удалённых клиентов реализованы: API_SURFACES. GET/HEAD /api/v1/operations — authenticated, контекст repository необязателен, pagination до 100, advisory conditions. GET/HEAD /api/v1/openapi.json?surface=… — документационное представление. Актуальная схема БД — 32.
Дата сверки: 2026-10-05, runtime со схемой 32. Это точка входа для интеграторов: реализованные маршруты ниже отделены от проектируемых расширений. Детальные JSON-схемы текущего сервера: GET /api/v1/openapi.json с действующим Bearer. OpenAPI сейчас 3.0.3; новую модель доступа описывает API_ACCESS, порядок внедрения — API_EVOLUTION.
Физическая очистка: GET/HEAD R/storage/cleanup (storage.read), PUT того же пути и POST R/storage/cleanup/run (storage.manage). Настройки с CAS, динамическая пауза, ограниченный проход и результат последнего выполнения: ONLINE_CLEANUP. Выполняется в фоне без остановки HTTP; 200 при запросе прохода не означает завершение удаления.
Обозначения и общие правила
R = /api/v1/repositories/{repository}. read, write, administrator — реальные сегодняшние проверки; own означает того же principal, не просто любой ключ с write. Пользовательская сессия также допустима. Администратор не обходит read/write. В таблице «целевое право» permissions репозиторных операций разделов 2–3 уже действуют для managed keys; глобальные identity/policy permissions пока проектные. Service administration выполняется file bootstrap либо делегатом с отдельными действиями на точные аккаунты, см. текущий контракт ключей. Несколько прав через + обязательны одновременно.
У reader доступны только GET/HEAD, кроме инфраструктурного CORS preflight; изменяющие методы получают 405 даже при наличии права. /console/ размещается только на writer. Автоматические HEAD у Fastify GET могут присутствовать; ниже перечислены контрактные методы, для bytes HEAD указан явно.
Сейчас размеры JSON — десятичные строки, revision — ограниченное целое; timestamps UTC. JSON body по умолчанию до 64 KiB (больше — 413), upload content передаётся отдельным бинарным stream. Ошибка native: {code,message,requestId} и необязательные reason, details, retryAfterSeconds (ошибки). Неизвестный путь — 404 до аутентификации, иной метод известного пути — 405 с Allow, неподдерживаемый тип тела — 415. Нет/неверный/истёкший credential — 401 с причиной; недостаточно права — 403; чужой upload/job скрывается 404; конфликт — 409; hash mismatch — 422; исчерпанная ёмкость и ENOSPC/EDQUOT — 507; busy/unavailable, включая потерю соединения с PostgreSQL и drain при остановке, — 503 с Retry-After; непредвиденная ошибка сервера — 500 internal без Retry-After, её нельзя повторять автоматически. Не считать любой POST безопасным для автоматического retry.
1. Система и identity — реализовано
| Метод и путь | Текущий доступ | Результат / важное условие | Целевое право |
|---|---|---|---|
GET /health/live | Без ключа | 200, минимальная liveness | Публичный минимум |
GET /health/status | Без ключа | 200 ready / 503 unavailable, draining; без подробностей, вне бюджета запросов | Публичный минимум для балансировщика |
GET /health/ready | Любой действующий ключ/сессия | 200/503, writable, role, агрегаты admission/bandwidth; вне бюджета запросов | Совместимый прежний доступ; подробная диагностика отдельно system.observe |
GET /health/metrics | Любой действующий ключ/сессия | 200 Prometheus text 0.0.4; шаблон маршрута, метод, класс; вне бюджета | Как readiness; подробная диагностика отдельно system.observe |
GET /api/v1/openapi.json | Любой действующий ключ/сессия | Рабочая спецификация | Аутентифицированный доступ |
POST /api/v1/auth/login | Без предварительного ключа | 200, token/expiresAt/account; password gate | Учётные данные пользователя |
GET /api/v1/auth/me | Любой действующий ключ/сессия | ID, administrator, точные repository grants | Своя identity |
POST /api/v1/auth/logout | Действующая авторизация | 204; отзыв текущей пользовательской сессии. Не отзывает file-based service key | Своя сессия |
POST /api/v1/auth/password | Пользовательская сессия | 204; currentPassword + newPassword, отзывает все сессии и PAT | Своя учётная запись |
POST /api/v1/auth/register | Без предварительного ключа | 201, сессия; при выключенной selfRegistration — 403, лимит по адресу | Публичный минимум при включённой регистрации |
POST /api/v1/feedback | Любой действующий ключ/сессия | 202, обращение авторам продукта; с serverLog — только администратор (ADR 0060) | Свои учётные данные |
GET /api/v1/feedback/attachments | administrator, не managed key | Серверный журнал и сводка установки, которые можно приложить к обращению | Администратор |
GET /api/v1/auth/options | Без ключа | 200, selfRegistration | Публичный минимум |
GET / POST / DELETE /api/v1/auth/tokens[/{id}] | Только сессия, не PAT | PAT: срок ≤ 365 дней, scope read/read-write, без administrator | Свои учётные данные |
GET / DELETE /api/v1/users/{id}/tokens[/{tokenId}] | administrator, не PAT | Список и отзыв PAT учётной записи | identity.manage |
GET /api/v1/security/audit | administrator, не PAT | Append-only журнал identity, курсор after, limit ≤ 100 | identity.read |
GET /api/v1/users | administrator | items, текущий предел 1000 пользователей | identity.read |
POST /api/v1/users | administrator | 201; name/password/administrator | identity.manage, с ограничением назначения полномочий |
PATCH /api/v1/users/{id} | administrator | 200; enabled и/или password; не изменение administrator | identity.manage |
GET /api/v1/access-groups | administrator | Группы с members/grants; до 100 групп | identity.read |
POST /api/v1/access-groups | administrator | 201; name | identity.manage |
PUT / DELETE /api/v1/access-groups/{id}/members/{userId} | administrator | 204; добавить/убрать membership | identity.manage, membership не обходит delegation ceiling |
PUT / DELETE /api/v1/access-groups/{id}/grants/{repository} | administrator | 204; access read/write либо снять grant | policy.manage + разрешение делегировать конкретные actions/resources |
CORS: OPTIONS для /api/v1/* и /health/ready проверяет origin/method/headers без Bearer. Это не разрешение на саму операцию; см. EXTERNAL_UI. /console/ — статический клиент, не API управления.
1a. Резервные копии — реализовано
Команды выполняет агент arkvory-backup agent; API ставит задания в очередь и читает состояние из БД, не принимает и не возвращает путей. Права проверяются до разбора входа. Байты — десятичные строки, время — ISO UTC. SDK: client.backup; CLI: arkvoryctl backup.
| Метод и путь | Право | Результат / условие |
|---|---|---|
GET /api/v1/backup/status | backup.read | vault {configured,id,available,encrypted,freeBytes,totalBytes} (encrypted — boolean или null: неизвестно, агент старой версии или нет vault.json; ADR 0070), agent {online,lastSeenAt,version}, plan, lastCompleted, nextRunAt, running, warnings |
GET /api/v1/backup/plan | backup.read | {enabled,hour,minute,timezone,retention{daily,weekly,monthly},revision} |
PUT /api/v1/backup/plan | backup.manage | Тело плана + expectedRevision; 200 план, 409 revision_mismatch, 400 validation с details (JSON Pointer) |
POST /api/v1/backup/runs | backup.manage | Idempotency-Key обязателен; 202 {id,kind:'capture',state}; повтор ключа — то же задание; 507 queue_full при 100 открытых |
POST /api/v1/backup/points/{id}/verify | backup.manage | Idempotency-Key; 202 {id,kind:'verify',state}, глубокая проверка; 404 для неизвестной точки; 409 idempotency_mismatch для ключа другой точки |
POST /api/v1/backup/retention/apply | backup.manage | Idempotency-Key; 202 {id,kind:'retention',state} |
GET /api/v1/backup/jobs?after&limit | backup.read | {items: Job[], next}, новые первыми, limit 1–100 (50); Job {id,kind,state,phase,startedAt,finishedAt,errorCode,pointId,progress} |
GET /api/v1/backup/points?after&limit | backup.read | {items: Point[], next}, новейший T первым; Point {id,snapshotAt,completedAt,blobs,contentBytes,newBytes,tables,rows,pinned,verifiedAt,verifyDepth,…} |
PUT /api/v1/backup/points/{id}/pin | backup.manage | {pinned: boolean} → Point; 404 для неизвестной или забытой точки |
GET /api/v1/backup/retention/preview | backup.read | {keep: {id,reasons}[], delete: {id}[]}; reasons daily/weekly/monthly/pinned/newest; рекомендательный, apply решает заново по vault |
state задания: queued|running|committing|completed|failed|interrupted; kind: capture|verify|retention. progress {bytesCopied,bytesTotal,blobsCopied,blobsTotal}: для копии — обработанные (скопированные или уже имевшиеся в vault) байты и объекты, для проверки — проверенные, для retention — удалённые blob и освобождённые байты. Коды предупреждений и уровни — CORE_RUNBOOK. GET также доступны как HEAD.
2. Загрузки, задания и байты — реализовано
| Метод и путь | Текущий доступ | Контракт / повтор | Целевое право |
|---|---|---|---|
POST R/uploads | write | 201, descriptor; Idempotency-Key обязателен, конфликт другого descriptor | upload.create |
GET R/uploads/{id} | write + own | Состояние собственной сессии | upload.read + own |
DELETE R/uploads/{id} | write + own | 200, отменяет pending; опубликованный объект не удаляет | upload.cancel + own |
PUT R/uploads/{id}/content | write + own | 200, целый octet-stream с проверкой размера/SHA и публикацией. После потери ответа сначала status | upload.write + upload.complete + own |
GET R/uploads/{id}/parts | write + own | partBytes и принятые parts; часть от 8 MiB, до 1 GiB при >10 000 частей | upload.read + own |
PUT R/uploads/{id}/parts/{index} | write + own | 204; X-Content-SHA256, повтор той же части безопасен; другой hash — 409 | upload.write + own |
POST R/uploads/{id}/complete | write + own | 200; повтор завершённой сессии возвращает опубликованный результат | upload.complete + own |
POST R/uploads/{id}/complete-async | write + own | 202, durable job; публикация подтверждается отдельно | upload.complete + own |
GET /api/v1/jobs/{id} | write в repo задания + own | queued/running/completed/failed, attempts и errorCode | job.read + own |
GET R/artifacts | read | items/next; limit 1–100, по умолчанию 50, after | artifact.list |
GET R/artifacts/{id} | read | Только available; descriptor, не bytes | artifact.read |
GET / HEAD R/artifacts/{id}/content | read | GET: 200/206/304/416, ETag, Range/If-Range. HEAD не передаёт body и игнорирует Range | content.read |
GET / HEAD R/packages/content?group=…&name=…&version=… | read | Точная версия UPack; пустой group — корневая группа. Ответ как у artifacts/{id}/content | content.read |
GET / HEAD R/packages/content?name=…&range=…&stage=… | read | Версия по SemVer-диапазону и стадии, без них — старшая стабильная; правила | content.read |
GET / HEAD R/asset/content?path=… | read | Текущая ревизия файла по пути, исходные bytes; ответ как у artifacts/{id}/content | content.read |
packages/content (downloadPackageContent) и asset/content (downloadAssetContent) разрешают один неизменяемый артефакт на момент запроса и дальше используют тот же путь выдачи, что artifacts/{id}/content: ACL content.read без права перечислять пакеты/assets, admission, bandwidth, Range/ETag/If-Range. ETag относится к выбранному содержимому, поэтому If-Range после смены текущей ревизии или старшей версии не склеивает разные объекты. Аутентификация, как и у остальных операций, — Authorization: Bearer.
Upload owner привязан к principal. Долговечность сейчас — local filesystem + PostgreSQL; наличие маршрута resume не означает сохранность после потери диска/узла. Вся схема восстановления: TRANSFER_RECOVERY.
3. Каталог, metadata и ссылки — реализовано
| Метод и путь | Текущий доступ | Контракт / граница | Целевое право |
|---|---|---|---|
GET R/artifacts/{id}/annotations | read | revision, labels, metadata, collections | annotation.read |
PUT R/artifacts/{id}/annotations | read + write | expectedRevision + value; CAS, 409 при конфликте | annotation.write + artifact.read |
POST R/artifacts/{id}/package | read + write | Читает и валидирует исходный UPack; регистрирует неизменяемую идентичность | package.publish + artifact.read |
GET R/packages | read | group/name, sort/direction/groupBy, after/limit; items/groups/next | package.read |
GET R/assets | read | prefix; items, текущий предел 1000, полноценного cursor нет | asset.read |
GET / HEAD R/assets/page | read | prefix/after/limit; items/next, до 100; literal prefix и UTF-8 порядок, контракт | asset.read |
GET R/asset?path=… | read | Текущий path/revision/artifactId | asset.read |
PUT R/asset | read + write | path/artifactId/expectedRevision; 0 для создания, CAS | asset.write + artifact.read исходника |
GET R/asset/history?path=…&before=… | read | До 50 ревизий и next | asset.read |
GET R/asset/revision?path=…&revision=… | read | Точная ревизия и artifactId | asset.read |
POST R/asset/restore | read + write | path/sourceRevision/expectedRevision; создаёт новую revision, не меняет старый blob | asset.restore + asset.read + artifact.read |
GET R/search | read | q/label/collection/metadata*/after; до 100 items (size, даты, labels, stages) и next | artifact.list; metadata и collections не возвращаются — нужен annotation.read |
GET R/audit?after=… | write | До 100 событий каталога; after — sequence, не аудит всех auth/download событий | audit.read |
GET R/changes?after=…&limit=… | read | Лента изменений репозитория для зеркал: публикация, удаление, аннотации, пакеты, пути (detail — путь), стадии (detail — стадия); порядок commit, без авторов; items/head/next, до 100 (ADR 0058) | artifact.list |
GET R/mirror | read | Состояние зеркального репозитория этой установки: фаза, caughtUp, ошибка, объём; 404 для обычного (ADR 0058) | repository.read |
POST / DELETE R/artifacts/{id}/references | read + write, reference owner | 204; JSON body key, ссылка принадлежит principal | reference.write + artifact.read |
GET R/artifacts/{id}/stages | read | Текущие стадии продвижения, до 16 | artifact.read |
PUT / DELETE R/artifacts/{id}/stages/{s} | read + write | Идемпотентно добавляет/снимает стадию с журналом; body {comment} | artifact.promote + artifact.read |
POST R/artifacts/{id}/promote | read (источник), write (цель) | {target, mode: copy|move, stages, comment}; 201 копия, 200 существующая; контракт | источник artifact.read + content.read, цель artifact.promote |
GET R/artifacts/{id}/promotions, R/promotions | read | Журнал стадий и переносов по sequence, after/limit до 100 | artifact.read / artifact.list |
GET R/stages?stage=…&ids=… | read | Артефакты со стадией, cursor artifactId/stage; ids — до 100 artifact ID через запятую | artifact.list |
GET R/packages/resolve | read | name/group + version|range, stage, prerelease, order; версия, artifactId, SHA-256, стадии | package.read |
DELETE R/artifacts/{id} | Только managed key | Логическое удаление; body {expectedAnnotationRevision} обязателен, зависимости и CAS проверяются всегда; 200 не означает, что файл удалён физически (ARTIFACT_RETENTION) | artifact.delete |
GET R/artifacts/{id}/deletion | Только managed key | Инспекция опубликованного артефакта: блокирующие зависимости; без bytes | artifact.delete |
POST R/retention/preview | Только managed key | Страница кандидатов по criteria/limit/after, без изменений; на reader 405 | artifact.delete |
POST R/retention/apply | Только managed key | Повторно проверяет и обрабатывает только перечисленные IDs; результат по каждому объекту | artifact.delete |
GET / PUT R/storage/policy | Только managed key | Квоты и автоочистка; PUT с expectedRevision, CAS (STORAGE_POLICIES) | storage.read / storage.manage |
GET R/storage/usage | Только managed key | Опубликованные, незавершённые и ожидающие GC байты | storage.read |
GET R/storage/preview | Только managed key | До 100 кандидатов сохранённой политики, hasMore | artifact.delete |
POST R/storage/run | Только managed key | Одна порция включённой политики, body {expectedRevision}; на reader 405 | storage.manage + artifact.delete |
GET R/storage/events | Только managed key | События по возрастающему sequence, after, level, до 100 | diagnostics.read |
POST R/artifacts/{id}/links | read (как content.read) | 201 {token, url, expiresAt}; body {} или {ttlSeconds} (60–86400, по умолчанию 3600); токен dtl_… только в этом ответе; ссылка не выдаёт ссылок (ADR 0062) | content.read |
PUT R/raw/{assetPath} | read + write | Новая ревизия пути одним запросом (X-Checksum-Sha256, If-None-Match: *); 201 новая, 200 те же байты; 409 revision_mismatch; на reader 405 (ADR 0064) | upload.*, asset.read + asset.write, artifact.read |
GET / HEAD R/raw/{assetPath} | read | Текущая ревизия пути, Range, ETag, X-Arkvory-Artifact-Id | content.read |
Пустой group= выбирает корневую группу; отсутствие group — все группы. Cursor пакетов связан с параметрами запроса, страницы не образуют snapshot. SDK допускает package page и own permissions до 8 MiB; остальные JSON-ответы до 2 MiB. Эти различия сохраняются до отдельного совместимого изменения.
Отдельного CRUD для schemas, labels и collections пока нет: labels/collections — ограниченные поля annotations. Нет REST-управления репозиториями. Native удаление опубликованного содержимого есть, но только логическое и только для управляемого ключа с artifact.delete: DELETE R/artifacts/{id}, deletion и retention/* ниже; физическое освобождение места — storage/* (ARTIFACT_RETENTION, ONLINE_CLEANUP).
4. Сервисные API и дальнейшие расширения
Маршруты A/B ниже реализованы с уточнениями: создание аккаунтов и назначение делегирования требуют локального serviceAdministrator; управление существующими целями допускает отдельные admin actions в пределах ceiling. PATCH меняет только enabled; effective permissions не возвращает policy revision. GET /api/v1/service-accounts/{id}/audit требует bootstrap либо service-audit.read. Текущие контракты: ключи, делегирование и карта admin actions. В этапе C уже работают assets/page и список/карточки логических репозиториев; остальные строки C–E остаются проектом и не входят в рабочую OpenAPI.
| Этап | Метод / область | Назначение и право |
|---|---|---|
| A | GET /api/v1/capabilities | Развёрнутые функции, gateway role, лимиты протокола; любой действующий credential. Не список чужих ресурсов. |
| A | GET /api/v1/auth/permissions | Собственные effective bindings, profile, credentialId и serviceAdministration; клиентский UX, не замена серверному authorize. |
| B | GET / POST /api/v1/service-accounts | Cursor-list: bootstrap или фильтр service-account.read; создание: только bootstrap. |
| B | GET / PATCH /api/v1/service-accounts/{id} | Карточка / enabled с expectedRevision: bootstrap либо service-account.read/manage. Policy меняется отдельным CAS-контрактом. |
| B | GET / PUT /api/v1/service-accounts/{id}/policy | Bootstrap либо policy.read/manage; expectedRevision, точные bindings, ceiling для изменения. |
| B | GET / POST /api/v1/service-accounts/{id}/keys | Metadata list / выдача pending key: bootstrap либо credential.read/manage. Secret только в первом ответе POST. |
| B | GET /api/v1/api-keys/{id} | Метаданные без hash/secret: bootstrap либо credential.read. |
| B | POST /api/v1/api-keys/{id}/rotate | Новый pending key, Idempotency-Key, прежние ограничения или сужение: bootstrap либо credential.manage + ceiling. |
| B | POST /api/v1/api-keys/{id}/revoke | Необратимый отзыв: bootstrap либо credential.manage + ceiling; повтор — 204. |
| B | POST /api/v1/auth/activate-key | Доказательство владения pending credential; 204, без выдачи дополнительных прав. |
| B | GET /api/v1/api-keys/{id}/delegations | Bootstrap либо владелец самого key; до 64 записей, включая tombstones. |
| B | PUT / DELETE /api/v1/api-keys/{id}/delegations/{accountId} | Только bootstrap, CAS expectedRevision; 200 grant/tombstone. PUT создания требует revision 0. Контракт. |
| C | GET /api/v1/repositories и /{repository} | Реализовано: собственные логические scopes, after/limit, карточка id/formats/permissions; managed repository.read, legacy собственные непустые grants. Настройки/создание не включены. |
| C | Пагинация каталогов (assets/page и accounts/keys уже работают) | Bounded pages, согласованный cursor, фильтрация ACL до LIMIT; старый ответ assets не менять молча. |
| C | POST /api/v1/transfer-tickets | Узкий краткоживущий допуск к одному immutable объекту; право выдачи отдельно от content access. Контракт после проверки gateway audience/revoke. |
| D | GET R/changes (реализовано, ADR 0058); webhooks (реализовано файлом оператора, ADR 0069); GET / POST R/webhooks — не реализовано | Cursor replay по ленте изменений; доставка webhook с подписью, повтором и защитой от SSRF. API подписок и event.read / webhook.manage остаются планом. |
| D | Каталог schemas и типизированных metadata | Версионированные схемы, ограниченная сложность; собственная policy, без исполнения пользовательского кода. |
| E | Управление retention / операции удаления | Реализованы explicit permission, preview, fixed-selection apply и защита ссылок/истории; scheduler и отдельный online GC реализованы: ONLINE_CLEANUP. Контракт. |
| E | Статус replicas / gateways / очередей | system.observe; управление квотами — отдельный операторский контракт, не параметр произвольного клиента. |
Реализованы raw-файлы по пути в /api/v1 (ADR 0064) и протоколы рядом с ним: реестр образов OCI (ADR 0063), Git LFS с блокировками (ADR 0065) и npm-реестр для Unity Package Manager (ADR 0066). OAuth/OIDC federation, S3/NuGet adapters — потенциальные отдельные интеграции, не обещание совместимости. Они добавляются только при конкретном сценарии и используют те же authorizer/storage use cases.
5. Рецепты для клиентов
| Клиент | Рабочий маршрут сегодня | Целевая минимизация |
|---|---|---|
| CI/CD | create upload → parts → complete или async job → register package / set asset | Publisher в одном repo, короткий срок ключа, отдельные identities dev/prod |
| Агент развёртывания | packages/content или asset/content (либо resolve → artifact content) + Range/ETag | Downloader либо только content по закреплённому объекту; approved/channel policy отдельно |
| Индексатор / каталог | packages/search/annotations | CatalogReader без content.read |
| Редактор metadata | annotations GET/PUT с expectedRevision | MetadataEditor без публикации и удаления |
| Внешняя браузерная консоль | login/me + native SDK, CORS exact origin | Пользовательские сессии; machine secret не встроен в frontend |
| Мониторинг | authenticated health/ready | Observer, минимум информации для health, подробности отдельно |
| Интеграция по событиям | Лента изменений GET R/changes (polling) и подписанный webhook из файла оператора | API управления подписками; события identity отдельно |
6. Как поддерживать карту
При добавлении операции обновлять use case, runtime-схему, OpenAPI, SDK, право/ресурс в карте, негативные ACL-тесты и описание retry. HTTP method не определяет permission: GET jobs сегодня требует write, PUT annotations — read+write. Нельзя заменить эти проверки одним middleware «GET = read».
Экспортируемая OpenAPI описывает все операции, включая HEAD, liveness и сам endpoint спецификации. Все имеют operationId, описание авторизации, retry и роли шлюза. Сервер при onReady и CI сверяют фактические маршруты со схемой; исключения ограничены точными static GET/HEAD. При сборке создаётся packages/contracts/dist/openapi.json. Правила и границы проверок: API_CONTRACT_GUARD, ADR 0018.
Discovery: GET/HEAD /api/v1/repositories возвращает items/next (до 100), GET/HEAD /api/v1/repositories/{repository} — собственную карточку или 404. Это projection прав, не глобальный реестр storage и не право читать bytes. Точные правила и SDK.
Ревизионные вложения сборки: GET/HEAD/PUT /api/v1/repositories/{repository}/artifacts/{id}/attachments и GET/HEAD /attachments/history. Метаданные и произвольные метки используют существующий annotations API. Права, SDK и сценарии.
Логическое удаление и retention preview/apply реализованы в ARTIFACT_RETENTION: managed-only artifact.delete, CAS аннотаций, пины истории и receipts. Миграции 13/14. Реестр/настройки/квоты репозиториев, SDK distribution, identity delegation/SSO и глобальное управление очередями остаются отдельными этапами.
Настройки хранения: GET/PUT repositories/{repository}/storage/policy, GET storage/usage, GET storage/preview, POST storage/run, GET storage/events. Surface catalog, права storage.read/manage, artifact.delete и diagnostics.read по операции. Контракт.
Поиск GET search: q — literal substring имени или значения актуальной metadata без учёта регистра; metadataKey + metadataValue — точная пара с учётом регистра, задаются вместе. Значение может быть пустой строкой. Фильтры объединяются через AND, доступ и cursor/100 сохраняются. SQL параметризован; regex/wildcards не поддерживаются. См. CLI. Элемент результата: id, name, size (десятичная строка), createdAt, publishedAt (null, если время публикации не записано), labels (текущие: из annotation, иначе из descriptor загрузки) и stages (текущие стадии продвижения в байтовом порядке, до 16). Страница и стадии читаются одним ограниченным запросом без N+1. Metadata и collections в ответ не входят: для них нужен annotation.read.