Стендап Сьогодні

Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті

Підписатись на RSS · 📢 Канал в Telegram @stendap_sogodni

26.01.2025

SearXNG - локальний метарушій для пошуку в інтернеті

Встановив собі сьогодні SearXNG. Це такий вебзастосунок, який збирає результати пошуку з всіляких рушіїв та показує в типовому інтерфейсі. Щось на кшталт DuckDuckGo чи Kagi, тільки відкрите. Ось, при написанні цього поста тільки ним та й користувався.

В нього є готовий до використання образ Docker та ніяких обовʼязкових залежностей, тож випробувати дуже легко. Можна встановити його на сервер (та і є багато публічних серверів), але мене цікавив саме локальний запуск — так я більше впевнений, що його не заблокують. Ресурсів витрачає небагато (600 Мб памʼяті.) Відпрацьовує в межах пари секунд.

Мені, головним чином, потрібні налаштування пошуку під себе. В SearXNG можна: обирати набір рушіїв за замовчуванням та підіймати чи опускати домени у видачі.

Можна й створювати власні рушії; як повноцінно на Python, так і через конфігурацію (ось, спробував зробити для Прому). Що відрізняє SearXNG від того ж DDG - вибір рушія з !prom видає результати в інтерфейсі SearXNG, а для переходу безпосередньо на пошук на сайті потрібно писати !!prom.

А ще, теоретично, із запуском локально можна зробити рушій, який буде шукати в моїй базі Obsidian, та частково закрити питання згадування минулих досліджень - тобто щоб результати з Obsidian зʼявлялися прямо на сторінці пошуку.


25.01.2025

Терміни та пріоритети

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

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

Прочитав, що щоб уникнути часової сліпоти, терміни не просто необхідні — вони також повинні бути візуальними. Мало бачити дату — потрібно бачити перебіг часу до цієї дати. Цього мало бачив в різних програмах — максимум “залишилося N днів” (що вже краще, ніж тільки дата). У своєму застосунку я намагаюся ілюструвати це смужкою прогресу від дати створення проєкту до кінцевої дати. (Окрім того, досить важливо бачити, скільки вже зроблено та скільки залишилося зробити, але поки не придумав, як.)

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


24.01.2025

Чому кукі безпечніше?

🥠 Кукі були потрібні до епохи JavaScript, коли серверу потрібно було якось зберігати дані на клієнті. Звісно, зараз у нас на клієнті повноцінні застосунки, тож кому ті архаїчні кукі потрібні?

Втім, насправді в кукі вбудовано багато механізмів захисту, та не всі з них ми взагалі можемо відтворити. Головним чином, це атрибут HttpOnly - кукі з HttpOnly не доступні для клієнтського коду, їх наче немає. А значить, їх неможливо вкрасти ніякою інʼєкцією коду. Що, до речі, чи не найбільш реалістична атака на ваш сайт.

Окрім того, в кук є ще низка атрибутів, яка допомагає захистити їх від помилок - CSRF, пересилання без шифрування тощо. Одним словом, безпека кук — це функція браузера, та нам не потрібно (та не варто) придумувати власні альтернативи.

(Примітка: вкрасти куки через злам браузера або локальний доступ до нього — звісно, можливо. Тільки це не так ймовірно, як інʼєкція.)

До речі, fetch() підтримує кукі, причому опціонально навіть якщо API не на тому ж домені. Єдине, що хтось з того домену повинен куку встановити. На відміну від саморобних токенів, кукі також надсилаються для зображень та інших ресурсів.

По змісту в кук немає різниці з будь-якими іншими токенами — туди можна записати будь-що. Але для автентифікації можна виділити два варіанти: або це випадковий токен, що вказує на стан на сервері, або токен зі змістом.

Мені подобається зберігати стан на сервері, бо це робить кукі повністю непрозорими та не потребує складних механізмів приховування та верифікації змісту. Та й розмір стану на сервері практично не обмежений. (Ось відповідь на Stack Exchange, де про це розповідає.)


23.01.2025

Комбінації клавіш під себе на macOS

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

Наприклад. Коли працюю з проєктами для iOS, а редагую в VSCode, то в мене є три вікна: VSCode, XCode, та симулятор. Щоб запустити збірку, доводиться переходити в XCode та натискати Cmd+R. Особливою образою є те, що в симуляторі Cmd+R починає записувати відео, що мені ніколи не було потрібно.

Ну то й що? Зробив за допомогою BetterTouchTool, щоб як в симуляторі, так і у VSCode - якщо назва вікна містить назву мого проєкту — комбінація Cmd+R перемикалася б спочатку на вікно XCode. Тепер більше не потрібно думати, в якому з вікон я знаходжуся. Якщо піти далі, для вебпроєкту Cmd+R може так само перемикати на вікно браузера, або запускати тести.

Або інший приклад - Cmd+P/Cmd+Shift+P для навігації по файлах та командах, до якої звик ще з Sublime Text. Тут допомагає Paletro та локальні налаштування застосунків. (Бо інакше Cmd+P зазвичай починає друк — майже атавістичну нині операцію.)

BetterTouchTool зручний тим, що відразу працює в контексті конкретного застосунку, а більшість комбінацій як раз не є глобальними. Та й можливістю задати складу поведінку. А ще в macOS можна дуже багато всього призначити стандартними засобами, в Settings -> Keyboard -> Keyboard shortcuts.


22.01.2025

Непорозуміння про JWT

JSON Web Token припинили бути модною технологією ще, мабуть, років 5 тому, але тепер вони стали чимсь таким звичним, що наче має сенс для автентифікації. Зокрема, вони “вигідно” відрізняються від просто випадкового ключа сесії тим, що всередину можна запхати якісь дані, та ще і якась криптографія присутня, тож це ж, мабуть, ще й безпечніше?

Ні. Головне, що треба усвідомити - JWT не є зашифрованим, а тільки підписаним! Який ти алгоритм не обирай для створення JWT, це впливає тільки на зміст підпису. Дані ж лише закодовані в Base64 - певно, щоб полегшити транспортування. (Власне, JWT складається з трьох рядків у Base64, зʼєднаних крапками: заголовок, зміст, підпис.)

Тому у JWT не можна класти ніякі секретні дані. Можна думати про JWT як квиток на концерт: на ньому зазначене твоє місце, а також є печатка каси. На концерті білетер — вже без каси! - може перевірити печатку та пропустити тебе в зал. В цьому і є весь зміст існування JSON Web Token.

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

Практично обовʼязково JWT повинний мати термін дійсності, бо він є самодостатнім носієм інформації, та його неможливо відкликати (а якщо технічно можливо, то ймовірно, JWT в такій архітектурі зайві.)

Що краще взяти замість JWT? На мою думку, кукі з ключем сесії та HttpOnly все ще ідеальний варіант, якщо ми можемо його собі дозволити.


21.01.2025

Tabby - доповнення коду на основі LLM, але локально

…Я вже багато років користуюся Tab9 для доповнення коду. В мене вимоги прості: хочу закінчення рядків. А ні, ще одна вимога: я хочу, щоб модель працювала локально. На жаль, як буває з багатьма продуктами, TabNine пішов в бік хмари та корпоративних клієнтів.Та локальне доповнення хоч і працює, але без кінця просить залогуватись кудись… в той час, коли в мене “дідівська” ліцензія ще з тої пори, коли ніяких облікових записів не було.

Отже, нещодавно натрапив на Tabby. Це обгортка для запуску великих мовних моделей та інтеграції в редактор (в моєму випадку - VSCode.) Хороша — можна запускати не тільки локально, а й будь-де, налаштувати декілька користувачів та обирати ту модель, яку заманеться. Рекомендую, якщо вам таке цікаво.

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

На M2 Macbook Air навіть 1B-моделі відпрацьовують дуже повільно, більше ніж десять секунд. Замислився — а як же ж тоді TabNine? Виявилося, що ті використовують 400M-моделі, але наче по моделі на кожну мову програмування. Та TabNine був швидким навіть на макбуці з 2015-го.

Але в мене були інші плани: поки я працюю, стоїть без діла ігровий компʼютер — чом би його не долучити? З Tabby це надзвичайно просто — вся різниця буде в адресі сервера. В мене наразі в наявності NVIDIA 4060M; на ній 1B-модель відпрацьовує в межах секунди, а 3B-модель десь трьох. Більше немає сенсу дивитися, бо 3 секунди вже занадто для моїх потреб — якщо доповнення не миттєве, швидше самому написати. Здивувало, що для цієї потреби я упираюся не в памʼять, а в обчислювальні потужності.

Ось такі поки результати.


20.01.2025

Продукт та маркетинг - це різні речі

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

Коли продукт поганий, а маркетинг гарний — це доволі типова ситуація. Типова тому, що маркетинг — це наше сприйняття, тож більшість того, про що ми взагалі знаємо, ми знаємо через гарний маркетинг. Втім, гарний маркетинг ще нічого не каже про якість продукту… знаєте, інколи маркетинг робиться взагалі до продукту, щоб оцінити попит. Є ще один знайомий випадок: продукт чудовий, а маркетингу немає. Це те, що називається “приховані скарби”.

Проте, нарешті, трапляється, що при гарному продукті маркетинг зроблений жахливо. Від цього особливо прикро, бо поганий маркетинг працює в мінус. От, наприклад, розробили люди цікавий, якісний гаджет — а потім йому зробили крамницю з усіма чорними практиками накрутки — всі ці “залишилось 09:59 до кінця акція”, “тільки що забрали передостанній товар” тощо. Або коли хтось продає дійсно гарну книгу чи курс оцією шаблонною сторінкою з відгуками та “тільки зараз отримайте бонусні матеріали безплатно”.

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


19.01.2025

Потоковий запис JSON

Я в попередньому пості писав про читання JSON поступово з вивільненням памʼяті, але ж хтось той JSON повинен був записати? Ситуація з записом значно простіша, та не потребує ніяких особливих інструментів.

Почнемо з того, що генерація JSON можлива в один прохід та може здійснюватись у вихідний потік. Можна навіть поєднати її зі стисканням в gzip та пересилати через мережу з мінімальним буфером для виходу.

(До речі: gzip стискає зміст через заміну повторюваних послідовностей. Таким чином, назви атрибутів та інші повторювані рядки в JSON - ідеальні вхідні дані для gzip.)

Але — інколи в нас немає всіх даних відразу — наприклад, вони не влазять в памʼять, або ми отримуємо їх пакетами з Kafka чи ще звідкілясь. Навіть в такому випадку нескладно утворити на виході єдиний JSON без залучення великого буфера.

Наприклад. Пишемо у вихід [ - початок масиву. Далі пишемо по черзі стільки даних, скільки потрібно — читаючи з бази або з генератора або де там ще в нас дані беруться. Наприкінці закриваємо масив: ]. От і все!

Інколи все, що потрібно — це трохи текстових операцій. Я колись так робив, щоб зливати з Kafka зміст для резервного копіювання.


18.01.2025

Потокова обробка JSON

Поки писав вчора про JSONLines, встиг дізнатися про альтернативу. Звичайний JSON теж можна обробляти потоком. Я маю на увазі, зчитувати частину, обробляти та вивільняти — звісно, коли йдеться про якийсь масив незалежних даних.

Адже ще одна перевага JSON - це його контекстно-незалежна рекурсивність (сам тільки що придумав). Вкладені обʼєкти синтаксично еквівалентні цілому документу. Нам не потрібно знати нічого про відступи, області імен, якісь посилання тощо. Якщо ми зчитуємо з потоку JSON та встановили вказівник в позицію початку вкладеного значення, то парсер буде готовий взяти та зчитати це значення, наче окрім нього нічого не існує. Залишиться тільки просувати вказівник до наступного значення, тобто повз кому.

Я все це не вигадав, а прочитав у прикладі до методу json.Decoder.Decode() з Go. Бо в Go декодер JSON відкриває достатньо інтерфейсу для поміркованого читання; можна читати цілий документ, а можна значення за значенням. В цілому, так можна зробити з будь-яким парсером, що читає потік — але ж пропускати коми та пробіли доведеться вручну.

Цікаво, що в JavaScript взагалі не бачу рідного модуля для читання JSON з потоку, а тільки з рядка. Знайшов ось stream-json, яка щось таке вміє. (А от JSONLines можна легко читати з потоку через модуль readline та обробляти рядок за рядком.)

Як бачимо, з одного боку, витончений читач може реалізувати потокову обробку й без обтяження споживача незвичним форматом даних. Але з іншого боку, JSONLines гарний тим, що там один документ на рядок, та працювати з ним можна звичайними текстовими засобами (наприклад, wc -l, щоб підрахувати кількість документів.)


17.01.2025

Формат JSONLines

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

Перед тим, як обирати з форматів для Big Data, краще подивитися на JSON Lines. Це формат масиву даних, де в кожному рядку міститься повний документ JSON. (Звісно, при цьому розриви рядка в середині документа заборонені.)

Перевага JSONLines: кожен рядок можна читати та обробляти окремо (та вивільняти памʼять). Також можна, навіть без розбору змісту JSON, орієнтуватися в масиві — достатньо стежити за закінченнями рядка, щоб визначити місце, полічити записи, розділити масив (чи склеїти два!) тощо.

Недолік JSONLines: він все ж не такий загально прийнятний, як JSON. Також людям його читати важче. Ну й те, що JSONLines - обовʼязково масив. Тому сфера застосування — скоріше, машинні документи, чи запити до API. Я бачив JSONLines в пакетних запитах ElasticSearch, експортах AWS Redshift, AWS Kinesis Firehose… вебхуках Mailtrap. :) Раджу мати на озброєнні.

Ще дотепний факт: насправді є цілих два стандарти для JSON Lines - другий називається NDJson та функціонально ідентичний, за винятком медіатипу.