Про Solid Project написано множество статей с громкими заголовками и прекрасными картинами будущего, где связанные данные свободны, а пользователи не боятся бана в твиттере. В этих статьях подробно рассказывается, какие изменения несет Solid, но весьма поверхностно затрагивается вопрос, что же это такое, из чего он состоит, какие в нем есть технические проблемы и подводные камни.
Мне понравились принципы, заложенные в Solid, и я захотел написать веб-приложение для этой платформы — многоуровневый список задач. В процессе разработки я ознакомился со многими библиотеками и реализациями серверной части, прочитал некоторые спецификации. В данной статье я хочу изложить полученный опыт.
Вступление (история из жизни)
Когда я начал разрабатывать многоуровневый список задач и стал проектировать серверную часть, меня охватила скука: опять писать CRUD, примитивную БД, настройки доступа, выбирать хостинг. И не забыть про соблюдение всех законов про хранение персональных данных и экстремистской литературы. Хотелось чего-то простого и готового. Тут я вспомнил про Markdown редактор StackEdit: он хранит пользовательские данные на Google Диске - то что нужно!
Но тут меня ждало разочарование: клиентские библиотеки гугла для JS оказались очень неудобными в использовании и сильно отличались от документации. Спустя много дней разработки методом проб и ошибок я уперся в кирпичную стену ограниченности гугловской системы прав доступа: с октября 2020 года больше нельзя хранить файл в нескольких папках, эта фича была заменена на шорткаты. Нужно было искать что-то другое.
Solid - это децентрализованное облачное хранилище данных с аутентификацией и настройкой прав доступа. В отличии от Google Drive и подобных сервисов, в Solid отсутствует единый центр аутентификации и хранения данных - каждый пользователь волен выбирать свой сервер. Существующие стандарты аутентификации не поддерживают такой вариант работы, поэтому понадобился новый, и не один.
Спецификации
Итак, Solid - это набор связанных между собой спецификаций, некоторые являются стандартом уже много лет, некоторые только Proposal. Последнюю версию можно почитать в репозитории. Разработка спецификаций ведется в панелях: отдельно разрабатывается аутентификация, авторизация, хранение данных и другие спецификации. Я рассмотрю лишь некоторые из них.
Наверное самая сложная для понимания часть SOLID: распределенная направленная семантическая графовая база данных. RDF-документ состоит из набора триплетов, каждый из них обозначает некоторое утверждение: например "цвет неба синий". Порядок слов важен, первым должен идти объект, затем свойство (предикат) и после этого значение этого свойства. То есть правильнее было бы написать <небо> <имеет цвет> <синий>. Некоторые утверждения можно записать разными способами.
Для унификации публикуются онтологии - описания предметных областей со специфическими предикатами и типами данных. Например, в онтологии vcard можно найти предикаты, относящиеся к профилю пользователя: имя, адрес, телефон, почта и типы данных человек, группа, организация и другие. Это позволяет стандартизировать формат хранения данных и не проектировать профиль пользователя каждый раз заново, а использовать готовую онтологию.
Звучит сложно и непонятно, но, как и с теорией реляционных баз данных, на практике все оказывается намного проще: RDF очень похож на EAV модель и легко преобразуется в объектную модель.
Некоторые интересные особенности:
Предикаты должны быть ссылками, но могут быть нерабочими (вроде как)
Есть хитрый Blank Node (subject и value в одном флаконе) - он используется когда значение неизвестно, но про него имеется что сказать. Например: дворцом владеет некто, кого нельзя называть, но нужно подчеркнуть, что это частное лицо.
<дворец> <находится во владении> <…>
<…> <является> <частным лицом>Значением (value) может быть коллекция
Значением (value) может быть URI другого subject, в том числе в другом документе.
Хранилище в SOLID называется POD, и у него всегда есть владелец. Человек, организация или бот - неважно, какой-то агент. Владелец имеет доступ ко всем ресурсам в POD и может давать доступ другим агентам.
В POD можно создавать контейнеры - аналоги папок. Они являются RDF ресурсами со списком файлов внутри: <контейнер> <ldp:contains> <файл>.
Управление ресурсами осуществляется посредством HTTP запросов Get, Post, Put, Patch, Delete. В этом плане POD похож на WebDav сервер. Разработан язык запросов SPARQL для получения и обновления RDF данных.
Также предусмотрено получение уведомлений об изменениях на сервере посредством WebSocket. Например, если подписаться на изменения внутри определенного контейнера на сервере, то при создании/удалении/обновлении ресурса внутри этого контейнера придет сообщение. К сожалению, в сообщении не будет указан URI измененного ресурса, надеюсь это поправят.
Предусмотрено создание Type Index - описания используемых кастомных типов данных. Это поможет использовать эти данные другим приложениям, искусственному интеллекту... ну или хотя бы в качестве документации пригодятся.
Идентификация
У каждого агента существует профиль - RDF-документ с некоторыми обязательными полями. Ссылка на этот документ (точнее на элемент в этом документе) называется WebID и является идентификатором пользователя. В профиле можно указать свои личные данные, контакты, а также ссылки на другие свои профили и идентификаторы, хранилища, сервера аутентификации и другое.
Аутентификация
WebID-TLS
Открытый ключ хранится в профиле пользователя, закрытый генерируется на клиенте, им в дальнейшем подписываются запросы к другим серверам. После того, как HTML элемент Keygen был выпилен из стандарта, WebID-TLS потерял былой шарм, хотя еще указан основным в текущей спецификации.WebID-OIDC, основан на OpenId Connect (в свою очередь основан на OAuth 2.0).
В процессе аутентификации участвуют 4 стороны: сервер-эмитент (WebID Issuer), сервер аутентификации (Identity Provider), приложение (Relying Party) и сервер данных (Resource Server).
Работает в двух вариантах: с помощью Bearer токена или DPoP токена. Вариант с Bearer токеном аналогичен OpenId Connect и не защищает от подмены Identity Provider (это когда злоумышленник аутентифицируется на сером Identity Provider под чужим WebId). Насколько я понял, Bearer токены оставили только для удобства разработки, и впоследствии выпилят.Токены DPoP более надежные, но требуют загрузки дополнительных криптографических библиотек в браузер (crypto-browserify).
Авторизация
WAC - web access control
Простая схема управления правами. У каждого ресурса может быть специальный .acl файл (RDF) со списком разрешений. Разрешения можно выдавать пользователю или группе, также можно выдать разрешение любому пользователю или открыть доступ для всех. Можно выдавать разрешения на чтение, запись, добавление и контроль (изменение прав доступа).Пример acl файла, разрешающего доступ на чтение
<https://pod.inrupt.com/username/.acl#public>: rdf:type Authorization acl:accessTo 'username.solidcommunity.net' acl:agent 'myfriend.solidcommunity.net' acl:mode Read
ACP - access control policies
Более сложная схема, находится еще на этапе Proposal. У каждого ресурса должен быть специальный .acr файл (RDF) со списком политик доступа. Политики доступа могут быть описаны в отдельных файлах (тоже RDF), но обязательно на том же сервере, что и ресурс, к которому они относятся. Каждая политика доступа разрешает или запрещает режимы доступа (чтение, запись, добавление) и имеет несколько правил, в которых уже прописаны конкретные пользователи и группы. Предполагается, что в правилах можно будет описывать и более сложные условия, например возрастные ограничения.
Пример acr файла по умолчанию и политики:<https://pod.inrupt.com/username/?ext=acr> rdf:type acp:AccessControl ; acp:access <https://pod.inrupt.com/username/policies/#Owner> ; acp:apply <https://pod.inrupt.com/username/policies/#Owner> . <https://pod.inrupt.com/username/policies/#Owner> acp:allOf <https://pod.inrupt.com/username/policies/#OwnerAgents> ; acp:allow acp:Read ; acp:allow acp:Write ; rdfs:comment "Resources with this policy accessible by the owners."@en ; rdf:type acp:Policy . <https://pod.inrupt.com/username/policies/#OwnerAgents> acp:agent <https://pod.inrupt.com/username/profile/card#me> ; rdfs:comment "Owner agents are matched by this rule."@en ; rdf:type acp:Rule .
Реализации
Существует несколько реализаций серверов SOLID, которые различаются не столько стабильностью и производительностью, сколько поддерживаемыми спецификациями и их версиями. То же самое касается и клиентских библиотек.
Сервер
Node solid server (NSS)
Сервер написан на JS, рабочий и довольно стабильный вариант, есть докер контейнеры для ленивых. Из минусов - поддерживает только ACL списки контроля доступа. Можно использовать развернутый инстанс https://solidcommunity.net.Enterprise Solid Server (ESS)
Production-ready, есть схема развертывания в AWS, поддерживает ACP. Можно использовать инстанс https://broker.pod.inrupt.comCommunity server
Разработка была начата в мае 2020 года с целью создать легко расширяемый POD с возможностью замены компонентов. Сервер написан на TypeScript, код довольно удобно читать. Еще не реализована аутентификация.
Его можно использовать в качестве тестового локального сервера, только берите из ветки main, а не master. Там более свежее, да и работает лучше.
Клиент
Tripledoc
Удобная и понятная библиотека, есть все, что нужно.const doc = await fetchDocument(webId); const subject = getSubject(webId); const name = subject.getString(foaf.name); subject.setString(foaf.name, Mr. ${name}); await doc.save();
@inrupt/solid-client
Аналогичная библиотека от inrupt, чуть более многословнаяconst dataset = await getSolidDataset(webId, session); const thing = await getThing(dataset, webId); const name = getStringNoLocale(thing, foaf.name); const updatedThing = setStringNoLocale(thing, foaf.name, `Mr. ${name}`); const updatedDataset = setThing(dataset, updatedThing); await saveSolidDatasetAt(webId, updatedDataset, session);
Solidocity
Все библиотеки показались мне низкоуровневыми и я написал обертку над tripledoc, позволяющую определить объектную модель данных и работать с ней. Собираюсь добавить в нее кеш в indexedDB и мердж при апдейтах с сервера.// описание Profile // https://github.com/franzzua/solidocity/blob/master/repository/profile.ts const profile = new Profile(webId); await profile.Init(); profile.Me.FullName = `Mr. ${profile.Me.FullName}`; profile.Me.Save(); await profile.Save();
Это только те библиотеки, что меня заинтересовали. Кому интересно, есть список побольше.
Библиотеки аутентификации
С библиотеками аутентификации раньше было много проблем, периодически что-нибудь да отваливалось. Сейчас вроде бы все хорошо, но на всякий случай держу про запас по две библиотеки для каждого окружения.
solid-auth-fetcher
Форк библиотеки от inrupt, немного быстрее восстанавливает сессию из LocalStorage, без редиректа на сервер аутентификации@inrupt/solid-client-authn-node
Для NodeJShttps://github.com/solid/solid-node-client
Для NodeJS окружения
Мой выбор: ESS/NSS, solid-auth-fetcher и solidocity.
Практика
Я написал очень простой Todo, если хотите попрактиковаться, то в него можно добавить следующие фичи:
Выбор места хранения списка
Поддержку нескольких списков
Шеринг списков по WebId юзера
Шеринг списков по ссылке
Для чего сгодится Solid
Появляется все больше приложений, работающих с пользовательскими данными. В начале они невероятно удобны и завлекают пользователей скидками. Но проходит время, и разработчику нужно отбить инвестиции - новых фич все меньше, а условия обслуживания все хуже. Тем временем у конкурента новый сервис, еще более удобный и привлекательный. Нужно удержать пользователей высокой ценой переезда - закрытым форматом данных.
Solid снижает издержки на смену приложения и это меняет правила игры: теперь разработчикам не нужно тратить много денег на старте на переманивание пользователей, ведь им гораздо легче попробовать что-то новое. Также уменьшаются затраты на хостинг - их теперь частично несут пользователи. Срок окупаемости проекта сокращается, риски уменьшаются и можно пробовать более идеи прилолжений.
Примеры сервисов, где Solid должен помочь:
Системы управление проектами
Каждому свое, нет золотой пилюли на все случаи жизни, всегда будет недоставать чего-то достаточно простого и функционального одновременно.Маркетплейсы
Подвинуть eBay, AliExpress или Авито сможет не корпорация с тысячами программистов, а тот, кто предложит наиболее удобный интерфейс для продавцов и покупателей.Файловые менеджеры
Total Commander, Far, Проводник Windows - все они лучше интерфейса Google Drive или OneDrive, но не поддерживают хранение файлов в облаке.Блоги, социальные сети, чаты
Очень высокая цена переезда. Зоопарк несовместимых реализаций,
Заключение
Многое в Solid еще сырое и будет изменено, но уже сейчас можно написать приложение под эту платформу и относительно недорого его запустить.