/

viche.social

Bluesky, Mastodon і Nostr в одній стрічці — клієнтський застосунок, де ваші ключі та акаунти лишаються на вашому пристрої.

blueskymastodonnostrpwaatprotoactivitypubastroreact

Viche — Bluesky, Mastodon і Nostr в одній стрічці
Viche — Bluesky, Mastodon і Nostr в одній стрічці

У мене є акаунти в Bluesky, Mastodon і Nostr, а люди, яких я справді хочу читати, розкидані по всіх трьох. Тож я тримаю три застосунки, заходжу в них по черзі — і все одно щось пропускаю, бо стрічки, яка мені потрібна, немає в жодному з них.

Viche — це та сама стрічка. Працює в браузері, тримає акаунти всіх трьох мереж одночасно й дає читати їх разом або окремо. Назва — від українського віче, давніх народних зборів: місця, де сходилися, щоб вирішувати спільно.

Встановлювати нічого: viche.app — це застосунок, viche.social — сайт і документація.

Навіщо це існує

Три мережі розв’язують ті самі задачі несумісними способами, і кожна хоче бути тією єдиною, яку ви оберете. Обрати одну означає втратити зв’язок із тими, хто обрав інакше, тож більшість із нас тримає всі три й зводить їх докупи в голові.

Саме це зведення і є продуктом. А робити його треба в клієнті, а не в новому протоколі чи на новому сервері: браузер уже вміє WebSocket до релеїв Nostr, HTTP до інстансів Mastodon і OAuth до PDS у Bluesky. Посередині нічого не мусить стояти — тому нічого й не стоїть.

Чим відрізняється

ПідхідЩо для нього головне
Vicheбагато акаунтів — в одній стрічці або окремо; ключі лишаються на пристрої
Клієнт однієї мережіглибина: усі можливості своєї мережі, і раніше за інших
Планувальники кроспостингурозіслати той самий допис усюди; читання — не їхня задача
Три застосунки поручнічого, зате працює вже зараз

Якщо ви живете переважно в одній мережі — беріть клієнт самої мережі. Окремий застосунок для Bluesky чи Mastodon завжди матиме нові можливості своєї мережі раніше, бо стежить лише за нею. Viche — для іншого випадку: коли увага справді розділена між мережами, і саме це варто полагодити.

Що всередині

Один словник, три адаптери

@viche/core — це типи й статичні дані: Network, Post, Session, NetworkAdapter і дескриптор на кожну мережу. Кожен адаптер реалізує цей інтерфейс для свого протоколу, і більше ніщо в застосунку не імпортує протокольну бібліотеку напряму. Адаптери підвантажуються через import(), коли акаунт цього потребує, тож пул релеїв і клієнт AT Protocol не потрапляють у збірку, доки ви не увійдете.

Можливості, а не умови

Дескриптор несе набір ключів можливостей — read-timeline, write-post, read-poll. Інтерфейс перевіряє належність до набору, тож додати мережу означає додати дескриптор і адаптер, а не перечитувати кожен компонент у пошуках нової гілки.

Ці ж набори формують публічну матрицю можливостей — тому вона й не може розійтися з кодом: сторінка питає дескриптор, а не список, який хтось веде руками. У кожної клітинки три стани: зроблено, буде, або протокол так не вміє. Третій — найцінніший. «Ми цього ще не зробили» і «ця мережа так не вміє» — різні речі, а коли їх зводять в одну порожню клітинку, таблиця починає розходитись з реальністю.

Одна стрічка — або кілька

Об’єднання — це налаштування, а не форма застосунку. Об’єднати стрічки в налаштуваннях має три режими: Усі зводить в одну стрічку кожен доданий акаунт, Обрані — лише ті, які ви позначили, а Жодної вимикає об’єднання: ви читаєте по одному акаунту й перемикаєтеся між ними.

Джерела — це акаунти, а не мережі, і кількість їх нічим не обмежена. Два входи в Mastodon і три в Nostr — це п’ять джерел, і ніщо не виділяє, з якого протоколу кожне з них: той самий протокол двічі нічим не відрізняється від двох протоколів по разу. Тож вибір насправді не про «об’єднати мої мережі», а про «які саме мої акаунти я зараз читаю» — а це те питання, яке в мене справді виникає.

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

Ключі, які нікуди не йдуть

Nostr підписує через розширення NIP-07 або віддалений підписувач, Bluesky використовує OAuth із DPoP, Mastodon — OAuth конкретного інстансу. Нічого з цього не потрапляє на мій сервер, бо його немає: застосунок — це статичні файли на CDN.

Нотатки щодо рішень

Зводити докупи має клієнт. Кожна серверна спроба зробити це перетворюється на ще один акаунт, якому доводиться довіряти ключі від решти трьох. У браузері облікові дані не залишають пристрій, і чесне твердження про приватність — що я не можу прочитати вашу стрічку — стає властивістю архітектури, а не обіцянкою, в яку вас просять повірити.

«Тільки статика» обмежує мене, а не застосунок. Жодного мого бекенду на шляху запиту немає, але браузер усе одно звертається до релеїв, AppView і API інстансів напряму. Цю різницю легко загубити: обмеження в тому, щоб не тримати власний індексатор, а не в тому, щоб не звертатися до чужих сервісів. Популярні теги, наприклад, рахуються з уже завантаженої стрічки, а не з індексу, який довелося б хостити.

Лише wss://, і ваш ключ ідентичності ніде не зберігається. Незашифровані адреси релеїв не позначаються попередженням, а відхиляються там, де адреса нормалізується, — і міграція вичищає ті, що збереглися раніше. Віддалений підпис зберігає одноразову пару ключів на сесію замість вашого ключа ідентичності: того, чий витік був би найболючішим, застосунок не зберігає взагалі.

Сховище передають, а не вшивають. Ніщо в шарі стану не звертається до браузерного сховища саме по собі — йому кажуть, куди писати, а що саме це буде, вирішує той, хто запускає застосунок. У вебі це localStorage, у тестах — мапа в пам’яті, нативна збірка передала б своє. Сьогодні працює localStorage, бо це найпростіше з наявного, а не тому, що на ньому щось побудовано; замінити його — це передати інше, а не переписувати застосунок.

Статус

Працює на viche.app. Три мережі — читати об’єднано або по одному акаунту; вхід, зокрема через віддалений підписувач для Nostr; дописи з відповідями, цитатами, згадками, тегами та завантаженням зображень; прямі повідомлення; сповіщення; підписки, глушіння й блокування; редагування профілю в усіх трьох мережах; розділ Explore із людьми, тегами й популярними дописами.

Відкритий код — це план, а не поточний стан. Є питання, які я хочу закрити добре, а не швидко.

Деталі

Автор
Dmytro Klymenko
Зроблено на
Astro — статична оболонка, один React-острівець, HeroUI — бібліотека компонентів, nostr-tools — релеї та підписи Nostr, AT Protocol — OAuth та лексикони Bluesky, TanStack Query — кеш віддалених даних, Lexical — редактор допису, countries-list — дані локалей
Початок
Оновлено

Інші проєкти