kolekcja myśli, esejów i notatek serwisowych

myśli (19)

2026 7
28 wrz 2026

Podróż do Czarnogóry — wskazówki

We wrześniu 2026 polecieliśmy tanimi liniami do Czarnogóry na cztery dni. Dwa z nich zeszły nam głównie na transport (szczegóły niżej), więc na miejscu tak naprawdę zostały dwa dni, z czego jeden deszczowy – trochę mało, ale i tak było warto.

Czarnogóra to dziwne połączenie naszego PRL-u z cosplayem Unii Europejskiej: płaci się w euro (choć w Unii nie jest – euro używa jednostronnie), ale pociągi są jak za dawnych lat w Polsce. Do tego góry, które faktycznie bywają czarne1, i spektakularne widoki.

Szeroka dolina z domami i polami, za nią ciemne pasma gór z chmurami zawieszonymi na zboczach pod zachmurzonym niebem – widok przez szybę autobusu
Dowód rzeczowy: góry faktycznie bywają czarne

Pogoda i ubrania

Prognozy pogody nie kłamią – jeśli trzy dni do przodu jest dzień z deszczem, to faktycznie będzie dzień z deszczem2.

Logistyka ubraniowa jest ciężka: w Polsce we wrześniu jest po 12 stopni i trzeba mieć kurtkę, a w Czarnogórze – dwadzieścia parę.

Przy pakowaniu sprawdziły się dwie rzeczy. Siatki do kompresji ubrań pozwalają zmieścić ciuchy na dwie pogody w plecaku pod siedzenie. Z kolei wielorazowa torba na zakupy przydaje się w transporcie jako dodatkowy bagaż, przy zakupach na miejscu była bezcenna, a po złożeniu da się ją wcisnąć do plecaka.

Transport

Bilety

Aktualnie działające strony z biletami:

Ceny (stan na wrzesień 2026) są niskie: z lotniska do Podgoricy pociąg kosztuje w okolicach 1,2 €, autobus na tej samej trasie jest trochę droższy – 3–5 € za osobę. Na każdym dworcu autobusowym dochodzi jednak opłata dworcowa – 1,5 € (na stacjach kolejowych jej nie ma).

FlixBus ma w Czarnogórze część własnych linii, ale nasze bilety powrotne z Kotoru do Podgoricy nie były na jego autobus – kupowaliśmy je online na stronie FlixBusa, który był tylko pośrednikiem przewoźnika. Niewiele to dało, bo i tak na miejscu trzeba było wymienić bilet elektroniczny na lokalny, żeby dopłacić za dworzec.

Z lotniska do Podgoricy

Przejazd pociągiem z lotniska do Podgoricy był ciekawy – przejechać się tak starym pociągiem w 2026 roku to wspomnienie samo w sobie.

Kilka rzeczy, o których warto wiedzieć:

  • Stacja kolejowa przy lotnisku (Aerodrom) jest spory kawałek od terminalu – ok. 20 minut pieszo.
  • Stacja nie jest oświetlona – po zmroku bywa tam nieprzyjemnie.
  • Google Maps prowadzi okrężną drogą. Można dojść szybciej, skręcając wcześniej między domki – Google Maps tej drogi nie pokazuje, bo trzeba przejść przez tory3. Organic Maps ftw.
  • Nie ma strony z opóźnieniami pociągów – trzeba czekać albo dzwonić na dworzec, żeby czegokolwiek się dowiedzieć. Względnie dobrą heurystyką jest liczba osób na peronie – jeśli ludzie wciąż czekają, pociąg pewnie jest opóźniony, a nie odjechał.

Te 20 minut sprawdziłem osobiście – dostałem questa, żeby zweryfikować, jak szybko da się tam dojść. Po drodze zauważyłem dwie emerytki, które walczyły z ustaleniem, w którą stronę iść: z Google Maps nie umiały korzystać, a jedyne, co zostawiła im córka, to mapka z drogą na stację. Zwykle mam spore opory przed zagadywaniem do obcych ludzi, ale tym razem współczucie nie pozwoliło mi ich zostawić – udało się je doprowadzić na miejsce.

W drodze powrotnej pociąg na lotnisko nie pasował nam czasowo, więc wracaliśmy autobusem – jednym z tych ze strony busticket4.me.

Do Kotoru

Kotor jest daleko od lotniska w Podgoricy i wymaga przesiadki: pociągiem do Podgoricy, stamtąd autobusem. Bliżej Kotoru jest lotnisko w Tivacie, ale wygląda bardzo lokalnie i nie trafiło nam się w wyszukiwaniu lotów. Żeby dotrzeć do Kotoru, musieliśmy przenocować w Podgoricy – przy późnym przylocie tego samego dnia nie zdążylibyśmy na autobus.

Trasa lubi się korkować: autobus, który według rozkładu jechał 3 godziny, u nas jechał 4.

Bilety na autobusy z Kotoru prawdopodobnie nie mają przydziału miejsc – liczy się kolejność przy bramce, w której wsiada się do autobusu.

Taksówki

Taksówka z lotniska do Podgoricy to ok. 20 €. Tyle podaje internet i sami sprawdziliśmy, że na miejscu ceny są podobne – również w aplikacjach.

Po samej Podgoricy aplikacja TeslaGoApp uratowała nam życie w deszczu – z noclegu na dworzec zapłaciliśmy 2 € za parę kilometrów, podczas gdy inne aplikacje potrafiły pokazać i 6 €. To lokalna korporacja taksówkowa z samymi elektrycznymi Teslami – działa w Podgoricy, poza stolicą raczej się nie przyda.

Noclegi

Spaliśmy w Podgoricy i w Kotorze, noclegi z Bookinga: ok. 50 € za dwie osoby za noc w Podgoricy, w Kotorze ok. 60 €.

Wjeżdżaliśmy na paszportach – i fajnie jest dostać pieczątkę do nowego paszportu4. Wynajmujący w Kotorze robił im zdjęcia. Poradniki sporo piszą o obowiązku rejestracji pobytu – u nas po prostu zajął się tym właściciel noclegu, sami nie musieliśmy nic załatwiać5.

Na miejscu

W wielu miejscach nie można płacić kartą – zdarzało się to w lokalach gastronomicznych, a za shishę płaciliśmy wyłącznie gotówką. Mieliśmy euro z polskich kantorów i nam wystarczyło, a tam, gdzie karta przechodziła, płaciliśmy Revolutem.

Używałem eSIM-a od BNESIM – 5 GB na dwie osoby i cztery dni wystarczyło6. Ofertę wyszukało mi AI i wyszła trochę taniej niż Airalo czy eSIM w Revolucie. Brałem ją na sapera, ale finalnie było warto.

Znajomość angielskiego wśród Czarnogórców jest dobra – dla porównania w Hiszpanii w lokalach czy muzeach rzadko trafiałem na kogoś, kto mówi po angielsku, a dyktowanie przez Google Translate bywa bardzo zawodne.

Co zobaczyć

  • Kotor – daleko, ale dla tych monumentalnych gór warto. Byliśmy głównie na Starym Mieście i na plaży.
  • Bar – nie byliśmy, więc nie polecam ani nie odradzam. Jeśli ktoś się waha, gdzie jechać: Bar jest skomunikowany kolejowo, więc dojazd powinien być prostszy niż do Kotoru.

Zatoka Kotorska o zmierzchu: nad światłami miasta wznoszą się skaliste góry, po zboczu pną się oświetlone mury twierdzy
Kotor wieczorem – mury twierdzy nad zatoką

Gdzie zjeść

W Kotorze – grill Tanjga. Widząc w menu mieszane mięsa, zamawialiśmy z pewną obawą – po doświadczeniach z Albanii7 spodziewaliśmy się mózgu, policzków albo ozorków. Niepotrzebnie: mięso mieszane jest tu dość bezpieczne – karkówka, kiełbasy, ćevapi. Frytki też są w porządku.

Największe zagrożenie czyha jednak w sklepie. Kajmak z supermarketu to nie jest kajmak, tylko nieślubne dziecko sera i twarogu. Pełne soli.

Opakowanie „a la Kajmak” firmy Mlekara Šabac na talerzu na stole na tarasie
a la kajmak – ani ala, ani kajmak, ratunku

Footnotes

  1. Wiedza zweryfikowana na miejscu. W planach miałem żartobliwą relację na Instagramie, która miała obalić ten mit – byłem pewien, że góry wcale nie będą czarne. Były. ↩

  2. Na pewnym etapie życia śledziłem burze i stąd wiem, na ile prognozom da się ufać – ta wiedza nieoczekiwanie przydaje się na wyjazdach. Trafność spada z każdym dniem wyprzedzenia: według NOAA prognoza na 5 dni sprawdza się w ok. 90% przypadków, a na 10 dni – mniej więcej co drugi raz. ↩

  3. Czy to legalne? Na linii, gdzie dworcem jest ceglana buda, a pociąg przejeżdża parę razy dziennie, to chyba nie jest najważniejsze pytanie. ↩

  4. Po trzydziestce to prawie jak zbieranie achievementów. ↩

  5. Właściciel noclegu w Podgoricy był z kolei prawnikiem – i o paszporty ani rejestrację pobytu w ogóle nie pytał. Chyba dużo to mówi o prawnikach. ↩

  6. Dla kontrastu: za 5 MB transferu w Albanii, który iPhone przypadkiem włączył mi na karcie nju, zapłaciłem 200 zł. ↩

  7. W Albanii w takich zestawach można uświadczyć mózg i podobne atrakcje. ↩

28 wrz 2026

ADHD buying guide: plecak lotniczy w Olsztynie

Uwielbiam robić zakupy na ostatnią chwilę – nic tak nie daje strzału dopaminy jak zwiedzanie całego miasta w poszukiwaniu plecaka na wylot1.

Poszukiwany, poszukiwana

Plecak z kategorii underseater pod lowcosty już mam, ale chodziło za mną coś, co pasuje też do biura i wspiera koncept posiadania laptopa na zagranicznych wojażach.

Decathlon na Jarotach

Podróż po Olsztynie zacząłem od Decathlonu na Jarotach, który ma fajne modele – ale osobiście szukałem czegoś bardziej biznesowego2.

Galeria Warmińska i okolice

W MediaMarkcie na Sikorskiego, tuż obok Galerii Warmińskiej, wpadły mi w oko plecaki Samsonite – co prawda bardzo podstawowy model, ale gdyby ktoś szukał, wspominam jako inspirację. Wystawa x-komu nie porywała, RTV Euro AGD też nie miał fajnych plecaków3.

Aura

Ochnik miał w ofercie plecaki, które były gigantycznie małe, nie wyglądały źle, ale czułem, że ciężko byłoby w nich zagospodarować miejsce. Wittchen natomiast wyglądał… tanio. Nie wiem, czy to tylko moja percepcja, czy naprawdę jakość materiału pozostawiała wiele do życzenia.

Finał: Galux w Manhattanie

Gdzie finalnie udało mi się kupić plecak? W Galuxie, w Manhattanie. Padło na model firmy Roncato za 420 zł – kupił mnie nietrywialnym wyglądem.

Czy polecam tę metodę? Nie. Czy przy następnym wyjeździe zrobię dokładnie tak samo? Oczywiście.

Footnotes

  1. Nie żebym wcześniej przez kilka dni nie przeglądał strony Samsonite i pół internetu – z pełnym paraliżem decyzyjnym. Ograniczony czas i pula plecaków ułatwiają decyzję. ↩

  2. Z drugiej strony zastanawiam się, czy dopłacanie za biznesowy wygląd miało sens, skoro już po pierwszym wyjeździe musiałem prać kosmetyczkę, bo wyciekł w niej żel. Może trzeba było wziąć coś, co bez żalu można zaorać w podróży. ↩

  3. Próbuję sobie przypomnieć, jakie, ale na pewno nie mieli w ofercie czegoś dedykowanego do samolotów. ↩

14 wrz 2026

Od lenistwa przy nauce n8n do stress-testów aplikacji w Zigu

Kontekst

Zaczęło się od potrzeby: przelotka powiadomień z n8n na Coolify do ntfy1. Problem - nie umiałem sensownie zapisać workflow n8n do gita (i nie chciało mi się poświęcać na to więcej czasu), więc zamiast dalej z tym walczyć, napisałem to sam, od zera, w Zigu2.

I tak powstał ntfy.zig - mały relay: przyjmuje webhooki (póki co Coolify i GitHub Actions/Deployments), przerabia je na tytuł/wiadomość/priorytet i forwarduje do ntfy.

Relację z vibe-codingiem na przestrzeni miesięcy można streścić do typowej adnotacji pod klientem llm, “korzystaj, ale sprawdzaj” - stąd też fajnie było wykorzystać doświadczenie testerskie i zrobić stress testy Locustem.

Setup

Jeden katalog stress/ w repo: fake endpoint ntfy symulujący opóźnienia/fail rate/timeouty, Locust mieszający ruch z obu kanałów webhooków (Coolify + GitHub, z celowo złym podpisem HMAC - sekretem, którym GitHub podpisuje payload webhooka, żeby odbiorca mógł zweryfikować, że request faktycznie pochodzi od niego - do testowania odrzucenia 401) i skrypt spinający całość, wszystko w jednym środowisku uv na free-threaded Pythonie 3.14t. Pełne techniczne detale, w tym dlaczego free-threaded akurat tu ma sens, są w PR #14.

Pierwszy przebieg - coś jest nie tak

Przy 20 użytkownikach wszystko ładnie: mediana 5-6ms. Podbiłem do 300 użytkowników żeby znaleźć sufit i dostałem:

OSError(49, "Can't assign requested address")

Oho, problemy. OSError 49 to wyczerpanie puli efemerycznych portów - i to zanim CPU zdążyło coś poczuć.

Przyczyna i fix

Powód: każdy pojedynczy forward do ntfy tworzył nowego klienta HTTP od zera i od razu go zamykał. Czyli nowe połączenie TCP (a dla https - nowy handshake TLS) na każdy webhook. Zero connection poolingu3 - stąd tak szybkie wyczerpanie portów.

Fix: jeden http.Client budowany raz przy starcie i współdzielony między wszystkimi requestami4, zamiast tworzenia nowego na każdy forward. Połączenia TCP zaczynają się zatem reużywać, a nie mnożyć w nieskończoność.

Weryfikacja

150 użytkowników, ~550 req/s. lsof na procesie ntfy.zig pokazał 11 stałych połączeń do fake ntfy zamiast jednego nowego na request. Zero błędów wyczerpania portów.

Metryka Przed Po
Połączeń TCP przy 150 VU / ~550 req/s ~1 na request ~11 stałych
Throughput (lokalnie) ~70 req/s (wait_time-bound) ~550 req/s

Wniosek

Stress test nie musiał być wyszukany, żeby złapać realny bug - wystarczyło podbić liczbę użytkowników wystarczająco wysoko, żeby zderzyć się z limitem portów zamiast z limitem CPU. Dziwny to zresztą świat, w którym można napisać i przetestować pod obciążeniem aplikację w języku, którego się w ogóle nie zna5.

Przy okazji: cała aplikacja w spoczynku zajmuje około 2MB RAM-u - więc obok tego, że działa poprawnie, jest też całkiem sympatyczna dla zasobów.

Footnotes

  1. Sympatyczny i dość minimalistyczny selfhosted serwer do powiadomień push. ↩

  2. Tak w skrócie wygląda moje podejście do życia. W wielu aspektach. ↩

  3. Mechanizm, w którym raz otwarte połączenie zostaje utrzymane i reużywane dla kolejnych requestów, zamiast być zamykane i otwierane od nowa za każdym razem. ↩

  4. context7 często ma nieaktualną dokumentację dla tak egzotycznych bibliotek jak świeża stdlib Ziga - przez co Sonnet 5, pracując nad nowym std.http, sympatycznie nawigował wprost po kodzie źródłowym biblioteki, zamiast polegać na (nieaktualnej) dokumentacji. ↩

  5. I model najwyraźniej też, ale on dla odmiany ma znacznie więcej cierpliwości do czytania kodu bibliotek. ↩

13 wrz 2026

Jak zalogować się do Grafany przez Authelię jako admin?

Ostatnio hobbystycznie wracam do zabawy w selfhosting — jeden z tych okresowych nawrotów, gdzie na nowo można rozwiązać stare problemy, testując różne podejścia do rzeczy, które kiedyś już “rozwiązałem”1. Tym razem przyszła kolej na monitoring.

Zdziwiło mnie, że zaraz po skonfigurowaniu OIDC nie mogłem dodać żadnego dashboardu w Grafanie — logowanie działało, a mimo to brakowało uprawnień.

Setup

Grafana stała już za Authelią (self-hosted SSO, jeden użytkownik, w grupie admins) przez natywne OIDC2 (auth.generic_oauth). Zadanie wydawało się trywialne: każdy z grupy admins ma dostawać rolę GrafanaAdmin w Grafanie automatycznie przy logowaniu, zamiast nadać ręcznie w UI — i tak zostałoby nadpisane przy każdym logowaniu, bo allow_sign_up: true re-provisionuje usera za każdym razem.

Grafana

Standardowa, udokumentowana ścieżka: scope groups na kliencie OIDC + role_attribute_path (wyrażenie JMESPath3) po stronie Grafany.

Grafanę stawiam przez Ansible, rolą grafana.grafana.grafana. Najważniejszy fragment configu to zmienna grafana_ini_oauth:

grafana_ini_oauth:
  "auth.generic_oauth":
    enabled: true
    name: Authelia
    client_id: grafana
    client_secret: "{{ grafana_authelia_oidc_secret | default('') }}"
    scopes: "openid profile email groups"
    auth_url: "https://authelia.example.com/api/oidc/authorization"
    token_url: "https://authelia.example.com/api/oidc/token"
    api_url: "https://authelia.example.com/api/oidc/userinfo"
    use_pkce: true
    allow_sign_up: true
    role_attribute_path: "contains(groups[*], 'admins') && 'GrafanaAdmin' || 'Viewer'"

Admin user jest jedynym członkiem grupy admins w Authelii — stąd contains(groups[*], 'admins') w wyrażeniu wyżej. Warunek konieczny, żeby to zadziałało: scope groups musi być dodany do klienta OIDC Grafany, tak jak w configu powyżej.

Co miało się wydarzyć

Dokumentacja role_attribute_path w Grafanie opisuje trójstopniowy fallback: najpierw id_token, jeśli nic nie znajdzie — userinfo (api_url), jeśli nadal nic — access_token. Brzmi jak solidna redundancja.

Co się wydarzyło w praktyce

W Grafanie 13.2.1 fallback do userinfo jest zepsuty — potwierdzone przez grafana/grafana#106686. Jeśli claima nie ma w id_token, Grafana nie sprawdza userinfo tak, jak deklaruje dokumentacja.

Tymczasem Authelia od wersji 4.39 przestała domyślnie wrzucać claimy ze scope’ów (np. groups) do id_token — trafiają tylko do userinfo. To sensowne zaostrzenie bezpieczeństwa samo w sobie (mniej PII w tokenie, który trafia do różnych stron), ale w zderzeniu ze zepsutym fallbackiem Grafany daje cichy blackout: dokładnie ten claim, który ma znaczenie dla roli, nigdzie nie dociera.4

Fix po stronie Authelii

identity_providers.oidc.claims_policies po stronie Authelii — jawnie wymusza konkretne claimy z powrotem do id_token per-klient, referencjonowane z klienta przez claims_policy: <nazwa>.

identity_providers:
  oidc:
    # ...
    claims_policies:
      grafana: # nazwa polityki, dowolna, referencjonowana niżej
        id_token:
          - 'groups' # wymuś ten claim do id_tokenu

    clients:
      - client_id: 'inny'
        # brak claims_policy — nie dostaje tego traktowania

      - client_id: 'grafana'
        client_secret: '...'
        claims_policy: 'grafana' # podłączenie polityki do klienta
        redirect_uris:
          - 'https://grafana.example.com/login/generic_oauth'
        scopes:
          - 'openid'
          - 'profile'
          - 'email'
          - 'groups' # musi być zażądany, inaczej nic do wymuszenia

Z tymi ustawieniami ponownie można logować się do Grafany jako admin — kolejny stary problem odhaczony, na nowo i inaczej niż ostatnim razem.

I czy, mając możliwość dodania dashboardu, dodałem go?

Pingu siedzący na krześle z napisem "TERAZ JUŻ NIE CHCĘ"
teraz już nie chcę

Footnotes

  1. RPi-Monitor, pamiętamy ↩

  2. OIDC (OpenID Connect) — protokół logowania (SSO) zbudowany na OAuth2. ↩

  3. JMESPath — język zapytań do wyciągania i przekształcania danych z dokumentów JSON, tu używany do wyciągnięcia grup z claimów tokenu. ↩

  4. Kto się naszukał, jak dodać nowy dashboard, żeby po dłuższej chwili zorientować się, że ma tylko Viewera? Autor. ↩

27 kwi 2026

AAAAA Rejwa szukam

Jednym z najbardziej niespecyficznych nawyków które mam, to obczajanie zagranicznych miast na Resident Advisor:

Resident Advisor
pov: moje typowa historia przeglądania gdy myślę o wyjeździe za granicę

Zaczęło się rok temu jak przypadkiem trafiłem na Lil Texasa w Budapeszcie.

YouTube▶
Domyślam się ze nie kady zna, dlatego tez podrzucam przykład xD

Do refleksji i wspomnień pewnie jeszcze wrócę, ale gdyby ktoś szukał inspiracji jak na imprezę za granicę bądź w Polsce trafić, to chciałbym popełnić spis stron na które warto rzucić okiem planując jakiś wyjazd.

Polska

Holandia

  • Partyflock - na chwilę pisania posta niestety nie działa, ale gdyby miała wrócić - polecam!

Ogólne

  • Resident Advisor - na plus bardzo duza baza z rozbiciem na gatunki, na minus - w wiekszych krajach brak mi feedów narodowych1
  • Harderstylemap - niczym core’owe ogłoszenia parafialne, ino z mapą. I bez barier.

Footnotes

  1. Mam tak na przykład z Wielką Brytanią lub Francją ↩

strona 1 z 4 · 19 notatek
Iława Gł. ← Karpaty → Kraków Gł. astro · gh-pages ostatni wpis: 2026-09-28