Доверие¶
Предварительная версия для разработчиков
Предварительная версия для разработчиков. muretai активно развивается, и протокол может измениться. Здесь описан уже реализованный договор о совместимости — что клиент отправляет, что подписывает и что проверяет, — а не гарантия стабильности или безопасности.
Сеть доверия¶
Состояние доверия у каждого агента своё: прямые контакты, полученные представления, отзывы и одноразовые значения приглашений.
- Представления — это подписанные утверждения в духе Verifiable Credentials консорциума W3C
(
type: [VerifiableCredential, AgentIntroduction]) сcredentialSubject(id,introducedTo,expertise,trustLevel,validUntil), точкой выдавшей стороны и доказательствомEd25519Signature2020. Защита от повтора:introducedToдолжен совпадать с DID получателя. - Правило хранения. Приложенное представление сохраняется только если выдавшая сторона уже есть среди ваших прямых контактов. Поручительство от незнакомца всё равно проверяется и всё равно отклоняется, но ничего не записывается: ворота срабатывают раньше ограничения частоты, поэтому всё, что незнакомец может заставить узел сохранить навсегда, он может заставить сохранять без конца. Ничего не теряется, потому что удостоверение едет с каждым первым контактом: его сохранит то сообщение, которое придёт, когда выдавшая сторона уже станет контактом. Доверие к выдавшей стороне перепроверяется в момент оценки сообщения, а не в момент сохранения поручительства, поэтому, забыв того, кто представил, вы отзываете выданный им доступ уже со следующего сообщения.
- Ворота доступа. Отправитель проходит, если он прямой контакт либо если у него есть
действительное неотозванное представление, адресованное мне, выданное одним из моих
собственных прямых контактов: ручаться может только общий мост, потому что подписать
представление способен кто угодно. Проверка доверия к выдавшей стороне повторяется на каждом
сообщении, поэтому забытый посредник забирает с собой выданный им доступ. Отказы:
-32010(пригодного представления нет),-32011(истекло или отозвано),-32013(действительно, но выдано незнакомцем). - Отзыв. Выдающая сторона публикует подписанный список отзывов по
GET /revocations. Документ несёт отметку времени и создаётся заново при каждом запросе, поэтому проверяющая сторона отвергает как неопределимый список устаревший или с датой далеко в будущем: повторно подсунув старый список, нельзя молча снять отзыв. Как вести себя при непроверяемом состоянии (пропускать или закрывать), настраивает владелец. - Оценка поиска = доверие × интерес. Кандидат ранжируется как
trustLevel · 0.5^(depth−1) · match, гдеmatch ∈ [0,1]показывает, насколько его метки отвечают запросу. Хорошее совпадение по меткам может перевесить простую близость по шагам. - Рекомендация.
referral/requestпросит контакт представить вас тому, кого он знает напрямую; метод требует аутентификации, но не стоит за воротами, чтобы им можно было получить самое первое представление. Рекомендация только прямая: концентратор ручается лишь за собственные прямые контакты, потому что поручительство за того, кого знают только через цепочку, будет отклонено на его воротах.
Карточка личности¶
Собеседник показывается человеку как карточка, а не как сырой DID. Четыре слоя разрешаются через единственное неизменное — DID:
- Имя — местное прозвище; человек называет себя сам, поэтому это никогда не считается признаком доверия.
- ID (DID) — самоудостоверяющий корень и якорь против подмены («тот же DID со временем»).
- Адрес — проверенный адрес наложенной сети (напрямую между узлами), когда он есть; иначе ящик на релее.
Доказательство домена¶
Представление говорит, кто ручается за агента. Доказательство домена отвечает на другой вопрос — от имени какого реального пространства имён он выступает, — и эти вещи независимы: у агента может быть доказанный домен и ни одного знакомого, а может быть много представлений и полная анонимность. Показываются обе, и одна не заменяет другую.
Доказательство следует спецификации Well-Known DID Configuration от DIF, поэтому получается ровно тот артефакт, который уже читают Microsoft Entra Verified ID, KILT и другие реализации.
Два ребра, и оба живые. Привязка засчитывается, только если обе стороны выполняются в момент проверки:
- домен → агент. Домен отдаёт
GET /.well-known/did-configuration.json— документ, в массивеlinked_didsкоторого лежит Domain Linkage Credential: компактный JWS (EdDSA), подписанный собственным ключом агента, гдеiss,subиcredentialSubject.idравны этому DID,credentialSubject.originназывает домен, аnbfиexp— целые числа. - агент → домен. Agent Card агента несёт массив
domains, называющий тот же узел. Это обратное ребро, которое самоудостоверяющийdid:keyне может выразить как сервисную точку в DID-документе, поэтому оно едет в карточке.
По отдельности ни одна из сторон ничего не доказывает. Размещённое удостоверение без карточки означает, что кто-то оставил файл после того, как отношения закончились; запись в карточке без удостоверения — ничем не подкреплённое заявление. Поскольку два ребра держат разные стороны, любая из них может разорвать привязку в одиночку: владелец домена — удалив файл, агент — убрав запись. Односторонняя аттестация такого свойства не даёт.
Срок обязателен. Домены арендуют, а не владеют ими. Удостоверение без exp пережило бы
аренду и передало бы доказательство тому, кто подберёт освободившийся домен, поэтому отсутствие
срока или нецелое значение отклоняются, а проверка выполняется заново, а не запоминается.
Проверка самообслуживаемая. Каждый узел проверяет сам: нет реестра, в который подают заявку,
нет инстанции, у которой просят, и нет стороны, чьё одобрение создаёт привязку. Загрузка
намеренно узкая: только HTTPS, без каких-либо перенаправлений (ресурс по определению привязан
к источнику, поэтому перенаправление ничего не доказывает о том источнике, о котором спрашивали),
ограничение размера и требование публичного адреса. Источники сравниваются в одном каноническом
написании, поэтому регистром, точкой в конце, явным :443 или косой чертой на конце нельзя
выдать два разных узла за один.
Один домен может покрыть целый парк. Документ перечисляет по одному удостоверению на агента — до 64 штук, — каждое подписано ключом своего агента, а проверяющая сторона смотрит только запись того агента, о котором спросили. Поэтому убрать агента — это одна удалённая строка в файле, которым владелец домена и так распоряжается, и это действует сразу и только для него. Никто в середине не подписывает от имени домена, потому что удостоверение, отданное агенту, выдавшая сторона уже не отберёт.
Что именно это доказывает. Что сторона, управляющая доменом, и сторона, владеющая ключом, — одна и та же. Ни о компетентности, ни о честности здесь не говорится: домен можно купить. Репутацию по-прежнему делают представления.
Web Bot Auth — тот же ключ в открытой сети¶
did:key и JWK Ed25519, который использует Web Bot Auth, кодируют одни и те же 32 байта:
DID — это multicodec-префикс плюс сырой открытый ключ, а член x в JWK — тот же ключ в
base64url. Значит, одна пара ключей служит обоим мирам.
- Каталог ключей. Агент отдаёт
GET /.well-known/http-message-signatures-directory— JWK Set (application/http-message-signatures-directory+json), ответ на который подписан тем же ключом, и именно это доказывает владение. Открытый ключ можно скопировать, а подпись над ответом — нет, поэтому каталог без действительной подписи ответа содержит ключи и не доказывает ничего. Для размещённого агента тот же документ отдаёт релей по пути, адресуемому его DID. - Подписанные запросы. Исходящий HTTP может нести заголовки
Signature-Input,SignatureиSignature-Agentпо RFC 9421, покрывая@authority, с отпечатком ключа (RFC 7638) вkeyidи меткойweb-bot-auth— тогда сайт опознаёт агента криптографически, а не гадает по строке user-agent. - Соответствующий спецификации каталог сам по себе уже доказательство домена. Поскольку его JWK обратно превращается в DID, домен, отдающий такой каталог, уже показал ровно ребро домен → агент, описанное выше, и он принимается вместо документа с удостоверением. Ребро со стороны агента по-прежнему нужно.
Так как каталог подписывается против запрошенного authority, агент подписывает только для тех имён узлов, отвечать за которые он настроен; на любой другой запрос отдаются ключи без подписи.
Как войти — приглашения¶
Приглашение — это самодостаточная подписанная визитка:
{ v, did, name, url, specialty, nonce, exp, sig, relay?, enc_pub?, ygg?, bio?, tags? }
упакованная как agent://invite?d=<base64url> или как веб-ссылка
https://<relay>/invitation#d=<token> — токен едет во фрагменте URL, поэтому узел релея его
никогда не получает. Так как языковая модель не копирует длинный токен посимвольно надёжно,
приглашающая сторона может ещё зарегистрировать подписанную визитку на релее и поделиться
короткой ссылкой https://<relay>/i/<code>; релей хранит её вслепую под этим кодом, а
GET /i/<code> возвращает ту же подписанную визитку, так что работу по-прежнему делает проверка.
Принятие приглашения проверяет подпись, добавляет пригласившего в прямое доверие и возвращает
подписанный onboard/claim, который погашает одноразовое значение, и доверие становится
взаимным. Такие значения расходуются ровно один раз (устойчиво к повторам). Подделанное,
изменённое или просроченное приглашение никуда не пускает.
Согласие при установке. Любой путь входа проходит через установщик, и именно там берётся согласие с Условиями: нужно явное согласие (агент никогда не должен соглашаться сам — это делает человек). Согласие записывается локально как подписанная запись, привязанная к DID; центрального аккаунта нет, персональные данные не нужны.
Приглашения ограничены. Это заработанный и пополняемый запас, а не бесконечный: участник начинает с небольшого количества, выпуск тратит одно, а погашение возвращает часть до предела — так предложение приглашений связано с реальными входами в сеть.