24 дня индивеба: адвент-календарь постов про Индивеб

Домен

Миниатюрный адвент-календарь про Индивеб! Каждый день — небольшой пост про Индивеб, зачем это нужно, и как поучаствовать в этом движении.

Что такое индивеб

Индивеб — это движение за владением своим контентом в интернете.

Веб: сайты, интернет-магазины, социальные сети — повсюду. Но веб не принадлежит большим корпорациям, хотя иногда и возникает такое ощущение. Веб вообще никому не принадлежит.

Большие корпорации превращают наше внимание в деньги при помощи того, что мы, пользователи, создаём. Посты, фотографии, лайки, комментарии — всё это превращается в данные для обучения рекламы и затягивания в свои сети новых пользователей. Но нам необязательно отдавать им свой контент.

Мы пишем посты в Фейсбук и ВКонтакте — а можем писать их на своём сайте.

Мы постим фотографии в Инстаграм — а можем постить их на своём сайте.

Мы читаем друзей в Твиттере — а можем читать их сайты.

Зачем домен

Всё начинается с доменного имени. Свой домен, свой адрес — это как участок земли, на котором можно построить всё, что душе угодно.

И, как и участки земли, домены не бесплатны: домен в зоне .com стоит в районе 13 долларов в год. Взамен ты получаешь домен второго уровня, который находится под твоим контролем.

Домен в Индивебе выступает в роли главного идентификатора: не маринин_тим_в_твиттере, не пользователь с номером 14611294 в ВКонтакте, а просто marinintim.com.

Я проплатил свой домен до 2028 года — чтобы не вспоминать о том, что надо продлить, каждый раз.

Регистрация домена позволяет пересечь границу между «пользователь веба» и «автор части веба», и на этой стороне жить гораздо интереснее.

Где регистрировать

Чтобы зарегистрировать домен, понадобится регистратор. Они регулируют выдачу доменов (чтобы у домена не было вдруг двух владельцев), и, в силу исторических причин, берут за это деньги.

Мой любимый регистратор — Hover, ещё могу порекомендовать Gandi. Оба выглядят самыми честными на рынке владельцев земли, оба поддерживают много доменов верхнего уровня, и там, и там интерфейс ничего так.

Процесс регистрации прост — регистрируешь аккаунт, выбираешь домен, заполняешь формочку или две, оплачиваешь — и домен твой.

Какой TLD выбрать

TLD — top level domain, это последняя часть домена. Несколько лет назад их стало очень много, от .computer до .москва.

.com кажется самым нейтральным. С учётом того, что в идеале домен тебе пригодится не на год, а на всю жизнь, то стоит дважды подумать, прежде чем выбрать в качестве основной identity какой-нибудь nagibator777.pizza.

Также я бы не рекомендовал брать домен в зоне .ru как основной: российские регистраторы у меня не вызывают доверия, да и вдруг однажды покинешь Россию.

Что с ним делать

Домен это ещё не сайт, но это первый важный шаг. Можно даже не делать сайт, а просто редиректить на актуальный аккаунт в трендовой социальной сети — и таким образом получить чуть больше контроля над тем, как ты показываешь себя онлайн другим людям.

День 2: страница

Страница

Чтобы в интернете появилась твоя страница, нам понадобится три вещи:

  1. Адрес в интернете, который указывает на…
  2. …Сервер, который отвечает на запросы, отдавая…
  3. …HTML страницы.

Разработчики часто на этом моменте бросаются расчехлять тяжелую артиллерию навроде React и Gatsby, но всё это не нужно, чтобы начать. И особенно нет необходимости быть разработчиком.

Про адрес в интернете, домен, я писал вчера. Сегодня расскажу про две оставшиеся части. В результате ты получишь свою страницу в интернете, которую можно кому-нибудь показать.

Для разработчиков: можно пропустить HTML и перейти сразу к части про Микроформаты.

HTML

Все страницы в интернете написаны на HTML — это такой язык разметки документов.

На больших сайтах HTML создаётся программами, но HTML можно писать руками.

Для этого понадобится редактор, который показывает текст файлов таким, как он есть (не Ворд). Редактор может быть любым — я обычно использую VS Code, но этот пост написал в BB Edit.

HTML-документ состоит из тегов, которые указывают, что в них написано. Например:

<p>
  Все счастливые семьи похожи друг на друга, каждая
  несчастливая семья <em>несчастлива по-своему.</em>
</p>

обозначает абзац (p от paragraph) с указанным текстом и выделением фразы «несчастлива по-своему».

Анатомия тега примерно такая: <имя-тега возможный="атрибут">содержимое</имя-тега>. </имя-тега> закрывает тег (у некоторых тегов не бывает содержимого, поэтому и закрывать их не надо).

Браузер это нарисует так:

Все счастливые семьи похожи друг на друга, каждая несчастливая семья несчастлива по-своему.

Теги бывают самыми разными, но сегодня понадобится всего пять: <a> (ссылка), <img> (картинка), <h1> (заголовок), <p>. С их помощью можно создать «визитную карточку»:

Тим Маринин

Пишу про Индивеб на , кормлю кота.

У меня есть Гитхаб и Твиттер.

Тим

Внутри каждого браузера есть возможность посмотреть на HTML страницы в «сыром», неотрисованном виде. Для этого нужно кликнуть правой кнопкой и найти что-то похожее на «View Source». Смотреть на код чужих сайтов и извлекать из него уроки совершенно незазорно — так начинали многие фронтенд-разработчики. Просто копировать сайты целиком, впрочем, всё же не стоит: лучше привнести немного себя в дизайн сайта.

Открой редактор, напиши в нём <p>Привет!</p>, сохрани как файл с именем index.html, и попробуй открыть этот файл в браузере. Если видно Привет!, то половина дела сделана. Теперь можно писать свою страницу, а дальше обсудим, как её разместить в интернете.

Что написать на своей странице? Выбор за тобой. Можно поставить ссылки на свои профили в соцсетях (для этого пригодится тег A), можно написать топ-3 лучших фильмов во вселенной Marvel (для справки: Железный Человек, Тор: Рагнарёк, Чёрная Пантера).

Микроформаты

Но прежде чем зануды начнут ругаться, стоит добавить немного дополнительной разметки. Она необязательна, но с ней лучше:

<!doctype html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <title>Название моего сайта</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  Тут будет разметка сайта.
</body>
</html>

Доктайп указывает, что в документе используется современный HTML (были другие версии, о них мы не вспоминаем).

Внутри тега <html> находится весь документ. lang="ru" обозначает, что текст документа — русский, это помогает читалкам текста выбрать правильный голос.

В <head> — всякая дополнительная информация: в какой кодировке документ, какой заголовок страницы, подготовлена ли страница для айфона.

А внутри <body> находится содержимое нашей страницы, которое будет нарисовано внутри браузера.

В HTML нет специального тега для визитной карточки. Но у каждого тега может быть указан «класс», и на этом строятся микроформаты — способ описать смысл разметки в формате, который понятен компьютерам.

Один из микроформатов, h-card, как раз обозначает визитную карточку: имя, адрес в сети, контакты.

Скажем, у нас есть такой HTML:

<p>
  <a href="https://marinintim.com">Тим Маринин</a>
  (<a href="mailto:mt@marinintim.com">mt@marinintim.com</a>).
</p>

Человеку понятно, когда он смотрит на результат, что речь идёт про меня: указана ссылка на мой сайт, моё имя, электронная почта.

Тим Маринин (mt@marinintim.com).

А вот так будет выглядеть разметка, если мы добавим нужные классы:

<p class="h-card">
  <a class="p-name u-url" href="https://marinintim.com">Тим Маринин</a>
  (<a class="u-email" href="mailto:mt@marinintim.com">mt@marinintim.com</a>).
</p>

Такую разметку можно автоматически анализировать и извлекать из неё информацию.

Про дополнительные свойства, которые можно добавить, можно прочитать на вики микроформатов.

Главное преимущество микроформатов — мы размечаем содержимое, которое уже есть на странице, и именно ту версию, которую видят читатели. Есть альтернативные способы добавить смысла на страницу, но в них обычно приходится дублировать: одна версия для людей, другая — для машин.

Микроформаты — фундамент индивеба. Благодаря им можно создавать инструменты, которые читают сайты, без необходимости угадывать, что на странице пост, а что нет.

Сервер

Сервер — это компьютер, задача которого — отдавать сайт всем посетителям. Поэтому он должен быть постоянно включённым и подключенным к Сети. Если такой компьютер есть, то можно использовать его (там есть нюансы с тем, чтобы разрешить доступ из интернета на компьютер у определенных провайдеров, но обычно это возможно), а если такого компьютера нет, то его можно арендовать. Обычно я использую DigitalOcean, самый дешёвый сервер у них стоит 5 долларов в месяц. В мире очень много компаний, которые арендуют сервера, вот ещё два навскидку: Linode и VScale.io (Селектел, Россия).

Блокировки и Digital Ocean

С блокировками РКН часть адресов DigitalOcean попала в список «запрещенных сайтов» — это означает, что если не повезёт с айпи-адресом, то будет не зайти из России. Если что, то можно создать сервер, проверить его адрес на IsItBlockedInRussia.com (спасибо Денису за сервис!), и если не повезло, то удалить и начать заново. Я считаю эти блокировки злыми, глупыми и бессмысленными, но пока что они существуют.

Размещать в интернете статические сайты можно и бесплатно (например, с помощью Github Pages, инструкция от HTML Academy), но в следующих постах я буду рассказывать про штуки, которые потребуют наличие сервера, на котором можно запустить свои программы.

Маленькое отступление: мощностей даже пятидолларового сервера хватит на несколько сайтов, и я работаю над проектом friendware, который упростит создание серверов-коммуналок, чтобы несколько людей могли с лёгкостью сосуществовать на одном сервере.

Но до тех пор, сервер придётся настроить вручную. Для этого понадобится вечер и терпение. Дело в том, что придётся узнать по чуть-чуть про много разных тем, главное — не опустить руки и дойти до победного конца.

Создание сервера

Так как сервер будет находиться далеко, то придётся использовать ssh — это программа для удаленного доступа к серверам. Прежде чем создавать сервер, нужно создать ssh-ключ.

У vscale есть руководство, как создать ключи и добавить их в VScale — с DigitalOcean работает точно так же.

Сам сервер можно создать через интерфейс провайдера (в Digital Ocean сервер называют droplet). При создании нужно будет выбрать операционную систему. Я рекомендую Ubuntu — это один из самых распространённых вариантов, поэтому проще будет найти решения типовых проблем в интернете.

Создание сервера займёт минуту или две, и по окончании процесса получишь айпи-адрес свежесозданного сервера. Чтобы подключиться к нему, в терминале нужно будет набрать:

$ ssh root@айпи-адрес-сервера

И если увидишь что-то вроде этого, то всё получилось:

Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-58-generic x86_64)
* Documentation:  https://help.ubuntu.com
* Management:     https://landscape.canonical.com
* Support:        https://ubuntu.com/advantage
[...]
Last login: Mon Dec  2 17:54:45 2019 from 178.62.245.33
root@bluewhale:~#

Выйти с сервера можно используя команду exit.

Установка nginx

После этого нужно установить на сервер nginx — это программа, которая будет отдавать сайт посетителям.

У DigitalOcean есть туториалы, как настроить веб-сервер, в которых процесс расписан несколько подробнее:

Если под рукой есть друг-линуксоид, то можно помучать его вопросами.

Но если вкратце, то установить nginx можно так:

$ apt install -y nginx

После этого можно попробовать зайти в браузере на айпи-адрес сервера. Если всё хорошо, то увидишь тестовую страницу nginx.

Загрузка файлов

Осталось совсем немного: загрузить страницу на сервер. Для этого есть разные способы, но самый простой это использовать программу scp, которая идёт в комплекте с ssh (никакого отношения к SCP Foundation программа не имеет, это просто secure copy). Для этого в терминале нужно перейти в папку с нужным файлом (cd папка_с_файлом) и запустить:

$ scp ./index.html root@айпи-адрес-сервера:/var/www/html/

Путь /var/www/html/ нужно заменить на свой, если он изменился при настройке nginx.

Можно попробовать перезагрузить страницу с айпишником в браузере — если всё получилось, то увидишь свою страницу.

Прикручивание домена

Но набирать айпи-адрес не весело, заставим этим заниматься компьютер: нужно настроить соответствие домена и адреса сервера. Это можно сделать у регистратора (искать в интерфейсе, обычно по слову DNS), создать запись типа «А» с адресом созданного сервера. После этого нужно немного подождать, потому что доменные записи обновляются не сразу, но через какое-то время сайт будет доступен по доменному имени.

К счастью, процедуру настройки сервера нужно проделать только один раз, а дальше обновлять сайт гораздо проще.

Маленькое отступление номер два: если ты дочитал до этого момента, то я предлагаю поучаствовать в тестировании friendware! Ты получишь юникс-аккаунт и кусочек сервера-коммуналки. С другой стороны, я буду администрировать этот сервер, и поэтому у тебя не получится научиться работе с серверами. Чтобы воспользоваться этим купоном, напиши мне на mt@marinintim.com со словом friendware где-нибудь в теме письма.

День 1: домен · День 3: пост

Пост

Вчера я писал о том, как разместить страницу с базовой информацией про себя в интернете. Это уже неплохо: у тебя больше контроля над тем, как можно представить себя в Сети. Можно добавить себе галочку, как в Твиттере, можно сделать анкету в духе раннего ВКонтакте. Впрочем, можно пойти дальше и «вернуть стену», а именно — начать миниатюрный блог!

В защиту примитивизма

В интернете существует много сервисов, позволяющий создать «свой блог»: Blogger (принадлежит Гуглу), WordPress.com (принадлежит Automattic), LiveJournal (сервис), Ghost (сервис). Всё, что написано в таких блогах, лежит на серверах этих компаний — и если они тебя забанят, то получить обратно свои же тексты будет крайне непросто.

Помимо сервисов, в мире есть много «движков» блогов с открытым кодом, самые популярные это WordPress и Ghost. Оба требуют настроенной базы данных и возможности запускать программы на сервере (PHP в случае WordPress, Node.js в случае Ghost). Эти опции уже лучше, и в целом могут быть неплохим вариантом: приятный интерфейс, интеграции с другими программами, автоматическая генерация разных лент, поиск, теги.

Но, продолжая тему технологического примитивизма, начатую вчера, это всё не нужно, чтобы начать.

Блог — это набор постов, обычно упорядоченных по времени создания.

Пост — это кусочек HTML.

Это можно собрать вручную.

В чём же смысл в том, чтобы делать это вручную, хотя бы поначалу? Я вижу для этого несколько причин.

Во-первых, мне кажется, что разработчики часто нездорово отдаляются от того, что происходит на самом деле. За слоями настроек вебпака, транспиляцией, GraphQL-схемами, да даже конфигурацией идеального набора плагинов для Вордпресса легко упустить, что в итоге нужно просто отдать читателю HTML, в котором будет что-то интересное. Мы легко увлекаемся блестящими штуками, и возвращение, хотя бы ненадолго, к земле, к «просто HTML» может сдвинуть перспективу.

Во-вторых, использование движка, в котором жёстко запрограммировано, что существуют Посты, у которых есть Теги, Дата и Статус Публикации, ограничивает то, что ты можешь придумать. Веб сам по себе нейтрален, блоги не были придуманы Тимом Бернерсом-Ли, они появились практически самостоятельно. Обычно такие движки (и уж тем более платформы-сервисы) предполагают, что пользователь будет публиковать посты, в которых будет текст и картинки. Если же вернуть себе возможность публиковать «сырой» HTML, то можно создавать вещи, которые не очень вписываются в этот формат. Например, календарик Индивеба — это просто HTML и немного CSS. Обратно-хронологический формат тоже подходит не для всего — может ты захочешь сделать гипертекстовую книгу, где нужно переходить по ссылкам, выбирая, что будет дальше,.

Чем технологии проще, тем меньше они настаивают на том, как их нужно использовать.

Как оформить пост

С точки зрения микроформатов, пост это элемент с классом h-entry. Вот так можно разметить коротенький пост, навроде тех, что бывают в Твиттере:

<article class="h-entry">
<a class="u-author" href="/">Тим</a> написал <time class="dt-published">2019-10-29</time>:
<p class="p-name p-content">
программисты be like: эээээ<br>
а потом: ааааа
</p>
</article>

Класс dt-published помечает, когда запись была опубликована, u-author — ссылка на автора, p-name и p-content в данном случае совпадают, но если бы это была статья с настоящим заголовком, то классы стояли бы на разных тегах. Например так:

<article class="h-entry">
<h2 class="p-name">Мини-пост внутри поста</h2>
<div class="e-content">
<p>
Обычно в таких случаях люди используют Lorem Ipsum,
но я люблю своих читателей, и пишу только крафтовые тексты, набранные
вручную. Никакого лорем, никакого янни.
</p>
<p>Впрочем, долорем сит амет. Дура лекс, сед лекс, так сказать.</p>
</div>
<footer>
Написал и опубликовал <a class="u-author" href="/">Тим Маринин</a>
<time class="dt-published">2019-12-03</time>.
</footer>
</article>

Во втором примере p-content заменилось на e-content. Префикс определяет, как нужно использовать это свойство. Всего в текущей версии микроформатов используется 5 префиксов:

  • h-* — вложенный объект (например, можно вложить h-entry внутрь h-feed)
  • p-* — просто текст, например, текст заметки
  • u-* — ссылка на что-либо (в примере — ссылка на автора)
  • dt-* — дата и время
  • e-* — текст с разметкой внутри

Добавлять эту мета-информацию (это пост, этот пост был опубликован тогда-то, автор поста тот-то и тот-то) стоит для того, чтобы пост могла прочитать и машина. Тогда сообщество может разрабатывать инструменты, которые используют эту разметку: например, читалка, которая собирает посты с разных сайтов в одну ленту.

На своём же сайте стоит завернуть посты (h-entry) в h-feed. Проверить, что пост получится распарсить можно при помощи онлайн-инструмента, но проверить h-feed он пока не может.

Кто-то на этом моменте вспомнит про RSS и Atom — и это справедливо, микроформаты частично вдохновлены Atom. Я рассматриваю микроформаты и индивеб в целом как логичное продолжение этой истории. Проблема RSS и Atom в том, что они заставляют авторов поддерживать несколько отдельных представлений контента: одно для людей, другое для машин. Это приводит к странной динамике, где сайты отдают в RSS либо не тот контент, что на сайте, либо только кусочек контента.

Кому это интересно

Но зачем вообще писать и публиковать в вебе?

Я уверен, что в мире существует множество людей, которым интересны те же самые вещи, что и мне. Для многих написать твит или отправить фоточку в инстаграм — это понятное действие, «я хочу поделиться этим со своими друзьями и подписчиками». Для этого же стоит постить и на свой сайт. Но есть и другие причины.

Для меня написание длинных текстов позволяет лучше разложить в голове свои мысли. Сам процесс формулирования, редактуры, перечитывания. Для некоторых текстов «публичность» это просто побочный эффект, главную пользу я извлёк задолго до.

Поэтому меня удивляет, когда знакомые говорят, что «мне нечего опубликовать на своём сайте». Никто никому не обязан быть полезным. Веб — нейтрален, и если хочется постить шутки про вебпак, а длинные рассуждения про индивеб не хочется, то можно спокойно постить шутки на своём сайте.

Так что загляни в свой твиттер, инстаграм, вконтакт — всё это можно постить и на своём сайте.

Конечно, поначалу не будет внешней аудитории (из гугла и других внешних источников), но индивеб не означает одномоментный отказ от имеющихся социальных связей и соцсетей. Но об этом — завтра.

День 2: страница · День 4: прагматизм

Прагматизм

No man is an island entire of itself; every man is a piece of the continent, a part of the main; if a clod be washed away by the sea, Europe is the less, as well as if a promontory were, as well as any manner of thy friends or of thine own were; any man's death diminishes me, because I am involved in mankind. And therefore never send to know for whom the bell tolls; it tolls for thee. Джон Донн, Meditation XVII: Devotions upon Emergent Occasions

Начинать публиковать на своём сайте может показаться с одной стороны одиноким, а с другой — немного заносчивым: все друзья — в социальной сети Икс, они читают мои посты там, и я не могу заставить всех перейти на сторону Индивеба, я же не настолько Важный Автор, чтобы все ради меня отказались от имеющейся социальной сети.

Помимо того, что большие социальные сети берут наш контент, они забирают в заложники и наш социальный граф: на кого я подписан, кто подписан на меня, кто добавил меня в «друзья». Графы бывают разными: в Фейсбуке и ВКонтакте они больше отражают офлайн-знакомства, графы одноклассников, семей, друзей. В Твиттере — граф интересов: я не знаком с подавляющим большинством тех, на кого подписан, не знаю, как они себя ведут в «реальной» жизни, но мне интересно, что они пишут.

Социальный граф — это очень важная часть наших данных, которые мы не задумываясь отдали на откуп большим корпорациям. Про то, насколько этот граф ценен для Фейсбука недавно писал Бен Томпсон.

И это правда — нельзя силой заставить людей перелезть на что-то другое. Тут возникает парадокс, который испытывает любая новая социальная сеть. «Польза» социальных сетей связана с количеством пользователей, а если точнее — с количеством потенциальных связей между пользователями. Этот феномен известен как закон Меткалфа. Его проще всего увидеть на примере сайта с объявлениями, навроде Авито: если нет продавцов, то покупателям незачем заходить на сайт, а раз там нет покупателей, то и смысла размещать объявления тоже нет.

Но внутри кроется цикл с позитивной обратной связью — чем больше продавцов, тем больше покупателей, тем больше продавцов, тем больше покупателей, и если получится раскрутить этот маховик изначально, то дальше он будет крутиться сам.

Индивеб не существует в вакууме. У большинства из тех, кто читает этот пост, уже есть аккаунты в каких-то социальных сетях, и было бы странно просто отказаться от них.

Единственный рабочий способ — переходить постепенно, сохраняя имеющиеся связи. Для этого в Индивебе существует POSSE: Publish (on your) Own Site, Syndicate Everywhere, или ПУСК: Пиши У Себя, Копируй всюду (русскую версию придумал я сам только что). Идея в том, чтобы публиковать изначальную копию на своём сайте, и автоматически или вручную отправлять копию в текущие социальные сети, с ссылкой на оригинал. Тогда друзья в имеющихся социальных сетях всё равно увидят пост, а ссылка позволяет поисковым системам и людям узнать, где была опубликована оригинальная версия, и, возможно, начать читать там. Так Челик Тантек уже долгое время публикует свои заметки и комментарии на своём сайте, и автоматически копирует в Твиттер (да, его логин в твиттере просто @t).

Если человек может извлечь пользу от платформы вне зависимости от количества участников сети, то можно обойти ограничения сетевых эффектов.

Например, меня ограничивают текущие большие платформы: на них нельзя публиковать произвольный HTML и JavaScript, а один из моих недописанных постов-черновиков — визуальное интерактивное объяснение разных алгоритмов сортировки. Также я очень радуюсь возможности в любой момент поправить любой из своих текстов, что до сих пор не реализовано в Твиттере.

Причины для первых пользователей могут быть идеологическими: владеть своим контентом, самостоятельно решать, что стоит постить, а что нет. Для некоторых эта тема ощутимо важнее, чем для других: так, например, Твиттер меняет условия сервиса в 2020 году, и для многих сексработниц сильно вырастут риски бана аккаунта.

Иногда у корпоративных социальных сетей есть преимущества для постинга именно там, которые сложно реализовать самостоятельно: например, чекины в Swarm используют богатую базу мест Foursquare. Для таких вариантов есть альтернатива, PESOS: Publish Elsewhere, Syndicate to your Own Site. Например, OwnYourCheckin позволяет забирать чекины на свой сайт.

ПУСК позволяет сгладить период перехода и начать его прямо сейчас. Вряд ли мы дождёмся от больших корпораций свободного экспорта социального графа, разве что государства начнут требовать его законодательно. Так, в свете GDPR многие платформы позволяют экспортировать то, что уже было опубликовано, в каком-то машиночитаемом формате, как правило, JSON. Твиттер ограничивает выгрузку (не чаще раза в месяц), но получающийся архив содержит все твиты с самого начала аккаунта, и его можно превратить в архив заметок на своём сайте.

Одним из важных принципов Индивеба является make what you need (или scratch your itch): делай, что нужно и важно именно тебе, а не абстрактным «пользователям». Тогда ты получишь пользу прямо сейчас, и можешь перейти на светлую сторону Веба самостоятельно, без необходимости сначала убедить всех своих друзей, что Телеграм-то уж точно лучше Вотсапа или наоборот, пытаясь собрать всех друзей в одном мессенджере. Веб — один на всех.

День 3: пост · День 5: комментарии

Комментарии

У социальных сетей есть важное преимущество перед обычными сайтами — лёгкая обратная связь на пост. Легко отправить фоточку в Инстаграм и получить свои лайки, легко запостить твит и собрать панамку ответов.

На обычных сайтах же обычно можно иногда оставить комментарий, если сайт это поддерживает, или попробовать связаться с автором каким-то другим способом (найти его почту, твиттер, джаббер…).

В Индивебе подход такой: мой контент должен быть на моём сайте в первую очередь. То есть, если мне захочется прокомментировать какую-то статью, то мне стоит написать пост-комментарий на своём сайте, и сослаться на оригинал.

В микроформатах это выражается как ссылка на оригинальный пост со свойством in-reply-to.


<article class="h-entry">
  <h2>В интернете кто-то не прав</h2>
  <p>
    <a
      class="u-in-reply-to"
      href="https://marinintim.com/2019/indieweb">
      Тим Маринин написал статью про Индивеб</a>, но…
  </p>
</article>

С другой стороны, если кто-то ответил на мой пост, я бы захотел об этом узнать! Для этого существуют веб-меншены (WebMentions, то есть веб-упоминания). Работает это так.

  1. Автор ответа (или система, которая используется) берёт ссылку на мой пост.
  2. Находит, куда я принимаю веб-меншены. Для этого на моём сайте есть тег <link rel="webmention" href="сюда-присылайте"> Например, можно запустить в консоли: curl -i -s $target | grep 'rel="webmention"'
  3. Отправляет на этот адрес форму с двумя полями: откуда ссылка и на что. Из консоли можно сделать так: curl -i -d "source=$your_url&target=$target_url" сюда-присылайте
  4. Мой обработчик веб-меншенов получает запрос, проверяет, что с этой страницы действительно есть ссылка, и сохраняет у себя.

Конечно, это можно упростить или даже вовсе автоматизировать. Например, Telegraph позволяет и отправить веб-меншен вручную, и спарсить все ссылки со страницы, чтобы попытаться отправить на них уведомления.

Чтобы принимать такие веб-меншены на своём сайте, можно написать свою реализацию спецификации, а можно воспользоваться встроенной возможностью делегирования: я указал в качестве приёмника веб-меншенов сервис WebMention.io (этот вариант отлично подойдёт для статических сайтов).

И так как использовать уже написанный сервис проще, чем делать самому, то возникает невольная централизация пользователей. Но получается, что я отдаю часть контроля стороннему сервису, где же здесь Инди-, чем это лучше того, чтобы просто отдать свой контент Ello и FriendFeed? Это лучше тем, что позволяет постепенно приходить к Индивебу, а потом, если захочется, можно написать и свою реализацию по открытой спецификации.

У автора сайта так же есть полная свобода в том, как отображать полученные ответы и упоминания: можно отрисовывать их в виде комментариев, можно отображать как количество лайков, можно вообще никак не отображать.

У Индивеб-сообщества есть сильный сдвиг в сторону действия и UX в целом: лучше попробовать три-четыре варианта на самом деле, прежде чем садиться писать спецификацию.

UX и дизайн важнее, чем протоколы, форматы, модели данных, схемы, и т.д.
Мы фокусируемся сначала на UX, и по мере того как понимаем, что нужно сделать, разрабатываем самый простые, лёгкие, минимальные протоколы и форматы, которых хватит, чтобы поддержать этот экспириенс, и ничего сверху. Сначала UX, потом его поддержка.

Этот сдвиг улучшает устойчивость системы: выживает только то, что пригождается на практике, а не абстрактно хорошие, но в целом бесполезные штуки.

И если хочется написать ответ, то напишите у себя — и отправьте мне веб-меншен.

Интерфейс сервиса Telegraph.p3k.io

Заметка от редактора: сайт переехал на Friendware, если найдёте битые ссылки или что-то в целом работает не так, как должно, напишите мне.

День 4: прагматизм · День 6: федерация

Федерация

Меня довольно давно интересуют циклы обратной связи — особенно в контексте системной динамики.

Мне кажется, что разработка в вакууме, где мы бросаем код через стену и не слышим ничего от пользователей, может привести к хорошему продукту только случайно — но последовательными итерациями цикла OODA можно делать продукт лучше систематически.

Вчера я мимоходом упомянул, что технологии Индивеба устойчивее, и сегодня я попробую развернуть эту мысль подробнее

Как ты наверняка знаешь, при помощи электронной почты можно написать любому другому человеку с электронной почтой, даже если он пользуется другим сервисом. Почему нет такого же, только для мессенджеров? Почему мне приходится держать и Вотсап, и Телеграм, и ВК, и эпизодически проверять инстаграмные директы?

В мире очень мало успешных федеративных программных систем: эта область традиционно считается сложной, как с точки зрения программирования, так и с точки зрения эволюции систем.

Это можно увидеть на примере электронной почты. В мире существует множество провайдеров, от гигантов вроде GMail и коммерчески успешных вроде FastMail до университетов и любителей, запускающих почтовые сервера у себя дома. В теории, письмо, отправленное с любого из серверов может прийти на любой другой сервер, и очутиться на другом конце света. Общение между клиентами и серверами и общение между серверами происходит по открытым протоколам SMTP, POP, IMAP — и поэтому реализаций программного обеспечения огромное количество.

Но поддерживать сервер почты нетривиально — особенно, если хочется, чтобы отправленные письма доходили получателям. Пользователям проще использовать больших провайдеров (Гугл вообще предоставляет сервис бесплатно), это приводит к тому, что в большинстве случаев у тебя или твоих корреспондентов будет GMail, и если GMail не будет соблюдать стандарты, то практически никто не заметит.

С другой стороны, есть проблема спама — из-за того, что на любой машине с интернетом возможно поднять почтовый сервер и начать рассылать спам, провайдеры электронной почты с подозрением относятся к любым незнакомым серверам. И если однажды попасть в бан-листы спама, то выбраться из них практически невозможно. Это приводит к тому, что быть «маленьким игроком» невыгодно.

С третьей стороны, протоколы почты, которые я упоминал выше, неидеальны. Они создавались во многом в эпоху интернета, где все доверяли друг другу (что, конечно, приводит к проблемам), но перейти на другой протокол вместо SMTP практически невозможно: нужно обновить тысячам серверов, которые находятся под контролем тысяч разных администраторов.

Похожую динамику можно наблюдать на примере IPv6, замены IPv4. О том, что у IPv4 есть ограничение количества адресов, в которое мы скоро упрёмся, было понятно довольно давно, но цивилизация всё никак не может перейти на новый стандарт, который лишён этой проблемы (и проблемы с NAT заодно): это же нужно обновить миллионы устройств, которые раскиданы по всему миру, а особой пользы от того, что лично ты переключишься на IPv6, нет.

В истории были чаты, построенные на открытых стандартах: IRC и XMPP (Jabber). И хотя оба существуют де-факто, я не могу рекомендовать их использовать, это попросту неудобно. Их заменили Slack, WhatsApp, Signal, Telegram, Discord: всё это мессенджеры, разрабатываемые корпорациями, с жёстко контролируемыми клиентами и серверами.

Мокси Марлинспайк (Signal) написал в 2016 большой пост про то, как федеративность ухудшает UX: там где, централизованная компания может единолично выкатить новую фичу, в федерации нужно убедить обновиться всех (или хотя бы большинство участников).

Когда федеративную систему начинает использовать большое количество людей, то улучшить её становится практически невозможно — она застывает во времени. В IRC никогда не будет стикеров — не потому, что людям не нужны стикеры, а потому что не будет.

(Надо признать, ещё существует Matrix.org, который ставит целью не замкнуть коммуникации внутри себя, а объединять разные сети, я смотрю на проект с некоторым интересом, возможно из него что-то получится)

Как же тогда дело обстоит с Индивебом? За счёт того, что он построен поверх Большого Веба, то он будет существовать ещё очень долго. Мы как цивилизация умудрились создать всемирную паутину — и сделать её действительно всемирной. У нас есть инструменты навроде Archive.org, которые сохраняют историю веба; поисковики, способные проиндексировать огромное количество данных; браузеры, которые постоянно обновляются.

Да, в Индивебе будут медленнее появляться новомодные фичи, чем их будут добавлять продакт-менеджеры Твиттера или Фейсбука. Но принцип progressive enhancement работает не только для разработки коммерческих веб-сайтов: есть ядро, то, без чего никуда (в случае индивеба — публикация постов и чтение постов других), а остальное — это приятные бонусы, которые я могу добавить тогда, когда захочу.

Инерция федераций делает систему более устойчивой, потому что есть бэкапы и бэкапы бэкапов, но замедляет единообразное и единовременное улучшение UX. С другой стороны, фокус Индивеба на людях и контенте в первую очередь, а технологиях и спецификациях — в далекую вторую, делает его менее подверженным проблемам жёстких необновляемых протоколов — гибкость заложена на уровне идеологии.

Впрочем, публиковать HTML всё ещё сложнее, чем это было у Тима Бернерса-Ли на машине Next при создании веба. О том, какие решения придумали в Индивебе, я расскажу завтра.

День 5: комментарии · День 7: микропаб

Микропаб

Вчера я упомянул, что публиковать HTML всё ещё сложнее, чем может быть, особенно с мобильных устройств.

В целом публикация HTML людьми идея совершенно не новая, давным давно существовали GeoCities и Народ.ру.

Но социальные сети сделали публикацию ещё проще: просто заполни формочку. У Тумблера были разные форматы постов: картинки, аудио, видео, текст, цитата. Но в подавляющем большинстве сайтом можно постить в основном заметки, с картинками и, изредка, видео.

Индивеб-сообщество создало протокол Micropub, который описывает именно эту часть опыта использования сети. У спецификации Micropub есть статус «Рекомендация» в терминах W3C.

В протоколе участвуют трое: клиент, сервер и медиа-эндпоинт. Клиент — там где пользователь пишет пост. На самом деле, речь не только про обычные посты — микропаб как протокол нейтрален, главное, чтобы то, что постишь, можно было представить в виде микроформатов.

Сервер ответственен за получение и сохранение постов. Медиа-эндпоинт — место, куда сервер может делегировать обработку медиа-файлов, например, картинок (но мне было бы любопытно увидеть медиа-эндпоинт для аудио, для облегчения хостинга подкастов).

За счёт нейтральности протокола, уже есть с десяток разных клиентов: они могут быть везде, откуда можно отправить форму.

Quill, один из клиентов

Клиент на самом деле не обязан даже быть предназначен для непосредственного использования человеком. Так был клиент OwnYourGram, который автоматически доставал картинки из инстаграма и постил их через микропаб на твой сайт (сейчас у них проблемы из-за того, что инстаграм блокирует их запросы).

Есть клиенты для мобильных (но можно написать и свой!)

Indigenous для iOS

Есть клиенты, которые сделаны в виде расширения для браузера.

В рамках проекта friendware я работаю над своим микропаб-клиентом, пишу его на Svelte (когда будет хотя бы красиво скриншотабельно, то обязательно покажу).

В целом, мне кажется, что самые интересные автоматические клиенты ещё не написаны. Интернет видел много разных твиттер-ботов, а написать индивеб-бота ещё проще — нужно просто отправить форму с нужным текстом и заголовком Authorization. Так, например, можно отправить простую заметку с текстом hello world, зная токен:

fetch('/micropub', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer секретный-токен'
  },
  data: 'h=entry&content=hello+world'
})

С токенами не всё просто, но похоже на интеграцию с Twitter или Facebook: внутри используется OAuth 2. Подробнее можно почитать на вики, а тут можно пройти весь флоу получения токена вручную (чтобы, например, получить токен для скрипта или для тестирования).

С серверами, на мой взгляд, всё сложнее — они требуют динамики. Если статические файлы можно сложить куда угодно, то здесь потребуется сервер. И отдельно требуется поддержка авторизации — как правильно принять и проверить этот самый токен.

Впрочем, можно взять уже и готовую серверную реализацию, и подпилить под свои нужды. За счёт того, что Микропаб — это стандарт, сервер и клиент можно писать отдельно, и если оба соответствуют спецификации, то любые клиент и сервер будут работать вместе.

Веб не предназначался только для чтения — но у нас долго не было стандартизированного способа писать в Веб. С 2017 года такой стандарт есть.

День 6: федерация · День 8: логин

Логин

Когда-то на каждом сайте, где была функциональность «за логином» (то есть, доступная только после регистрации аккаунта), приходилось регистрироваться отдельно, вбивать почту, придумывать пароль.

Затем появился OAuth 2 (я нарочно опускаю всю историю с OpenID), и некоторые крупные сайты стали предоставлять возможность «войти через Икс»: такое есть у Твиттера, Фейсбука, ВКонтакте, Гитхаба.

Тонкий момент заключается в том, что в этом случае доступ к сервису, в котором я зарегистрировался через кнопку «Sign-in with Github», зависит от моего аккаунта на Гитхабе — если мой аккаунт заблокируют, потому что окажется, что я из неугодной страны, то я потеряю доступ ко всем прочим сайтам. Но если получится вставить между сайтом и Гитхабом ещё один слой, который контролирую я, то получится обойти эту ситуацию.

На этом построены web-sign-in и IndieAuth. Выглядит это так: на своём сайте я добавляю ссылки на свои аккаунты на Гитхабе, Твиттере, Фейсбуке, и добавляю им атрибут rel="me".

Если аккаунты по ссылке ссылаются обратно на мой сайт, то можно с большой долей уверенностью считать, что так получилось не случайно, и я контролирую и сайт, и аккаунт. После этого я могу логиниться на сайты, поддерживающие IndieAuth, используя свой домен. На примере абстрактного сайта example.org это выглядит так:

  1. я вбиваю https://marinintim.com в качестве своего логина
  2. example.org редиректит меня на IndieLogin (сервис, упрощающий интеграцию)
  3. IndieLogin смотрит, какие ссылки есть на моём сайте с атрибутом rel="me" из провайдеров, с которыми он умеет работать
  4. IndieLogin предлагает мне выбрать, через какого провайдера я хочу подтвердить, что это я
  5. IndieLogin начинает OAuth с выбранным провайдером
  6. Когда всё успешно, то меня перебрасывает на example.org, и я залогинен.

Ключевой момент здесь в том, что example.org получает в конце только https://marinintim.com, и не знает, через какой сервис я это подтвердил: я смогу в следующий раз выбрать не Гитхаб, а Твиттер, или вообще заменить ссылку на своём сайте на другой профиль.

По сути, IndieAuth — это протокол, основанный на OAuth 2.0, в котором указано, что идентификатор пользователя — это URL, который можно открыть в браузере (не вдаваясь в детали: OAuth 2.0 это не протокол, а скорее конструктор, из которого можно собрать протокол, поэтому у всех немного разные реализации).

Чтобы начать использовать это всё как пользователь, достаточно закинуть на свой сайт ссылки на свои аккаунты. Сделать на своём сервисе поддержку IndieAuth несколько сложнее (как обычно с OAuth), поэтому появился IndieLogin, который сильно упрощает поддержку протокола.

И чтобы проверить, что у тебя получилось, можно зайти на сайт pin13.net или завести аккаунт на вики Индивеба.

Конечно, независимость, которая получается в этом случае, тоже не безграничная: регистратор домена может разделегировать домен, или хостинг может отказать хостить твой сайт. Но такие случаи происходят сильно реже, чем когда кто-нибудь в централизованных веб-сервисах решает забанить пользователей по каким-то причинам.

День 7: микропаб · День 9: комментарии часть два

Комментарии часть два

Если кратко: Пример того, как можно отрендерить веб-меншены, используя Svelte и сервис Webmention.io.

Я уже писал про вебменшены, а сегодня хочу поделиться небольшой штукой, которую написал на Svelte: простая рендерилка веб-меншенов, которые она достаёт с Webmention.io, куда я делегирую свои веб-меншены.

Ответы, лайки и репосты

Webmention.io даёт довольно простой API, где можно получить в виде JSON веб-меншены для переданного URL, и внутри этого JSON есть информация про автора, иногда картинка, и тип события. Я пока обрабатываю только like-of, repost-of и in-reply-to.

Но если вглядеться в скриншот, то можно заметить, что это лайки из Твиттера, а не с инди-сайтов. Продолжая тему прагматизма, я не отказываюсь от общения с людьми, которые пока не завели себе свои сайты, но мне хочется делать это с моего сайта. Bridgy делает за меня запросы в API Твиттера и отправляет веб-меншены.

Мне кажется, что проще всего будет объяснить свой компонент в стиле литературного программирования.

В целом, Svelte-компоненты состоят из скриптов, стилей и разметки. Скрипт заворачивается в <script>, стили — в <style>, ну а разметку и заворачивать не надо.

<script>let likes = []
let replies = []
let reposts = []
let other = []

let loc = document.location.href;
let mentionsRequest = fetch('https://webmention.io/api/mentions.jf2?target=' + loc, {
    headers: { accept: 'appplication/json' }
})
.then(r => r.json())
.then(mention_feed => {
    if (mention_feed && mention_feed.children) {
        mention_feed.children.forEach(mention => {
            switch (mention['wm-property']) {
                case 'like-of': likes.push(mention); break;
                case 'repost-of': reposts.push(mention); break;
                case 'in-reply-to': replies.push(mention); break;
                default: other.push(mention); break;
            }
        })
    }

    likes = likes
    reposts = reposts
    replies = replies
    other = other
})
</script>

Я запрашиваю все вебменшены и раскладываю их по четырёх категориям, в зависимости от wm-source: in-reply-to, repost-of, like-of, прочее.

Стили сами по себе не очень интересные, поэтому я их скипну (если любопытно, то в полном исходнике их легко увидеть).

В Svelte добавляет в HTML немного деклараций, например {\#await promise}контент{/await} будет отрисовывать контент, пока промис не зарезолвится. Выше в скрипте я сохранил запрос к API в mentionsReq, поэтому могу отрисовать плейсхолдер, пока ждём ответа от API.


{#await mentionsRequest}

<p>Подгружаю вебменшены...</p>

{/await}

Чтобы отрисовать ответы, я прохожусь по массиву replies и рисую каждый ответ. {@html reply.content.html} отрисует «сырой» HTML, который пришёл из API. Так как ответ написал не я, то я завернул это всё в <blockquote>, и в футере ставлю ссылку на оригинал.


{#if replies.length}
    <section>

        {#each replies as reply, i}
            <article class="reply">
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-message-square"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
                {#if reply.content && reply.content.html}
                <blockquote class="reply">
                    {@html reply.content.html}
                    <footer class="source">
                        <a class="author" href="{reply.author.url}">
                            <img class="avatar" src="{reply.author.photo}" alt=""> {reply.author.name}</a>
                        <a class="link" href="{reply['wm-source']}">{reply['published']}</a>
                    </footer>
                </blockquote>
                {:else}
                <a href="{reply['wm-source']}">{reply['wm-source']}</a>
                {/if}
            </article>
        {/each}
    </section>
{:else}
    <p>Пока ответов нет. Если напишешь ответ — пришли <a rel="nofollow" href="https://indieweb.org/Webmention">вебменшен</a>!</p>
{/if}

С лайками и репостами ещё проще — я решил рисовать только аватарку и ссылку на того, кто лайкнул.



{#if likes.length}
    <section>
        <svg aria-label="лайки" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-heart"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
        <ul class="likes">
        {#each likes as like}
            <li class="like"><a title="{like.author.name}" href="{like.author.url}"><img alt="{like.author.name}" class="avatar" src="{like.author.photo}"></a></li>
        {/each}
        </ul>
    </section>
{/if}

{#if reposts.length}
    <section>
        <svg aria-label="репосты" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-repeat"><polyline points="17 1 21 5 17 9"></polyline><path d="M3 11V9a4 4 0 0 1 4-4h14"></path><polyline points="7 23 3 19 7 15"></polyline><path d="M21 13v2a4 4 0 0 1-4 4H3"></path></svg>
        <ul class="reposts">
        {#each reposts as repost}
            <li class="repost"><a title="{repost.author.name}" href="{repost.author.url}"><img alt="{repost.author.name}" class="avatar" src="{repost.author.photo}"></a></li>
        {/each}
        </ul>
    </section>
{/if}

И, наконец, корзина «остальное» — просто рисую ссылками.


{#if other.length}
    <section>
        <p>Вебменшены, которые не лайки-репосты и не ответы:</p>
        <ul>
            {#each other as mention}
                <li><a href="{mention['wm-source']}">{mention['wm-source']}</a></li>
            {/each}
        </ul>
    </section>
{/if}

Svelte добавляет немного себя в HTML, но мне кажется, что это всё ещё читается почти как HTML. SVG-иконки взяты из проекта Feather Icons.

Компонент я скомпилировал в ES6-модуль, а использование выглядит примерно так:

import Webmentions from '/webmentions.mjs';

new Webmentions({
	target: document.querySelector('#webmentions')
})

В принципе, из этого можно сделать рендеринг на стороне сервера, но в таком, не-server-side-виде люди увидят самые свежие упоминания на момент загрузки страницы, а не упоминания-на-момент-сборки-сайта: такое решение подойдёт даже статическим сайтам. Но получается, что вебменшены видны только тем людям, у которых включён джаваскрипт (и в случае, если я ничего не сломал).

В целом, я довольно много за эти дни написал о том, как публиковать, но есть и обратная сторона — как читать. У сообщества есть несколько наработок на эту тему, про которые расскажу в ближайшие дни (спойлеры: RSS/Atom, JSONFeed, WebSub/PuSH, h-feeds, Microsub).


Момент, который стоит учитывать, чтобы получилось отправить свой вебменшен: у записи-ответа должен быть свой URL. Вообще у всех записей должен быть свой URL. Это необязательно должна быть отдельная страница, это может быть ссылка-фрагмент (вида example.com/#some-identifier), главное, чтобы по этой ссылке можно было достать обратно h-entry. Проверить, что пост размечен понятным для машин способом можно на IndieWebify.me.

И теперь, когда мой сайт показывает веб-меншены, можно отправить свой — и, если всё ок, через минуту-две увидеть его здесь.

День 8: логин · День 10: ридер

Ридер

Если кратко: Лучшие способы читать веб ещё не придуманы.

Сегодня мы потребляем Веб в том виде, как решили продукт-менеджеры в больших корпорациях. Алгоритмические ленты, релевантная и нерелевантная реклама, интерфейсы для Целевой Аудитории.

Если повезёт, то команда, работающая над интерфейсами, слышала про доступность и работает над этим. Если повезёт, то новый A/B тест не поломает твои сценарии использования.

Индисайты публикуют посты в формате, который могут читать и люди, и машины — мы можем делать интерфейсы, которые подходят именно нам. Например, Твиттер, но заточенный прямо для меня.

Ридер, или читалка, это мой агент в сети — она собирает посты с сайтов, на которые я подписался, и показывает их в удобном мне виде. В идеале — ещё и даёт возможность провзаимодействовать с постом: лайкнуть, ответить, зарепостить.

На вики Индивеба есть список уже существующих читалок, но я предлагаю, если есть возможность, попробовать написать свою.

Одна из ловушек мышления, в которую частенько попадают программисты, — это желание сделать общее решение: зачем писать программу для вот этой конкретной проблемы, когда можно потратить больше времени и получить Универсальный Фреймворк для Решения Таких Проблем, на котором я с лёгкостью решу изначальную проблему. Эта ловушка мышления иногда оправдана: когда мы точно понимаем Такие Проблемы. Но чаще у нас есть Некоторые Предположения, которые могут оказаться неверными, а мы уже потратили полгода на обсуждение Архитектуры и Правильных Абстракций.

Это я всё к чему. У Индивеба есть два важных принципа: UX is more important than formats and specs и use what you make. UX — это сложно. Пытаясь сделать Универсальную Читалку, программисты больше фокусируются на абстракциях и логике, но мало думают про то, как это использовать. На самом деле, это одна из причин, почему ПО с открытым кодом обычно имеет интерфейс, который, ну, не очень.

Когда логика системы уже запрограммирована, то изменить её сложно. Случаются ситуации, где UX будет лучше, если бы абстракции в системе, которыми манипулирует пользователь, были немного другими. Но редко кто будет переписывать ради этого программу, и в результате страдает UX.

Но если зайти с другой стороны: ориентироваться больше на опыт взаимодействия с интерфейсом, чем на код, который его поддерживает, то получившийся продукт будет лучше. Как пример: в приложениях iOS обычно нет кнопки «Сохранить», хотя внутри такая же файловая система, как на Маке, где такие кнопки есть, оно просто работает, и такой UX сложно получить, идя от того, что уже есть.

Возвращаясь к читалкам, очень сложно заранее угадать, какой интерфейс лучше всего подойдёт для меня для чтения постов. Если не думать об этом, то получится только скопировать имеющиеся: Фейсбук/ВК, Твиттер, RSS-читалки, Инстапейпер/Покет. Это тоже может быть валидной целью, но мне кажется, что лучшие читалки ещё не написаны.

Поэтому так важно писать не для кого-то, не для абстрактного пользователя, а для себя — если я буду делать для себя и использовать каждый день, то недостатки будут мешать в первую очередь мне — и мотивировать на улучшения. Большим компаниям приходится для этого собирать тонну аналитики, но когда главный пользователь — это ты, то легко понять, это работает или это не работает.

С другой стороны, есть определенная часть программирования, которой не избежать при написании читалок: например, скачивание фидов и парсинг микроформатов. Чтобы упростить разработку читалок, появилась ещё одна спецификация: Microsub. В ней тяжелые части берёт на себя сервер, а клиенту отдаёт JSON, который уже легко отрендерить. Можно взять Aperture, сервер Microsub, написаный Аароном Пареки, одним из основателей IndieWebCamp.

Постепенно, из десятков разных проектов мы, как сообщество, неизбежно будем находить общие моменты, которые работают для многих, и это хорошо! Но не стоит пытаться делать универсально изначально. Сначала надень маску на себя, потом на ребёнка.

Про свою читалку я расскажу завтра.

День 9: комментарии часть два · День 11: follow me

Follow me

Если кратко: Потребление контента меняет нас. Вдумчивое отношение поможет сделать эти изменения осознанными.

Первый звоночек прозвучал, когда Твиттер отключил нотификации для сторонних клиентов. Поддержка нотификаций пропала из Твитбота, который был моим любимым клиентом на айфоне: у меня даже не стояло «официального» приложения Твиттера.

Я начал открывать официальную мобильную версию, чтобы посмотреть на нотификации. И из-за того, что это чуть больше действий (вместо открыть приложеньку → увидеть нотификации приходится открыть сафари, открыть твиттер, увидеть нотификации), я стал замечать, как часто я это делаю.

И чем больше я наблюдал за собой, тем меньше мне нравился Твиттер и я-в-нём, вечная погоня за социальным одобрением в виде лайков, которая неявно приводит к тому, что ты постишь не столько то, что хочется, а то, что понравится аудитории (при этом, конечно, аудитория по большей части у тебя в голове).

Мозг часто воспринимает счётчик фолловеров как прокси для оценки ценности (если ты сейчас «да я, да никогда…»: случалось увидеть стрёмный ответ в твиттере и успокоить себя тем, что у этого мало подписчиков? А радоваться, когда количество подписчиков переходит какой-то рубеж?). То же самое с лайками, дело зашло настолько далеко, что Инстаграм экспериментировал со скрытием лайков: The idea is to try and depressurize Instagram, make it less of a competition, give people more space to focus on connecting with people that they love, things that inspire them (Морисси, цит. по Variety).

Потом начинаешь подмечать структуру в постах: депрессия, пост-ирония, manufactured outrage. У этого много причин (Россия для грустных, в мире много плохого, zeitgeist), но в конечном итоге я понял, что посещение веб-сайта твиттер дот ком редко помогает мне в чём-либо, кроме как залипнуть или разозлиться, и очень странно обнаружить себя выбирающим раз за разом снова погрузиться в вот это всё.

Параллельно с этим я начал читать Stratechery, который помог начать думать об индустрии™ более системно, а не только в терминах гугол плохо (впрочем, AMP всё ещё плохо).

У больших социальных приоритеты сдвинуты в сторону повышения вовлеченности, а какой — уже не столь важно. И если исправить Твиттер и Инстаграм нам не под силу, то стоит позаботиться хотя бы о себе.

Размышления привели меня сюда, к Индивебу по-настоящему. Если в 2016 году я скорее был восхищён возможностью и сообществом, то теперь решил принимать активное участие действием, сдвигая его в сторону, которая кажется правильной именно мне. В том числе, писать этот адвент-календарь. :)

Перебороть привычки нелегко: пока что я по-прежнему часто открываю твиттер на телефоне. Но попытки писать на своём сайте, и только потом отправлять в твиттер, помогают взять паузу и подумать о том, что я хочу сказать (и хочу ли).

Но если выстраивать иерархию важности, то она выглядит так:

  1. люди
  2. что пишут люди
  3. где пишут люди

В качестве читалки я пока что настроил Monocle+Aperture, во многом потому, что это очень просто: нужно добавить на свой сайт пару <link> и нажать пару кнопок. Я продолжаю читать многие сайты, просто заходя на них. При этом в бэкграунде размышляю над тем, как выглядел бы хороший экспириенс чтения для меня, чтобы затем его реализовать.

Очень легко начать делать очередной RSS-ридер, или что-то приближенное к нему. Если хочется такого — можно взять уже написанные (впрочем, если нужно написать своё — полный вперёд, покажи потом код). Но мне не хочется делать «просто ещё один твиттер». Вполне вероятно, что Microsub в реализации моей читалки будет только мешать, в силу предположений, которые есть внутри спецификации.

Думая над тем, какие сценарии решает (хотя бы в теории) для меня Твиттер, я составил такой список. У тебя он может быть абсолютно другим, но задуматься, что дают текущие социальные сети, стоит каждому.

Мои сценарии в Твиттере

  1. быть в курсе новостей: не началась ли война
  2. быть в курсе новых штук в профессии: пора ли переходить на гриды
  3. знать, что происходит у друзей-в-городе: не пойти ли нам в бар
  4. знать, что происходит у друзей и знакомых, которые живут далеко: не женился ли Кабан
  5. пассивно узнавать штуки, про которые было бы интересно узнать: криптография, распределенные системы, фермеры США
  6. поорать от смешных мемов

Новости

В общем, это, наверно, самый понятный сценарий, но и у него есть интересный поворот: вместо того, чтобы читать условную NY Times или BBC, можно отдельно подписываться на журналистов, чья работа тебе нравится: супер-важное-не-от-них принесёт ретвитами.

У каждой новостной организации есть свой взгляд на мир, свой bias, и на него стоит делать поправку. Про это есть известная реклама Guardian.

Тут есть два под-сценария: а) узнать, не случилось ли что суперважного, что точно повлияет на мою жизнь сейчас или в скором будущем, б) узнать что-то важное, но о важности чего я не догадывался.

Сейчас я решаю это чтением телеграм-канала Бумаги, эпизодическим визитом на яндекс-новости и подписками на журналистов в Твиттере.

Твиттер профессиональный

Очень часто можно встретить совет, что разработчику нужен твиттер: чтобы читать коллег, и таким образом расти в профессии. Но как мы знаем по пятничным хэштегам, редкий разработчик пишет о разработке, как же тогда работает этот самый рост?

Мне кажется, что твиттер для многих выступает в роли условной курилки, где можно потрындеть о профессии, и этот back channel позволяет чувствовать общий пульс: важно не мнение каждого конкретного разработчика про Реакт, но Дискурс™.

С другой стороны, если вступать в перепалкидиалоги, то это помогает попасть в сообщество. Здесь кажется более эффективным приходить вживую на митапы, но они есть не везде, и не всем легко разговаривать с незнакомыми ещё людьми.

Важно, чтобы у человека-со-стороны была возможность наблюдать и подключиться к дискуссии, если она покажется ему интересной, иначе сообщество превратится в замкнутый междусобойчик.

С точки зрения читалки, кажется разумным иногда подписываться не только на людей, но и на темы (хэштеги) или условные клубы. Но для этого нужны «хабы» или «пабы» — точки пересечения людей. Для постов про Индивеб есть Индиновости (контента на русском языке там пока нет, но это поправимо).

Друзья-в-городе

Здесь большую часть проблемы можно было бы решить большим групповым чатом, если бы получилось собрать всех в одном приложении (что порождает подпроблему, что не все со всеми дружат, such is life).

И так как мы переходим уже от абстрактных людей к настоящим людям, то здесь можно пропагандировать индивеб. Если у тебя сайт уже настроен, об этом можно рассказать и показать друзьям.

Это отличается от предыдущего пункта фокусом на людях: мне интересно, всё, что они пишут, даже если они устраивают марафон Звёздных Войн или рекомендуют какое-нибудь новое кафе. В случае с сообществом меня больше интересуют мнения людей про код и расписание митапов.

Друзья-далеко

ВКонтакте относительно неплохо решал эту проблему, до того момента, как я перестал пользоваться ВКонтакте. Когда я переехал в Петербург, то в Томске осталось много знакомых, и интересно, что с ними происходит.

Я ещё размышляю о том, как это должно выглядеть, но хочется большей сжатости. Если забыть про географическое деление, то можно разделить на условные «друзья» и «знакомые»: про друзей интересно всё, про знакомых скорее выжимку событий, как в LinkedIn.

С другой стороны, интерес прочитать обратно связан с количеством постов: если человек пишет раз в месяц, то мне интересно, что же он выбрал на этот раз. Инстаграм присылает пуш-уведомления в этом случае, мне хочется что-то менее отвлекающее, может быть, отдельная вкладка «Редкий изумруд».

Пассивно узнавать

Это, как мне кажется, одна из самых интересных фич твиттера, которая нигде толком не описана.

Если хочется узнать больше про какую-то тему или поменять взгляды на что-то, то подпишись на людей, которые этим занимаются. Постепенно, на протяжении какого-то длительного времени, ты будешь видеть другие точки зрения, видеть, что им любопытно, что они ретвитят, и поневоле забирать это в своё сознание.

Например, если хочется узнать больше про феминизм, можно пойти читать теорию, а можно подписаться на пять-десять активисток, и читать. Не взаимодействовать, не спорить, не просить разъяснить матчасть, а просто слушать. Понять конкретных людей всегда проще, чем целые движения.

На другом примере: подписавшись на десяток людей, занимающихся криптографией, я узнал больше о том, как криптографы думают и подходят к проблемам, чем при попытке пройти бонэмовский онлайн-курс Crypto I. Подписавшись на историков, ты будешь получать в своей информационной диете больше истории — и больше думать о ней.

Я не уверен насколько Ситник сдвигает Дискурс в целом, но мне кажется, что восприятие нюдсов среди фронтендеров действительно стало как-то проще. Repeatead exposure matters.

Тут нужно наводить мосты. Я не заставлю этих людей перелезть на другие платформы: я их не знаю! Но то, как я читаю не обязано совпадать с тем, куда они пишут. Райан Баррет писал про переводчик между Microsub и традиционными сервисами типа Feedly/Feedbin/Miniflux, чтобы читать свои RSS-подписки в инди-читалке: можно представить аналогичные сервисы для твиттер-аккаунтов, телеграм-каналов, даже ВК.

Особенно если не делать это публичным сервисом формата один-на-всех, а индивидуальным, тогда даже не важно, предоставляет ли сервис настоящий API: можно притвориться пользователем и просто забирать HTML. Я часто думаю про концепцию adversarial interoperability, о которой узнал в отличном выпуске подкаста Y Combinator с Кори Доктороу.

Facebook doesn’t have users. It has hostages. People hate it. They lost 15 million Americans aged 13 to 34 last year, the largest ever exodus of Americans from Facebook.

All those people landed on Instagram, right? I know what I’m going to do. I’m going to offer a tool that, just like Mint, logs into Facebook, pretends to be you, grabs all of the messages waiting for you on Facebook, cleans out all the waiting queue in your favorite groups, and then puts them into a new context for you, where Facebook can’t watch you and allows you to reply to them.

Facebook, if you believe that they have 2.3 billion users, which may or may not be true, if they have 2.3 billion users, they have to support 2300 one in a million use cases every single day, which means that their bot detection and their ability to distinguish a bot from someone who’s just doing a one in a million thing is really, really hard.

Про мемы

Есть ещё отдельная категория потребления, которая пробивается в любые социальные сети.

В твиттере оно выражалось сначала в форме текстовых шуток, а потом сократило путь до мозга и превратилось в Весёлые Картинки™.

По наблюдениям, у меня есть два подхода к такому потреблению: я его ищу целенаправленно и, обычно, тематически (например, r/KOTORmemes), или же контент настигает меня сам через ретвиты от людей, на которых я уже подписан.

В целом, я пока не планирую как-то специально поддерживать это в читалке.

Место, где нажать кнопку рефреш

Но эти сценарии возникают на рациональном уровне. Подсознательно, я полагаю, мне просто хочется дёрнуть pull-to-refresh и получить либо дозу удовольствия, либо дозу социального одобрения.

И вот этот момент мне интересно попробовать побороть в себе: подходить к потреблению осознанно, понимая зачем я делаю это сейчас.

Потребление контента — неожиданно более интимная штука, чем может показаться на первый взгляд, и в нём всё очень индивидуально. Сценарии, которые я описал выше, — мои, у тебя могут быть совершенно другие (и мне было бы интересно о них узнать!)

Описывая их, я с каждым пунктом всё больше понимаю, что Твиттер решает их плохо. Лучше всего у него получается discovery, находить новых людей, основываясь на том, что я уже читаю — но и здесь лучше работает «смотреть кому отвечают интересные мне люди», чем сам блочок Who to follow.

Если же начать задумываться о кейсах, которые Твиттер вообще не решает, то этот пост не закончится вообще никогда (навскидку: посты от людей на Петроградке, агрегация лучших ссылок, спрашивать советов, нытьё на узкий круг людей).

Надеюсь, что к концу календаря я смогу показать работающий прототип своей читалки. А до тех пор, предлагаю попробовать сделать своё или начать с Monocle.

День 10: ридер · День 12: события

События

Если кратко: Микроформат h-event позволяет публиковать события, на которые могут откликаться другие люди.

Одним из типов контента, который люди публикуют в Фейсбуке и ВКонтакте, являются события: объявление о том, что тогда-то и тогда-то там-то и там-то случится то-то и то-то.

В целом организация событий — дело непростое, требующее много сил и времени. Но просто рассказать друзьям о том, что ты планируешь куда-то пойти или что-то организовать — не так сложно.

В движении микроформатов для этого придумали микроформат h-event.

Выглядит это как-то так:


<div class="h-event">
<a class="u-url p-name" href="https://marinintim.com/2019/hwc/">HWC: встреча про Индивеб</a>
<p>
Начало <time class="dt-start" datetime="2019-12-22T15:00:00.000Z">22 декабря (вс), 18:00</time>,
и до <time class="dt-end" datetime="2019-12-22T18:00:00.000Z">девяти вечера</time>
<p>
Адрес:
<span class="p-location h-card">
<a class="u-url p-name p-org" href="https://vk.com/in1oogramm_bar">бар «Ин100грам»</a>,
<span class="p-street-address">Ул. Декабристов, 7</span>,
<span class="p-locality">Санкт-Петербург</span>,
<span class="p-country-name">Россия</span>
</span>
</p>
</div>

Главные свойства — это u-url, в котором лежит ссылка на событие, dt-start, который описывает время начала, p-location, в котором описано, где же пройдёт событие.

У <time> есть много вариантов, как может быть представлена дата

Конечно, помимо этих свойств может быть описание события, и всё остальное, что тебе придёт в голову. Но эти свойства — машиночитаемы, и поверх них можно сделать RSVP.

RSVP: ответ на событие

Как же сделать для индисобытия кнопку «Пойду»? Конечно же, при помощи микроформатов и Webmention!

Такой вид поста называется RSVP (неожиданно из французского).

На своём сайте нужно опубликовать пост-ответ со специальным полем p-rsvp. Возможные ответы: yes, no, maybe, interested, поэтому чтобы писать на русском, нужно использовать тег data, и нужный вариант положить в атрибут value. Например, так:


<div class="h-entry">
<span class="p-author h-card">
<a class="u-url" href="https://marinintim.com">
Тимофей Маринин
</a>
</span>:
<data class="p-rsvp" value="yes">приду</span>
на <a href="https://marinintim.com/2019/hwc/"
class="u-in-reply-to">HWC: встречу при Индивеб</a>
</div>

Указание автора в p-author здесь особенно важно, чтобы организаторы понимали, кто придёт

После этого нужно отправить вебменшен от этого поста по обычным правилам.


Кстати, да! Событие в примерах выше — реальное. 22 декабря я буду рад встретиться и поговорить про индивеб вживую в баре Ин100грам на Декабристов, 7. Присылайте RSVP и приходите.

День 11: follow me · День 13: Webmention.app

Webmention.app

Если кратко: Webmention.app позволяет легко отправлять вебменшены из консоли.

Вебменшены и микроформаты — основа Индивеба. Поверх них можно сделать всё остальное.

Для того, чтобы добавить микроформаты, достаточно просто добавить немного классов в свою разметку, но с вебменшенами понадобится немного больше работы.

Я уже писал про Webmention.io, но он предназначен для приёма вебменшенов, но ещё полезно уметь отправлять вебменшены.

Для валидного вебменшена нужно чтобы «источник», то есть то, откуда ссылаются, уже было опубликовано в интернете. После этого нужно найти эндпоинт, куда автор сайта принимает вебменшены и отправить туда POST-запрос с source и target.

Это можно делать вручную, можно реализовать спецификацию самостоятельно, а можно воспользоваться уже готовыми инструментами.

Один из таких инструментов: Webmention.app. У него есть веб-версия и консольная утилита. Его написал Рэми Шарп.

Оно написано на джаваскрипте, поэтому поставить несложно, если есть npm:

$ npm install -g @remy/webmention:

Команда называется webmention. Её аргумент — ссылка, откуда нужно распарсить ссылки. То есть, если я хочу проверить, куда отправятся вебменшены с моей статьи Прагматизм, я выполню такую команду:

$ webmention https://marinintim.com/2019/indieweb/4
source = https://marinintim.com/2019/indieweb/4/
target = https://stratechery.com/2019/portability-and-interoperability/
endpoint = https://stratechery.com/xmlrpc.php (pingback)

source = https://marinintim.com/2019/indieweb/4/
target = https://indieweb.org/POSSE
endpoint = https://webmention.io/indiewebcamp/webmention (webmention)

source = https://marinintim.com/2019/indieweb/4/
target = https://tantek.com
endpoint = https://webmention.io/tantek.com/webmention (webmention)

source = https://marinintim.com/2019/indieweb/4/
target = https://indieweb.org/OwnYourCheckin
endpoint = https://webmention.io/indiewebcamp/webmention (webmention)

А чтобы отправить по-настоящему, нужно добавить --send:

$ webmention https://marinintim.com/2019/indieweb/4 --send

Вебменшены и микроформаты позволяют создать минимальную живучую версию социального веба: поверх них можно сделать лайки, комментарии, закладки, цитаты, события, и много всего другого. Поэтому так важно научиться их отправлять, пусть поначалу и вручную.

День 12: события · День 14: маркдаун

Маркдаун

Если кратко: Генераторы статики навроде 11ty упрощают публикацию текстов и в то же время помогают сохранить контент надолго

Читатели в вебе получают HTML, но у нас есть довольно много опций, как организовать создание этого HTML, и не все из них хороши.

Можно использовать большие сервисы: твиттер, тумблер, фейсбук — я уже писал о том, чем это чревато. Если же брать сервисы поменьше, то они частенько умирают.

Зачастую можно успеть вытащить свои тексты и фотографии обратно, но если на посты кто-то давал ссылку — то она пропадёт.

Когда же у тебя есть контроль над ссылками, то можно поменять хостинг, движок, практически всё — и оставить ссылку рабочей.

Тема живучести ссылок появилась не вчера — ещё в 1998 году Тим Бернерс-Ли, создатель веба, писал о том, что крутые URI не меняются (там же, в частности, не рекомендуют использовать урлы с .html на конце).

Но сегодня я хочу обратить внимание на момент, который сильно упрощает миграцию между технологиями, — хранение постов в файлах, в маркдауне.

Написать блог, который достаёт посты из базы, несложно. В целом, это первое, что делают люди, когда начинают изучать фреймворк Ruby on Rails. Но привязывая свой контент к базе данных, мы заставляем себя поддерживать эту базу данных и на сервере — а поддерживать базу данных несколько сложнее, чем написать пару SQL-запросов.

На вики Индивеба это называют DBA tax, так сказать, налог на БД: то время и силы, которые ты тратишь на настройку, поддержку, бэкапы, обновления базы данных вместо того, чтобы писать посты и радоваться жизни. Не все считают это такой уж проблемой, и вполне можно делать свой сайт как захочется — но в варианте без БД меньше движущихся частей.

Но есть вариант не писать HTML-страницы целиком, и в то же время не возиться с базами данных: генераторы статических сайтов. Их существует огромное количество, но самый прекрасный, на мой взгляд, вариант — это Eleventy (11ty).

Он написан на джаваскрипте, поэтому его легко поставить через npm, и с ним легко начать работу: если у вас есть просто набор файлов, то вы, по факту, уже можете использовать 11ty, и конвертировать файлы по мере необходимости, вынося, например, общие части разметки в шаблоны. Большая Миграция, где нужно переделывать все файлы, не нужна.

На 11ty делают новую версию сайта Веб-стандартов. В качестве другого примера можно посмотреть на репозиторий подкаста LP.

Но практически любой генератор статических сайтов поддерживает формат Markdown, который придумал Джон Грубер в нулевых (markdown это игра слов про markup, разметку): он упрощает написание текстов, которые превратятся в HTML.

Скриншот текста в формате Markdown

Почему Маркдаун, а не reStructured Text, или вовсе просто HTML? Дело в том, что HTML на телефоне набирать попросту неудобно, а у Маркдауна хорошая поддержка на iOS: существует много программ, от Drafts до Ulysses.

(Во многом, я полагаю, из-за авторитета Грубера в сообществе разработчиков под платформы Apple. Если знаете хорошие приложения для маркдауна под Андроид — дайте знать.)

Таким образом можно писать черновик поста в одном из таких приложений на ходу, а потом уже опубликовать на своём сайте. Маркдаун поддерживается на многих сайтах для гиков, навроде Гитхаба и Стэковерфлоу, но когда публикуешь у себя, то внутрь маркдауна можно добавить любой HTML.

Простые текстовые файлы с маркдауном легко хранить — у меня не осталось бэкапов от многих вариаций «да щас я блог напишу на кложуре», но полным-полно текстовых файлов, которые я постепенно буду добавлять обратно на этот сайт (часть из них осталась вживых благодаря Дропбоксу).

С простыми файлами просто жить.

Но как же тогда поддержать Micropub на своём статическом сайте? Нужно, чтобы где-то был микропаб-сервер, который будет принимать новые посты, и перезапускать генератор статики.

Как я уже писал, я разрабатываю и пробую на себе Friendware. В нём это решается двумя программами: одна из них, micropubd, принимает посты и кладёт их в текстовые файлы, а вторая, onchanged, следит за изменениями в папке с файлами и запускает скрипт build.sh, в котором я вызываю 11ty. Веб-сервер раздаёт сайт из папки _site, куда 11ty складывает результат по умолчанию.

Так нельзя сделать на хостинге, где можно только размещать статические файлы (например, Github Pages), зато можно реализовать при помощи гита и Github Actions (это на случай, если хотелось с ними поиграть, но всё как-то повода не было).

Если хранить контент в системо-независимом формате, то сохранить его надолго становится гораздо проще.

День 13: Webmention.app · День 15: WebSub

WebSub

Если кратко: WebSub (бывший PubSubHubbub) позволяет подписчикам узнавать о новых постах практически мгновенно.

Я часто в этом календаре упоминаю Твиттер, во многом, потому что это единственная большая социальная сеть, которой я пользуюсь каждый день: не смог разобраться в Фейсбуке, перестал заходить ВКонтакте, а Гуглплюс и вовсе закрыли.

На главной Твиттера, если разлогиниться, показывается основной слоган: See what’s happening in the world right now.

У твиттера за счёт централизации есть реалтайм: новые посты сами прилетают в ленту практически сразу. Но как в отсутствие твиттера, мои читатели узнают, что я опубликовал что-то новое?

Первый наивный ответ: зайди да посмотри, то есть, поллинг. Человек (или, чаще, программа) заходит на сайт и смотрит, нет ли чего нового. «Наивный» здесь не ругательство — это решение, которое решает большую часть проблемы и довольно легко в реализации.

Одним из первых подходов к проблеме был RSS (расшифровки: Rich Site Summary, полная выжимка сайта, Really Simple Syndication, Очень Простое Распространение). Это XML-документ, в котором можно описать ленту постов, и многие сайты в двухтысячных публиковали обновления сайтов в этом формате. У читателя, соответственно, должна быть читалка, которая понимает этот формат и ходит скачивать новую версию файла раз в какой-то промежуток времени.

В наши дни читатели часто используют не десктопные RSS-читалки, а веб-сервисы, что несколько решает проблему популярных сайтов: представь, что компьютеры сотни тысяч читателей запрашивают раз в пятнадцать минут этот файл — это сотни тысяч запросов. Feedbin и Feedly запрашивают файл один раз для всех своих пользователей.

Но у поллинга есть две проблемы: у многих сайтов не выйдет новых постов за этот час, то есть запросы происходят зазря, а с другой стороны, когда пост всё-таки выйдет, то я не узнаю о нём, пока читалка не скачает обновление через час. Поэтому появился WebSub — это протокол, где сайт присылает уведомление про новый пост. С этим протоколом всё не так просто: изначально он назывался PubSubHubbub (я не шучу); для него нужен хаб; чтобы получать апдейты, нужен сервер. В версии 0.4 появилась возможность публиковать в реалтайме не только обновления в RSS/Atom-лентах, но любых штук, доступных в вебе. Меня больше всего интересует h-feed на главной странице, список опубликованных постов.

Реалтайм требует работы от того, кто публикует контент. Для поддержки WebSub понадобятся три составляющие:

  1. Хаб: подписчики подписываются на нем на обновления, он рассылает новый контент подписчикам
  2. Две ссылки в виде тега link или http-заголовка Link: одна на себя (то есть, на обновления чего будут подписываться люди, rel="self") и на хаб (rel="hub")
  3. Пинг: каждый раз, когда обновляешь или публикуешь новый контент, нужно пингануть хаб, чтобы он получил новую версию

Superfeedr предоставляет бесплатные хабы (можно использовать «общий»: http://pubsubhubbub.superfeedr.com/, но на своём будет дополнительная аналитика вида «сколько подписчиков»).

Теги добавить не сложно, единственное, что нужно быть аккуратным — rel="self" должен быть точным, без редиректов.

Пинг хаба — это просто POST-запрос с двумя параметрами: hub.mode=publish и hub.url=урл_из_рел_селф. Его можно делать вручную, а можно добавить пинг в скрипт, который делает деплой (если же сайт динамический, то можно рассматривать как часть логики публикации).

Таких фидов может быть много: например, фид на главную страницу, на категории постов, на ответы.

Подписываться на обновления несколько сложнее, и тот же Aperture до сих пор не поддерживает WebSub, полагаясь на поллинг, но всё ещё впереди.

Используя WebSub, мы можем сделать посты в вебе реалтаймовыми и без больших корпораций.

День 14: маркдаун · День 16: ActivityPub

ActivityPub

От Тима: привет! Сегодня в календаре — гостевой пост про ActivityPub. Это важная часть истории про альтернативы корпоративным социальным сетям, которая развивалась параллельно стандартам Индивеба Microformats и Webmention.

Мне хочется, чтобы ActivityPub был упомянут в календаре, но мне не хватает знаний из первых рук про всю эту экосистему — поэтому я попросил Гришу написать пост про ActivityPub. ActivityPub-экосистема подходит к федеративным социальным сетям с несколько другой стороны, но не буду спойлерить.


Если кратко: ActivityPub — это самый популярный протокол федерации между серверами социальных сетей, микроблогов и прочих подобных вещей.

По своей концепции ActivityPub очень похож на SMTP. Есть независимые сервера, на каждом сервере есть свои пользователи, у каждого пользователя есть инбокс. В инбоксы кладут «активити» — действия других пользователей, которые так или иначе касаются получателя. Совершают эти действия «акторы» — пользователи, группы, боты, и так далее. Например, если Вася с сервера mastodon.xyz подписан на Петю с сервера example.social, и Петя создаёт новый пост, Петин сервер положит этот пост Васе в инбокс на его сервере, и он появится в Васиной ленте.

Вся совокупность серверов социальных сетей, которые федерируются друг с другом, называется fediverse — соединение слов federation и universe.

Все объекты представлены в (весьма наркоманском) формате JSON-LD и передаются по HTTPS, но пока что представьте себе, что это обычный JSON с ключом @context. Вот пример объекта (актора) пользователя, обратите внимание на ключ inbox:


{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://w3id.org/security/v1"
  ],
  "id": "https://example.social/users/petya",
  "type": "Person",
  "url": "https://example.social/@petya",
  "preferredUsername": "petya",
  "name": "Петя",
  "inbox": "https://example.social/users/petya/inbox"
  "outbox": "https://example.social/users/petya/outbox",
  "followers": "https://example.social/users/petya/followers",
  "following": "https://example.social/users/petya/following",
  "summary": "<p>Тот самый мазохист, который упомянут в спецификации LD-signatures<\/p>",
  "endpoints": {
    "sharedInbox": "https://example.social/sharedInbox"
  },
  "icon": {
    "type": "Image",
    "url": "https://example.social/uploads/avatars/79b12c2b44826342fa0fe07d9662010d.jpg"
  },
  "publicKey": {
    "owner": "https://example.social/users/petya",
    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n",
    "id": "https://example.social/users/petya#main-key"
  },
}

Вот создание (активити типа Create) поста (объекта типа Note) пользователем (актором типа Person):


{
  "@context":[
    "https://www.w3.org/ns/activitystreams",
    {"sensitive":"as:sensitive"}
  ],
  "id":"https://example.social/posts/2851/activity",
  "type":"Create",
  "actor":"https://example.social/users/petya",
  "to":["https://www.w3.org/ns/activitystreams#Public"],
  "cc":["https://example.social/users/petya/followers"],
  "published":"2019-12-15T16:28:35Z",
  "object":{
    "id":"https://example.social/posts/2851",
    "type":"Note",
    "to":["https://www.w3.org/ns/activitystreams#Public"],
    "cc":["https://example.social/users/petya/followers"],
    "attributedTo":"https://example.social/users/petya",
    "published":"2019-12-15T16:28:35Z",
    "sensitive":false,
    "content":"<p>Я сегодня видел няшного котика!<\/p>",
    "url":"https://example.social/posts/2851"
  }
}

Получить ActivityPub-объект для любого пользователя или поста можно по адресу этого пользователя или поста, добавив в запрос заголовок Accept: application/ld+json, например:

$ curl -H "Accept: application/ld+json" https://mastodon.social/users/grishka

Результат:


{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://w3id.org/security/v1",
    {
      "manuallyApprovesFollowers":"as:manuallyApprovesFollowers",
      "toot":"http://joinmastodon.org/ns#",
      "featured":{
        "@id":"toot:featured",
        "@type":"@id"
      },
      "alsoKnownAs":{
        "@id":"as:alsoKnownAs",
        "@type":"@id"
      },
      "movedTo":{
        "@id":"as:movedTo",
        "@type":"@id"
      },
      "schema":"http://schema.org#",
      "PropertyValue":"schema:PropertyValue",
      "value":"schema:value",
      "IdentityProof":"toot:IdentityProof",
      "discoverable":"toot:discoverable",
      "focalPoint":{
        "@container":"@list",
        "@id":"toot:focalPoint"
      }
    }
  ],
  "id":"https://mastodon.social/users/grishka",
  "type":"Person",
  "following":"https://mastodon.social/users/grishka/following",
  "followers":"https://mastodon.social/users/grishka/followers",
  "inbox":"https://mastodon.social/users/grishka/inbox",
  "outbox":"https://mastodon.social/users/grishka/outbox",
  "featured":"https://mastodon.social/users/grishka/collections/featured",
  "preferredUsername":"grishka",
  "name":"Гришка",
  "summary":"\u003cp\u003eГришка, теперь децентрализованный!\u003c/p\u003e",
  "url":"https://mastodon.social/@grishka",
  "manuallyApprovesFollowers":false,
  "discoverable":true,
  "publicKey":{
    "id":"https://mastodon.social/users/grishka#main-key",
    "owner":"https://mastodon.social/users/grishka",
    "publicKeyPem":"-----BEGIN PUBLIC KEY-----\n[redacted for brevity]\n-----END PUBLIC KEY-----\n"
    },
  "tag":[],
  "attachment":[
    {
      "type":"PropertyValue",
      "name":"VK",
      "value":"\u003ca href=\"https://vk.com/grishka\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003evk.com/grishka\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"
    },
    {
      "type":"PropertyValue",
      "name":"Сайт/блог",
      "value":"\u003ca href=\"https://grishka.me\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egrishka.me\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"
    }
  ],
  "endpoints":{
    "sharedInbox":"https://mastodon.social/inbox"
  },
  "icon":{
    "type":"Image",
    "mediaType":"image/jpeg",
    "url":"https://files.mastodon.social/accounts/avatars/000/909/172/original/c8f4511dede32ad7.jpg"
  }
}

Сам по себе ActivityPub слишком сложный и многогранный, чтобы быть полностью описанным в одной статье адекватной длины. Если появилось любопытство, хочется больше подробностей и потыкать в настоящие сервера, я бы советовал начать с этих двух статей в блоге Mastodon.

Существующие реализации

На данный момент есть уже достаточно много различного серверного софта, поддерживающего ActivityPub:

Всё это в изрядной степени работает друг с другом. То есть, я могу подписаться на пользователя Pixelfed со своего акканта в Mastodon, и смотреть на его фотографии котов в своей ленте. Как тебе такое, Илон Маск Марк Цукерберг?!

Мой опыт

Мне, как человеку, полжизни прожившему и 5 лет проработавшему ВКонтакте, очень хотелось аналогичной социальной сети, но со стеной без Mail.ru Group и без централизации со всеми её последствиями. Посмотрел я такой на это всё, и решил сделать свою, потому что Friendica слишком далека от того, что я хочу, и назвал её Smithereen. Да, этот Smithereen.

Пишу на джаве. Для федерации, само собой, использую ActivityPub. С мастодоном уже работает, но до чего-то презентабельного там далековато, так что ссылок пока не будет. Но могу описать сложности, с которыми я столкнулся:

  • JSON-LD — наркоманский формат. Это как XML со схемой и неймспейсами, только JSON. Для нормального участия в fediverse надо уметь его корректно парсить, переводя всё полученное в понятный тебе вид. Есть спецификация, в которой по шагам описаны алгоритмы преобразования, но лучше поберегите свою психику и не читайте её без необходимости.
  • Подписывание запросов. По-хорошему, каждый запрос должен быть подписан приватным ключом актора два раза: HTTP-подписью и LD-подписью. Первая генерируется и проверяется просто, но запросы, подписанные только HTTP-подписью, нельзя проксировать, т.к. подписываемые заголовки включают в себя Host, делая подпись зависимой от хоста назначения запроса. Вторая — это очень больно, ибо предполагает многочисленные неочевидные манипуляции с JSON-LD, но запросы с ней проксировать можно, нужно и полезно. Подробно о моих страданиях с LD-подписями можно почитать вот здесь.
  • Первопроходчество. Его много. Всё существующее так или иначе похоже на твиттер, концепции друзей ни у кого нет, сообществ тоже ни у кого нет, даже стен ни у кого нет. И это я пока не начал думать про фотоальбомы и прочие видеозаписи. Но сам по себе протокол достаточно открытый для интерпретации и расширения, приходится много всего придумывать.

Что почитать по теме

День 15: WebSub · День 17

Импорт

Я недавно обнаружил, что пишу в твиттер десять лет с перерывами.

Подумал и решил их переложить на свой сайт, а там уже разобраться, что оставлять, что удалять.

Для этого пришлось выкачать архив Твиттера, открыть в нём tweet.js и исправить этот файл на JSON (удалив начало). В итоге в нём получается массив из твитов, с которыми можно делать что угодно.

Вот так выглядит один твит:

{
  "retweeted" : false,
  "source" : "<a href=\"https://tapbots.com/software/tweetbot/mac\" rel=\"nofollow\">Tweetbot for Mac</a>",
  "entities" : {
    "hashtags" : [ ],
    "symbols" : [ ],
    "user_mentions" : [ ],
    "urls" : [ ]
  },
  "display_text_range" : [ "0", "140" ],
  "favorite_count" : "11",
  "id_str" : "924968941260242944",
  "truncated" : false,
  "retweet_count" : "1",
  "id" : "924968941260242944",
  "created_at" : "Mon Oct 30 11:59:15 +0000 2017",
  "favorited" : false,
  "full_text" : "Подходит математик к трём логикам:\n— Что, все пойдём бухать?\n— Не знаю, — говорит первый\n— Не знаю, — говорит второй\n— Да! — говорит третий.",
  "lang" : "ru"
}

Я решил сконвертировать каждый твит в заметку, оставив ссылку на твиттер, но не конвертируя лайки/ретвиты. Получился вот такой несложный скрипт на джаваскрипте:

// всякие зависимости
const exec = require('child_process').execSync
const mkdirp = (path) => { exec('mkdir -p ' + path) }
const fs = require('fs')

// достаю интересующие меня части твита
function tweet2note(tweet) {
  let date = new Date(tweet.created_at)

  // путь к файлу: 2019/12/17/id_твита/index.md
  let path = date.toISOString()
    .split('T')[0] // 2019-12-17
    .replace(/-/g, '/') +
    '/' + tweet.id_str

  let note = {
    text: tweet.full_text,
    meta: {
      lang: tweet.lang,
      publishing_software: tweet.source,
      date: date.toISOString(),
      syndicated: {
        twitter: `https://twitter.com/marinintim/status/${tweet.id_str}`
      },
    },
    path: path,
  }

  if (tweet.entities && tweet.entities.media) {
    for (let m of tweet.entities.media) {
      if (m.type === 'photo') {
        // пока что просто вставляю <img> с src твиттера
        note.text += `
  <img src="${m.media_url_https}" />`
      }
    }
  }

  if (tweet.in_reply_to_status_id_str) {
    // добавляю ссылку на твит, на который отвечаю
  note.meta.in_reply_to = `https://twitter.com/_/status/${tweet.in_reply_to_status_id_str}`
    note.meta.in_reply_to_text = 'твит @' + tweet.in_reply_to_screen_name
  }

  return note
}

// преобразую в текст файла index.md, который 11ty превратит в HTML.
function note2file(note) {
	const meta = note.meta
	return `---
layout: note.njk
tags:
- note
- twitter_pesos
lang: ${meta.lang}
author: Тим
date: ${meta.date}
syndicated:
  twitter: ${meta.syndicated.twitter}
publishing_software: '${meta.publishing_software}'
${
  meta.in_reply_to
  ? `in_reply_to: "${meta.in_reply_to}"
in_reply_to_text: "${meta.in_reply_to_text}"`
  : ''}
---
${note.text}
`
}

console.log(`Total tweets: ${tweets.length}`)

let i = 0;
for (let tweet of tweets) {
  let note = tweet2note(tweet)
  mkdirp(note.path)
  // делаю синхронно, потому что иначе нода умирала,
  // а разбираться почему было лень
  fs.writeFileSync(note.path + '/index.md', note2file(note))
  console.log(`[${++i} / ${tweets.length}] ${note.path + '/index.md'}`)
}

В итоге оказалось, что твитов в том архиве было больше восьми тысяч, и 11ty начал несколько захлёбываться, пытаясь их засунуть на одну страницу и сделать авто-линковку.

Oh well. Пока что добавил большую часть из них в .eleventyignore, но потом непременно включу в общий сайт. На /notes пока отрисовываются 100 последних заметок, в будущем я добавлю архивы по месяцам.

В результате работы скрипта я получил восемь тысяч файлов, вот пример одного из них:

---
layout: note.njk
tags:
- note
- twitter_pesos
lang: en
author: Тим
date: 2018-01-10T17:59:56.000Z
syndicated:
  twitter: https://twitter.com/marinintim/status/951151635073519616
publishing_software: '<a href="http://twitter.com" rel="nofollow">Twitter Web Client</a>'

---
[AT THE THINGS-NAMING FACILITY BACK IN THE DAY]
*looks at the paper with an article about man biting the dog*
- Is it about something new? Let's call it news. *stamp* Next!

*looks at the strip of photos w/ jumping horse*
- Pictures are moving? Let's call it movies. *stamp* Next!

И вот пример вёрстки, которая получается в итоге:

<article class="h-entry">
  <span class="u-author h-card">
    <img class="avatar u-photo" src="https://marinintim.fra1.digitaloceanspaces.com/tim_and_green.jpg" alt="">
    <a class="u-url h-card" href="https://marinintim.com">Тим</a>
  </span>
  ·

  <a href="/notes/2018/01/10/951151635073519616/" class="u-url">
    <time datetime="2018-01-10T17:59:56.000Z">10 января</time>
  </a>
  <div class="e-content p-name">
    <p>[AT THE THINGS-NAMING FACILITY BACK IN THE DAY]
    <em>looks at the paper with an article about man biting the dog</em></p>
    <ul>
      <li>Is it about something new? Let's call it news. <em>stamp</em> Next!</li>
    </ul>
    <p><em>looks at the strip of photos w/ jumping horse</em></p>
    <ul>
      <li>Pictures are moving? Let's call it movies. <em>stamp</em> Next!</li>
    </ul>
  </div>
  Эта же заметка <a class="u-syndication" href="https://twitter.com/marinintim/status/951151635073519616">в твиттере</a>
</article>

В архиве также есть папка tweet_media, где лежат все загруженные картинки, и их можно сопоставить с твитами по айдишнику.

Примерно по этому же принципу можно сохранить свои посты из любой соцсети:

  1. Раздобыть архив или сохранить через API все посты
  2. Написать скрипт, который переделает архив в контент сайта
  3. Шага три нет. Хотя нет, можно переписать скрипт из шага 2 ещё раз.

На днях я собираюсь утащить свои посты из ВК, о чём напишу на индивеб-вики/VK.

Если всё ещё кажется, что «мне нечего писать на своём сайте», то попробуй импортировать свой твиттер или инстаграм и обнаружишь, что тебе есть, что сказать или показать миру: так почему бы не со своего сайта?


Пока я не собираюсь удалять свой твиттер-аккаунт, но бэкапы — это всегда хорошо, а когда они ещё и доступны для просмотра — ещё лучше.

День 16: ActivityPub · День 18: Два фида

Два фида

Если кратко: Можно указать «каноничную» версию фида ссылкой с классами u-uid u-url

Один из моментов, которые мне нравятся в этом сайте — это то, как чисто выглядит главная страница: немного ссылок на статьи, фотография, моё имя.

Но в индивеб-читалках такая разметка превратится только в заголовки, без какого-либо намёка на содержимое.

В общем, хочется, чтобы в читалках показывался полный текст, а на странице было видно только ссылки, без тонн описания.

Решение подсказала Вика: завести вторую страницу с полными текстами, и прилинковать её из основного фида.

<div class="h-feed">
    [...h-entry]
    <a class="u-url u-uid" href="/full_feed/">Фид целиком</a>
</div>

Читалки, увидев u-uid u-url, поймут, что это ссылка на полную версию фида и предпочтут её. Впрочем, возможно, что некоторые люди тоже предпочтут вариант «много постов на одной странице» — это частое решение для блогов.

Этот паттерн с двумя вариантами ленты называется partial feed, и это неплохой компромисс между принципом DRY и дизайном, который я хочу. Второй HTML-файл лучше RSS или Atom-ленты, потому что это один и тот же формат, пусть и с разным количеством контента внутри: не возникает проблем вида «лента валидна в этом формате, но невалидна в этом».

Сейчас HTML на главной странице и для полного фида я генерирую по-разному: на главной хочется выделить блок с Индивеб-календарём, поэтому эти статьи стоят отдельно. Для я этого я добавляю всем статьям «теги» в терминологии 11ty: индивеб-статьям добавлены теги indiewebcalendar и post, обычным статьям — entry и post; полный фид делает выборку по post, а главная — две выборки.


Если же в фиде и так уже лежат посты целиком, то делать второй HTML-файл для машин не стоит: это повышает усилия по поддержке сайта (надо обновлять шаблоны в двух местах), и сдвигает фокус от людей на машины.

Каждый раз, когда начинаешь делать разметку не для людей, а для машин (кто сказал Schema.org?), стоит поймать себя за руку: а нельзя ли сделать эту информацию, которую я добавляю, полезной не только машинам, но и людям?

Я как-то переводил для «Фронтира» эссе Доктору про метаданные («Метачушь», оригинал 2001 года), в котором он убедительно показывает, что метаданные не работают: не точны, теряются, перестают быть актуальными. Когда же делаем из метаинформации просто информацию, то становится проще её поддерживать.

Микроформаты помогают добавлять минимум мета-: ссылка на полную версию фида это всё-таки ссылка, которую может нажать читатель.

День 17: Импорт · День 19

Несовершенство

Если кратко: HTML очень легко относится к ошибкам, мы можем также.

Корпоративным сетям необходимо огромное количество пользователей, потому что инвесторы ждут прибыли от своих вложений, им нужны миллионы пользователей, по копеечке поведенческих данных с каждого — инвестору очередной миллиард.

На таких масштабах невозможно думать о людях как людях, они становятся пользователями.

Индисайт, или сайт одного человека, не рассчитан на миллионы пользователей: он в первую очередь для меня, потом для моих друзей, затем для тех, кому интересно, что я тут пишу, для архивов интернета, в конце концов, но Инвесторы в этой цепочке не возникают вообще.

Поэтому на этом сайте нет Гугл- или Яндекс-аналитики — мне незачем собирать эти данные, я не показываю рекламу. Full disclosure: эпизодически я смотрю на аксесс-логи, на огонь, воду и аксесс-логи можно смотреть бесконечно.

Когда я делаю свой сайт, я не думаю в масштабах человечества, я думаю в человеческих масштабах. Например: ошибки на индисайтах делают инструменты надёжнее.

Когда делаешь сервис для миллионов людей, то каждая ошибка, пусть даже она проявляется всего один раз на сто миллионов запросов, кого-нибудь обязательно заденет. На моём сайте — нет. (В то же время, если что-то на моём сайте не работает, то достучаться до меня гораздо проще.)

Это приводит к тому, что программистам больших сайтов хочется разложить мир по полочкам, с полочками проще работать: тогда будет меньше ошибок. Например, Фейсбук предлагает всем сайтам добавить себе определенную невидимую мета-информацию в виде og: метатегов для превьюшек, вместо того, чтобы распознавать её из того, что есть на странице.

Жёсткие структуры легче ломаются. Это было одной из главных проблем XML-эпохи Интернета: один не там закрытый тег, и читатель вместо сайта видел большое сообщение об ошибке. У универсальной разметки много потенциальных преимуществ, но жёсткий подход умирать при любой ошибке не взлетел (до появления сайтов на Реакте, конечно).

Но антоним жёсткости здесь не столько «гибкость», сколько устойчивость. Никакая строгая типизация тайпскрипта не спасёт от того, что в браузер доехала только половина бандла, а потом в вагоне метро пропал интернет, потому что этот случай находится вообще за пределами мира тайпскрипта, в этом мире и веба-то толком нет.

HTML и CSS очень толерантны к ошибкам авторов: незакрытый тег закроется сам, если нет тега <html>, то браузер его сам добавит, да ещё и попытается угадать кодировку текста, если она не указана. Это пример закона Постела в действии (Джон Постел — один из отцов Интернета).

Стоит держать этот принцип в голове, когда делаешь индивеб-инструменты, особенно те, что взаимодействуют с сайтами других. Тогда инструменты будут получатся менее хрупкими, и они будут работать для всех.

Инструменты нужны не для абстракций и красивой архитектуры, а чтобы работало. Так, например, в спецификации парсера микроформатов указано, что следует парсить и старый, устаревший вариант разметки.

За счёт того, что индивеб построен на микроформатах, а те, в свою очередь, на HTML, то система в целом получается более устойчивой, и от одной пропущенной двойной кавычки не упадёт.

Невалидная страница сайта — это на которой читателю нечего прочесть. Остальное уже детали.


Тема хрупкости также обсуждалась сегодня с немного другой стороны Джеффом Хуангом («This Page is Designed to Last»), в своей статье он пропагандирует простые решения (впрочем, тоже HTML без реакта и вебпаков), чтобы соптимизировать лёгкость поддержки сайта в работоспособным виде в долгой перспективе.


Не надо подходить к своему сайту так же серьёзно, как к постройке ракеты. Если сайт взорвётся, то всегда можно задеплоить новую версию, no big deal.

Недавно пришёл октябрьский the Economist, с таким вот артефактом:

журнал Economist с необрезанной частью страницы, артефакт производства

И ничего, нормально. Люди ошибаются, и это нормально.

Забавы ради

Если кратко: Эту статью надо читать на странице поста, не в ленте

Возвращаясь к вопросу причин публикации на своём сайте, хочется подробнее остановиться на форматах.

Формат в этой статье это не PNG и даже не HTML, а что-то более отвлечённое: например, книга и пост это два разных формата, и то, и другое может быть HTML-страницей, но они всё-таки отличаются для нас.

Маклюэн говорил, что «мы пятимся назад в будущее»: то есть, подходим к новым вещам как к старым, по крайней мере, поначалу; думаем по-старому, осмысляем будущее словами из прошлого.

Так, многие западные веб-дизайнеры врёмен начала профессии до появления веба работали в полиграфии — и потому пытались обращаться с вебом как с бумагой (но он — не бумага, см. Dao of Web Design, 2000 год). Так, многие программисты по-прежнему думают в парадигме batch processing, оставшейся от времён перфокарт и больших компьютеров (в обработке данных это теперь называется гордо ETL, но интерфейсы для пользователей вписываются в эту портянку с трудом).

Примерно то же самое происходит тогда, когда нам хочется чем-то поделиться, мы ищем подходящий формат, опираясь на то, что уже привычно: этот завтрак — инстаграмный, а вот этот кусок разговора тянет на «я запощу это в твиттер».

Инстаграм-сторисы это не просто фотографии в инстаграме, а что-то другое, со своими правилами. Каждый человек как-то интуитивно понимает, что идёт в профиль, что идёт в сторисы, а что скидывается просто в DM.

Мне интересны новые форматы. Сейчас они чаще всего появляются в виде новых приложений: от, собственно, Инстаграма до Тиктока, но это очень дорогой способ экспериментировать. Я уже писал про проблему приватизации социального графа; а любое новое приложение довольно вероятно не взлетит или испортится. Поэтому я размышляю о новых форматах, которые могут появиться на личных сайтах.

Обычно когда мы говорим про независимые публикации в сети, то думаем про блоги — реверсивная хроника событий обратно-хронологическая последовательность текстовых постов. Как пример: kottke.org (публикуется с 1998 года). Само слово «блог» — это отголосок от web log, журнал, то есть, мы осмысляем возможности веба через прошлое.

Но сайт не обязан быть таким! Как пример, ранние страницы jwz связаны между собой не по датам, а по темам, с вручную расставленными ссылками: associate.html, dig.html, dadadodo.html.

Новый формат не может зародиться внутри существующих больших социальных сетей, потому что там уже и так много ограничений, и авторам остаётся только играть внутри текста, как боулинг с оградками.

Формат может возникнуть по инициативе самой платформы (например, ныне покойный Vine). Как редкое исключение: ретвиты в твиттере придумал не твиттер, а пользователи: они копировали текст понравившегося твита, добавляли RT @пользователь в начале, и постили как свой.

Чтобы было пространство для нового формата, платформа должна добавлять минимум своих ограничений. У iOS много ограничений, но программы могут рисовать на экране что им угодно, используя по мере необходимости фреймворки системы для удобства.

Существующие форматы создают ожидания. Обычные блоги на мой вкус частенько слишком серьёзные (мой — не исключение), и я несколько скучаю по хаосу народ.ру и Geocities. Очень много примеров Geocities есть в блоге One Terabyte of Kilobyte Age.

Серьёзность это плохо. Она не даёт выйти за рамки того, как принято сейчас, поэтому возникает риск застрять в локальном максимуме. Новые форматы поначалу кажутся нелепыми и непонятными (см., например, на ленту твиттера десятилетней давности), но только так мы находим что-то по-настоящему рабочее.

Самиздат это хаос, хаос — отсутствие формата, отсутствие формата порождает новый формат.


Когда есть возможность писать HTML, CSS и JS самостоятельно, то веб-страница превращается из просто текста в холст для самовыражения в два тега. Я захотел, чтобы на этой странице падали снежинки — и сделал. Тебе могут не понравится мои снежинки, но они нравятся мне.

Держи подарок: http://nm.merz-akademie.de/~helene.dams/icanhashistory/

NASCAR

(Внимание: эту статью надо читать до конца, прежде чем делать)

На многих сайтах есть целый арсенал кнопок в конце страницы: поставить +1 на гуглплюсе, пошэрить в твиттере, расшарить в фейсбуке. Это довольно удобно, когда хочется поделиться постом, чтобы не копировать ссылку вручную.

Обычный вариант, которые предлагают сервисы, — добавить к себе на сайт их джаваскрипт, который нарисует кнопки, но тогда а) дизайн этих кнопок определяется сервисами, б) сервисы получают информацию о том, кто посещает твой сайт (если человек залогинен в сервис), в) для каждого сервиса нужно добавлять свой джаваскрипт.

Огромное количество разномастных кнопок создаёт так называемую NASCAR problem, где сайт становится похожим на гоночную машину с десятком логотипов брендов.

Илья Бирман создал «Лайкли», небольшую библиотеку/виджет, которая умеет рисовать такие кнопки для нескольких сервисов в едином стиле.

Но во всех таких кнопках прописаны урлы Твиттера, Фейсбука, ВК, что делать, когда у каждого читателя есть свой сайт? Не добавишь же миллион кнопок. Нужна одна кнопка, которая для каждого читателя превратится в ссылку на личный интерфейс для постинга.

Эту проблему можно решить, добавив уровень indirection. Это мог бы быть какой-то отдельный, централизованный сервис, который бы редиректил пользователей на их сайты. Но в 2014 году на IndieWebCampUK придумали и спрототипировали неожиданное решение, в виде кастомной схемы урлов!

В целом, «поделиться», «лайкнуть», «репостнуть» и прочие действия в вики называют web actions.

У Аарона Пареки есть видео, как эта штука работала. В ней несколько составляющих:

  • способ постить: вероятнее всего, Micropub,
  • URL для интерфейса постинга,
  • особый джаваскрипт на сайте, чьей статьёй хочется поделиться,
  • инди-конфиг, который расскажет этому сайту, где находится интерфейс для постинга.

Инди-конфиг — это страница на твоём сайте (например, где-нибудь в админке), которая вызывает window.parent.postMessage и передаёт туда JSON-конфигурацию, самые частые поля которой это reply — урл для ответов, и like —  урл для лайков. То есть, примерно такой config.html:

И нужно подключить эту страницу как обработчик схемы web+action:

window.navigator.registerProtocolHandler('web+action', 'https://marinintim.com/friendware/config.html?url=%s', 'Tim Marinin');

Если открыть страницу в браузере, то появится предложение зарегистрировать обработчик.

В Firefox предложение установить обработчик довольно заметно, а в Хроме спрятано за ромбик в адресной строке.

Сайт, на котором есть кнопка «Поделиться», загружает айфрейм с адресом web+action:load и подписывается на сообщение, которое отправит config.html, из которого достаёт нужные урлы и открывает их, заменяя {url} на урл, которым хочется поделиться.

При первом нажатии на ссылку с кастомным протоколом появится диалог выбора.

В начале статьи я предупредил, что нужно дочитать до конца, прежде чем начать делать. Дело в том, что это работало в 2014, но не работает сейчас — и Хром, и Файрфокс не дают загрузить айфрейм со своей схемой (Файрфокс просто, Хром жалуется на CSP). В принципе, сейчас может сработать вариант просто делать ссылку для лайка как кастомную схему, например, web+like://url-to-like, но у такого варианта нет фолбэка: если читатель не зарегистрировал обработчик, то он увидит не очень дружелюбную ошибку:

Страшное сообщение об ошибке

У варианта из 2014, если не получалось достать конфиг из айфрейма, можно было перенаправить на какой-то вариант по умолчанию, например, твиттер.

Но мне нравится ключевая идея: дать возможность лайкать и отвечать на своём сайте, не отходя от кассы. Так что я думаю сделать свой подход к этой проблеме.

Пока только обрисую детали, подробности расскажу на HWC.

  1. Кнопка ведёт на урл, общий для всех (как в случае с твиттером): например https://intent.marinintim.com/like?url=тут-лайкаемый-урл
  2. intent.marinintim.com смотрит в localStorage, если там сохранён урл личного сайта, идём на шаг 4
  3. intent.marinintim.com предлагает лайкнуть в твиттере/фейсбуке/етц или ввести свой урл
  4. intent.marinintim.com замещает свой фрейм нужным урлом
  5. Нужный урл принимает урл в виде параметра и происходит лайк

В этой схеме есть очевидная централизация (впрочем, intent.marinintim.com может быть не единственным интент-сервером), зато а) https, который поддерживается всеми браузерами, б) есть фолбэк, на случай если у читателя ещё нет своего сайта с микропабом.

Один из плюсов Индивеба-как-движения — если хочется попробовать что-то новое, то можно взять и сделать, не нужно ничьего разрешения, в сообществе принято «show, don't tell». Поэтому первый сырой прототип:

Лайкнуть


Кстати! Аарон сделал IndieWeb Events, который агрегирует события про Индивеб. Там есть и наша встреча. Приходи, даже если не получилось отправить RSVP.

Первый HWC

Провели первый Homebrew Website Club в Питере!

Было несколько человек: @mt (это я), @irkka, @fogrew, @Artem Shar, @Realetive, @iwanttobealight.

Пили пиво/коктейли, добавляли на сайты снежинки, обсуждали IndieAuth, OAuth, IndieMark.

Мне понравилось! Приятно было поговорить про это всё вживую, рад был увидеть всех.

Всё ещё кажется, что проще всего начинать с домена и статического HTML — тогда можно сразу начинать участвовать в движухе, а потом уже заменить на мощное React SSR application или ещё что-то другое.

Можно посмотреть фотографии на Индисобытиях.

У HWC есть интересная роль: он помогает сфокусироваться и всё-таки начать что-то делать: по результатам встречи появилось несколько новых личных сайтов (это радует!), опять же — всегда можно спросить у людей рядом, нормальна ли разметка, или подебажить вместе.

В следующем году будут ещё встречи, stay tuned!

Участие

Очень важно знать, что компьютеры изменчивы, и что происходящее можно изменять по своему желанию. В мире закрытых садов iOS и Андроида, проприетарных файловых форматов и ещё более закрытых API, в это сложно поверить, но компьютер может подчиняться пользователю, а не наоборот.

Для большинства людей Excel, наверно, — самое «программистское», с чем они сталкивались, и практически все с ним справляются. Никто не считает, что Excel это закрытый клуб, где на входе нужно будет выводить всё те же формулы на доске фломастером.

Мне кажется, что каждому человек стоит испытать этот момент, когда программа, которую только что написали, заработала. После этого компьютер уже не страшный чёрный ящик, а потенциальный помощник (не как в журнале «Юный Техник», а по-настоящему): может разгрести документы, обработать файлы, в целом, автоматизировать рутину.

Редкий человек станет профессиональным программистом (то есть, работать программистом всё время за деньги), но это и не нужно. Зато программистом-любителем, дилетантом, может стать каждый, и тем самым улучшить себе жизнь на порядок, понимая, что можно делегировать компьютеру.

(Тут был удалён длинный рант про то, что смартфоны усложняют дилетантское программирование, к сожалению, для программирования всё-таки нужен компьютер)

Также я вижу и Индивеб — не все начнут изобретать свои протоколы и новые микроформаты, но каждый может сделать себе сайт, который захочется. Я много раз видел восторг на лицах людей, когда у них получается сделать свою страничку в Интернете, который очень похож на восторг от первой программы.

Большими социальными сетями управляют профессионалы, а здесь получается Любительский веб, такой, как тебе захочется. Можно вообще сделать сайт для своей кошки.

Да, сайты Аарона Пареки и других давних участников Индивеба зачастую сложны и полны функциональности, но и функциональность, и сложность наращивались постепенно. Если хочется как-то измерять свой «левел», то можно ориентироваться на IndieMark. Но Индивеб — это всё-таки не просто набор технологий, это сообщество. Дальше я попробую описать, как в нём поучаствовать.

Если есть деньги на хостинг и навыки сделать HTML-страницу, то, конечно, стоит сделать свой сайт. Придумывать новый проект всегда легче, чем доделывать предыдущий, именно поэтому я так часто упоминаю «начать со статики, с одного index.html» — в идеале решение должно быть настолько простым, чтобы само сделалось, а дальше наращивать уже работающее.

Впрочем, начать можно даже не тут — можно взять Вордпресс, и добавить в него плагинов с микроформатами.

Не надо решать проблему, которая появится через год: её можно решить через год. Не надо решать проблему соседа — начни с себя, свои проблемы всегда знаешь лучше. (Например, можно получать вебменшены в виде пуш-уведомлений, но я, зная, как легко я на них отвлекаюсь, решил этого не делать)

Обратная связь всегда помогает, особенно в начале, — понять, правильно ли стоят микроформаты, отправился ли вебменшен, нормально ли, что отвалился IndieLogin (реальный случай со вчерашней встречи). Иногда хочется обсудить, как лучше сделать ту или иную штуку, узнать, как эту же проблему решали другие, или просто обсудить новости про децентрализацию Твиттера.

В англоязычном Индивеба для этого есть реалтайм-обсуждения в IRC (которые зеркалируются в Slack и Matrix). Не у всех хватает уровня английского, чтобы общаться в чатах на технические темы, да и IRC подходит не всем (плюс таймзоны — основная аудитория этих чатов живёт в Америке), поэтому я создал публичный чат в Телеграме: #indieweb-ru (альтернативная ссылка), в будущем попробую настроить бридж через Matrix и в IRC.

Иногда ещё лучше — встретиться вживую. Есть формат Homebrew Website Club, это что-то среднее между митапом и просто встречей поговорить за вебсайты. Другой формат встреч, которые проходят в англоязычном Индивебе — это Indie Web Camp, двухдневные воркшопы/хакатоны, где участники придумывают и тут же реализовывают новые индивебные штуки (вполне может оказаться, что идея на словах звучит хорошо, а на практике неудобно — хорошо это понять как можно быстрее!)

IWC в Петербурге я хочу попробовать провести в следующем году, по дате, думаю, про май/июнь (если хочется поучаствовать, то, конечно, пиши!)

Я много писал о технических решениях в этом календаре, от микроформатов до WebSub, — но все эти решения не появились из ниоткуда, их придумали и развивали участники сообщества (так, PuSH стал WebSub и научился в HTML). Далеко не все технические и социальные штуки ещё решены: например, в этом году многие участники экспериментировали с приватными постами. Здесь можно поучаствовать в формулировании, обсуждении, создании прототипов, тестировании.

Это не значит, что нужно приходить из ниоткуда с блокчейном, который решит все ваши проблемы, но всегда есть вариант взять какую-то проблему, решить её на своём сайте, убедить попробовать кого-то ещё, потом задокументировать на вики — там, глядишь, и станет новым Индивеб-стандартом. У сообщества акцент на сделать: лучше попробовать и потом улучшить вариант, решающий 80% проблемы, чем десять лет обсуждать по почтовым рассылкам потенциальное 100% решение.

Помимо же всех плюшек от того, что ты получаешь свой собственный сайт, участие в Индивеб-сообществе ощущается как сообщество. В чаты можно приходить и показывать, что получилось сделать; получить конструктивный фидбек и просто плюсик в карму всегда приятно.

Заходи в русскоязычный чатик, а если есть возможность — то подключайся и к международным чатам Индивеба. Индиреволюция только начинается.

Итоги

Месяц назад мне пришла в голову идея рассказать русскоязычному миру про Индивеб: сообщество, цель которого — вернуть себе контроль над тем, что мы постим в Сети. Я давно следил за сообществом, но не встречал материалов на русском языке.

Форматом для этого выбрал адвент-календарь, вдохновляясь, в том числе, 24ways: каждый день — новый пост.

Я не могу судить, получилось ли у меня оправдать ожидания, но для меня было важно поделиться не только техническими штуками, но и попробовать копнуть глубже, в «зачем» и философию, в принципы, ради которых и нужны эти технологии.

Теперь, под Рождество, пришло время подвести итоги. Сначала про числа:

  • 24 поста (включая этот);
  • 1 митап: HWC;
  • 666 переходов из твиттера по ссылке на первый пост;
  • 413 вебменшенов (подавляющее большинство из Твиттера через Bridgy;
  • 3 раза переделывал ленты: RSS, Atom, микроформаты (придётся в четвёртый);
  • написал порядка 16000 слов в 22 статьях (считал так: find ./*/ -name 'index.html' | grep -v feed | grep -v 16 | xargs -I% bash -c '~/pup <% ".h-entry" | w3m -T text/html' | wc -w );
  • писал посты в 8 редакторах: BB Edit, VS Code, Ulysses (айфон, айпад, макбук), Drafts, vim, самописной CMS (до переезда на сервер-коммуналку).

И не про числа:

  • самое любопытное, то есть, статистику по чтению, я не собирал — не хотелось встраивать внешнюю аналитику, а по акссес-логам ничего не понять;
  • получилось ни разу не упомянуть Ярушечку;
  • появилось несколько новых индисайтов! и слышал от нескольких людей, что они займутся своими сайтами в следующем году.

Так что, думаю, шалость удалась! Для меня же писать календарь было азартно и интересно, я и сам узнал больше про Индивеб, и структурировал свои мысли.

Пожалуй, главным оказалось писать. Появляется время и место для размышлений, и я много думал про природу веба и распределённых систем. Сам процесс написания текстов помогает лучше думать: пока мысли только в голове, то возникает ощущение, что всё понятно и стройно; только написав и перечитав, понимаешь, что не всё так однозначно, и видишь пробелы в своих же рассуждениях (а потом ещё раз — после публикации). В следующем году попробую больше и дольше писать ✍️

Не всё получилось описать хорошо; впрочем, если бы я пытался довести тексты до идеального состояния, то все бы они умерли в черновиках. Дедлайны сползали, но помогали. Я немного горжусь тем, что довёл до конца и не бросил где-нибудь на середине.

Конечно, не про всё успел рассказать (навскидку: IndieWeb Webring, хаб IndieWeb.xyz, /now-страницы, Vouch), и не всё успел поддержать у себя на сайте в том виде, как хочется. Сейчас, когда календарь закончился, то я смогу выдохнуть и спокойно приступить к поддержке Micropub для заметок и картинок.

При всей назойливости социальных сетей, оказалось, что они не очень эффективно распространяют информацию. Про то, что я пишу календарь, мало кто знал из знакомых до тех пор, пока не обсудил с ними вживую.

Понял ещё одну коварную штуку: про Индивеб, Веб в целом, и уж тем более про гипертекст вообще написано много, очень много. Это бесконечная кроличья нора, ведущая в сотни других нор, особенно если наловчиться с Wayback Machine. Чем больше я читал про гипертекст, тем больше убеждался, что «современный веб» это только тень возможного, безвкусный полуфабрикат. Я об этом редко вспоминаю, но WWW — не единственная гипертекстовая система (и Теда Нельсона и других пионеров несколько бесило, что взлетела именно она — это же ненастоящий гипертекст, ссылки в одну сторону). В следующем году хочу изучить гипертекст-вне-веба: собираюсь купить книгу Бернштейна и попробовать фикшен-гипертекст издательства Eastgate.

We now know that the future of serious writing lies on the computer’s screen, and that writing for the screen means writing with links. Economics, technology, and our dire circumstances converge to make hypertext essential to our future, if we are to enjoy a future. We know far too little about the craft of hypertext. Our planet is burning. Our electronic networks, one of the crowning glories of our civilization, are crammed with the ravings of fools and the counsels of con men. Many of our governments are controlled by senile knaves or irrational zealots. We can do better. Here are some steps. https://www.markbernstein.org/Sep19/Intertwingled.html

Ещё из книг для нового года: Manton Reece выпускает книгу Indie Microblogging, должно быть любопытно.


Надеюсь, что тебе понравился этот календарь, что в нём было что-то новое, любопытное, что заставило подумать. Если хочешь, то напиши обратную связь, мне всегда интересно узнать, что думают другие: можно на почту mt@marinintim.com, можно в #indieweb-ru@telegram, но лучше всего, конечно, написать у себя и прислать вебменшен.


Огромное спасибо @irkka за вычитывание и обратную связь, @phillennium за советы к «Прагматизму», всем, кто пришёл на HWC, всем, кто лайкал и писал, отдельно хочется выразить благодарность Андрею Ситнику, Вадиму Макееву и Андрею Романову за помощь с распространением. И, конечно, международному сообществу IndieWeb.

Осталось перевернуть календарь в хронологический порядок, чтобы проще было читать потом, и можно слезть с табуреточки, налить чаю, выключить пафос и включить Jingle Bells.

Fin.