Реестр did:ru: контракт сканирования и выпуска документов
Версия контракта 0.2.0, состав полей — 2026-10-19-draft (черновик для согласования). Машиночитаемая спецификация — (OpenAPI 3.1). Состав полей документов реестр отдаёт сам: GET /api/v1/schemas — это источник истины, таблицы ниже — снимок.
1. Кто и что
Участник | Роль | Что делает в реестре |
Орган по сертификации |
| выдаёт и отзывает сертификаты соответствия серий |
Изготовитель |
| регистрирует приборы, выпускает паспорта, передаёт приборы |
Покупатель |
| принимает приборы своей подписью, проверяет паспорта по метке |
Объект | Идентификатор | Кто создаёт |
Организация |
| сама организация |
Прибор (экземпляр) |
| организация-владелец |
Сертификат соответствия серии |
| орган по сертификации |
Паспорт прибора |
| владелец прибора |
Карточка организации |
| сама организация |
Идентификатор did:ru:… выводится из ключа (z + base58btc от SHA-256 отпечатка JWK по RFC 7638), поэтому занять или подделать чужой нельзя. Сертификат соответствия — это не сертификат ключа электронной подписи.
2. Метка
QR-код метки содержит ссылку:
<адрес реестра>/s/<DID прибора> пример: http://localhost:8081/s/did:ru:device:z9vYb8rKq3f2SxTn5Lm4Hc7Wd1Ae6Pj2Ug8Rk3Nx5Ty4Q
Камера телефона открывает по ней страницу проверки — приложение не нужно.
Сканер партнёра передаёт прочитанное как есть в
GET /api/v1/scan?code=…или сам берёт DID из пути после/s/.Принимается и голый DID в QR (
did:ru:device:…).Что печатать:
GET /api/v1/labels/{did}(ссылка) иGET /api/v1/labels/{did}/qr.svg/qr.png(картинка, коррекция M).
Адрес реестра в метке задаётся при развёртывании и после печати меток не меняется. Нужно согласовать до печати.
3. Сканирование — один вызов
GET /api/v1/scan?code=http%3A%2F%2Flocalhost%3A8081%2Fs%2Fdid%3Aru%3Adevice%3Az9vY…
Публичный: без токена. Ответ (сокращён):
{ "result": { "did": "did:ru:device:z9vY…", "kind": "device", "verdict": "caveat", "caveats": ["certificate_revoked"], "owner": { "did": "did:ru:org:zEhB…", "name": "ОЙЛТИМ", "nameVerified": false }, "passport": { "id": "urn:uuid:e9ba…", "type": "InstrumentPassportCredential", "status": "active", "signed": true, "issuer": { "did": "did:ru:org:z84u…", "name": "Взлёт", "nameVerified": false }, "validFrom": "2026-10-19T15:42:32Z", "claims": { "serialId": "SN-0001", "nomenclature": "ЭМИС-ВИХРЬ 200", "manufacturedOn": "2026-10-01", "certificate": "urn:uuid:1699…" }, "verification": { "result": "registry_valid", "cryptographicallyVerified": true, "checks": { "proof": "valid", "status": "active", "time": "valid", "certificate": "revoked", "…": "…" } } }, "certificate": { "id": "urn:uuid:1699…", "status": "revoked", "claims": { "number": "RR-0042", "scope": "расходомеры серии 200" }, "…": "…" }, "labelUrl": "http://localhost:8081/s/did:ru:device:z9vY…", "checkedAt": "2026-10-19T15:45:36Z", "schemaVersion": "2026-10-19-draft" } }
Что показывать первым — verdict:
verdict | Значит |
| паспорт подлинный и действует, сертификат соответствия серии действует |
| паспорт подлинный, но сертификат соответствия отозван, истёк или не найден |
| паспорту доверять нельзя (подпись не сходится, паспорт отозван или вне срока, издатель не найден) |
| прибор зарегистрирован, паспорт не выпущен |
| идентификатор прибора деактивирован (списан) |
Почему не valid — caveats: passport_signature_invalid, passport_unsigned, passport_revoked, passport_expired, passport_issuer_unresolved, certificate_revoked, certificate_expired, certificate_missing. Отдельно, без влияния на verdict: transfer_in_progress — передача подписана, получатель ещё не подтвердил (кому — transferTo).
Подлинность паспорта и статус сертификата соответствия — разные проверки: после отзыва сертификата паспорт остаётся подлинным, verdict = caveat.
Названия организаций (name) указаны самими организациями (nameVerified: false) — подтверждение по сертификату ключа УКЭП ЮЛ (ИНН, ОГРН) будет следующим этапом.
4. Как класть: порядок шагов
Все изменения подписываются ключом стороны. Сейчас ключи — Ed25519 (alg: EdDSA), формат — компактный JWS.
Шаг | Кто подписывает | Вызов |
1. Регистрация организации | сама организация |
|
2. Карточка организации | сама организация |
|
3. Сертификат соответствия | орган по сертификации |
|
4. Регистрация прибора | изготовитель |
|
5. Паспорт | владелец прибора |
|
6. Передача, шаг 1 | владелец |
|
7. Передача, шаг 2 | получатель |
|
8. Отзыв | издатель документа |
|
Подпись операции над DID (DID-Operation-Proof)
header = {"alg":"EdDSA","kid":"<DID подписывающего>#key-1","typ":"did-operation+jws"} payload = {"op":"create|update","did":"<DID документа>","prevVersionId":"<versionId — только для update>", "bodyDigest":"<base64url(SHA-256(тело запроса как отправлено))>","iat":<unix-время>} DID-Operation-Proof: base64url(header) . base64url(payload) . base64url(подпись)
prevVersionId берётся из didDocumentMetadata.versionId ответа GET /did-api/api/v1/dids/resolve. Изменение поверх устаревшей версии отклоняется (409, version_conflict). Подписать может сам DID, его контроллер или владелец. Стать владельцем можно только своей подписью.
Подпись документа (application/vc+jwt)
Тело запроса — JWS: {"alg":"EdDSA","kid":"<DID издателя>#key-1","typ":"vc+jwt"}. Полезная нагрузка — сам документ (W3C VC 2.0):
{ "@context": ["https://www.w3.org/ns/credentials/v2"], "id": "urn:uuid:3f0e…", "type": ["VerifiableCredential", "InstrumentPassportCredential"], "issuer": "did:ru:org:z84u…", "validFrom": "2026-10-19T15:42:32Z", "credentialSubject": { "id": "did:ru:device:z9vY…", "serialId": "SN-0001", "nomenclature": "ЭМИС-ВИХРЬ 200", "manufacturedOn": "2026-10-01", "certificate": "urn:uuid:1699…" } }
Отзыв: Credential-Operation-Proof — JWS typ: vc-operation+jws с полезной нагрузкой {"op":"revoke","credentialId":"<id>","iat":<unix-время>}.
5. Поля документов (черновик, ждут согласования)
Источник истины — GET /api/v1/schemas и JSON Schema каждого типа: GET /api/v1/schemas/{type}. Неописанные поля отклоняются (unknown_field), обязательные — проверяются (missing_field), типы и форматы — тоже (invalid_field). В ответе об ошибке — field.
InstrumentPassportCredential — паспорт прибора, субъект did:ru:device:…
Поле | Название | Тип | Обязательно |
| Заводской номер | строка ≤ 64 | да |
| Модель | строка ≤ 256 | да |
| Дата изготовления | дата | |
| Сертификат соответствия серии | id | да |
| Заменяет паспорт | id действующего | если он есть |
ConformityCertificateCredential — сертификат соответствия, субъект did:ru:org:… (держатель)
Поле | Название | Тип | Обязательно |
| Номер сертификата | строка ≤ 64 | да |
| Продукция (область сертификации) | строка ≤ 512 | да |
| Нормативные документы | список строк ≤ 20 | |
| Дата выдачи | дата |
Срок действия — validUntil документа.
OrganizationCredential — карточка организации, субъект did:ru:org:…, выпускает сама организация
Поле | Название | Тип | Обязательно |
| Название | строка ≤ 256 | да |
| ИНН | 10 или 12 цифр | |
| ОГРН | 13 или 15 цифр | |
| Роль |
|
Документы стенда 5 (поля — GET /api/v1/schemas): ProductTypeCredential — тип прибора и набор его характеристик (обязательные проверяются у паспорта); IncomeInspectionCredential — акт входного контроля (выпускает получатель); BlockPassportCredential — паспорт блока с составом; CertificateCardCredential — карточка сертификата от агента.
Правила сверх полей:
паспорт выпускает только владелец или контроллер прибора (
not_subject_authority);паспорт ссылается только на действующий сертификат соответствия (
certificate_not_in_force,unknown_reference,wrong_reference);у прибора один действующий паспорт: новый указывает в
replacesдействующий, и тот отзывается при выпуске; безreplaces—passport_exists,replacesне на действующий паспорт прибора —wrong_reference;карточку организации выпускает только сама организация.
6. Доступ
Что | Как |
Скан, метки, схемы, чтение и проверка документов, получение DID и его истории | публично |
Выпуск, отзыв, изменение DID, поиск, ленты событий | токен партнёра |
Токен отвечает на вопрос «какая система вызывает», подпись — «кто из участников это решил». Токен без подписи ничего не меняет. Токен партнёра — временная мера: целевой вход — через сервис аутентификации платформы.
Упрощение для стенда 5 — токен организации. Если организация заведена на платформе с ключом у неё (CUSTODY_FILE), её система вызывает /api/v1/custody/… токеном этой организации, а подписывает платформа её ключом: POST /custody/credentials {"type":[…],"credentialSubject":{…}}, POST /custody/devices, POST /custody/dids/{did}/transfer|accept|decline, PUT /custody/dids/{did}/services. Правила реестра те же, что для подписанных запросов. Это учебное упрощение, не юридическая равнозначность УКЭП. Сценарий целиком — docs/stand5/README.md и scripts/stand5-flow.mjs.
7. Ошибки
{ "error": "invalid credential: credentialSubject.serialId (Заводской номер) is required for InstrumentPassportCredential", "code": "missing_field", "field": "serialId" }
Разбирать по code и field, текст error — для разработчика.
HTTP | code |
400 |
|
401 |
|
403 |
|
404 |
|
409 |
|
410 |
|
422 |
|
503 |
|
8. Что ещё изменится
Что | Как повлияет на интеграцию |
Состав полей паспорта и сертификата | меняется |
Подписи ГОСТ Р 34.10-2012 и сертификаты ключей X.509 (УКЭП ЮЛ) | добавится алгоритм подписи и ключ с |
Вход через сервис аутентификации платформы | токен партнёра заменится токеном платформы |
Запись истории DID в распределённый реестр | в истории появится идентификатор транзакции ( |
Адрес реестра в метке | фиксируется до печати меток |
Несовместимые изменения повышают мажорную версию контракта; добавление необязательных полей — нет.
9. Стенд
Адрес | Что |
| портал станций: изготовитель (типы, приборы, паспорта, сообщения), сертифицирующая организация (сертификаты, агент), покупатель (входной контроль, приёмка, блоки) |
| печатный цифровой паспорт прибора или блока: характеристики, идентификация, сертификат, вложения, формуляр |
| страница проверки: камера, фото метки или ссылка |
| полный API VC-сервиса |
| состав полей |
Журнал изменений
Дата | Перечень изменений | Автор |
2026-11-05 | Перенесён из архива DID-VC-итоги-2026-11-03 без изменения содержания. | Дис |