Štítekfilozofie24

Archiv promlouvá

Koncem září přišel dopis. Pavel si tímto enginem zálohoval starý blog a napsal, co se při tom dozvěděl: za devět let přes tisíc příspěvků a období, kdy psal každý den. Nevěděl to. Blog byl celou dobu jeho, příspěvky byly celou dobu v něm, a přesto mu to číslo řekl až archiv, když ho poprvé někdo přečetl celý.

Tohle vydání je o tom, co archiv řekne, když se ho zeptáš. Jak se do něj dostane, jak z něj odejde a jak se hlídá, o tom web napsal dost. Tenhle text je o tom, co archiv ví o tobě.

Co archiv spočítá

./blog.sh stats přečte každý příspěvek a odpoví jednou obrazovkou. Na archivu, kolem kterého tento engine vznikl, dnes vypadá takhle:

Archiv
  příspěvků: 6912 — z toho publikovaných 6907, rozepsaných 1, naplánovaných 4
  31. 10. 2003 – 22. 10. 2026, dnů: 8392
  nejvíc toho přibylo v roce 2024 (příspěvků: 642)

Slova
  slov celkem: 425 828, průměrně na příspěvek: 61,6, uprostřed: 22
  nejdelší: minimal-jedna-barva-tri-pisma (slov: 3267)
  přečíst celý archiv trvá 35,5 hodiny
Číst dál

Kde tenhle engine vyrostl

Každá vlastnost na tomhle webu má stránku, která říká, co dělá. Méně jich říká, proč tu je, a skoro žádná, jaký byl týden před tím, než vznikla. Tahle část se psala jinde: na sean.cz, v archivu, ze kterého byl engine vyňat a na kterém každá změna přistane první, 6 655 příspěvků od roku 2003. Do minulého týdne jen česky.

Od 7. října se sean.cz čte i anglicky: 126 příspěvků, každý tentýž příspěvek jako český originál, jen se slovy v druhém jazyce, tak jak to druhý jazyk umí tady. Je mezi nimi celá série, která sleduje tenhle engine ode dne, kdy se na něj web přestěhoval, Nový Sean.cz: zatím jednadvacet dílů, červenec až říjen.

Pár míst, kde začít, pokud chcete ochutnávku a ne celý běh:

Číst dál

Stránka, kterou cizí člověk otevře první

Sobota, LinuxDays v Praze, stánek přátel z OSCloudu – Mastodon, Pixelfed, PeerTube, Matrix, služby pro lidi, kteří chtějí mít svá data tam, kde na ně vidí. Celý den za jedním stolem a ani jedna přednáška. Tento engine tam byl poprvé naživo: na letáku a v hovoru pokaždé, když se řeč o Fediverse stočila k otázce „a kde bych psal?"

Čtyři pole na jednom A4

Leták je jeden list. Čtyři pole – psaní, stěhování, nasazení, web – a v každém výřez z terminálu a QR kód. Žádný z kódů nevede na stránku vyrobenou pro tu akci. Každý otevírá štítkovou stránku, kterou tento web už měl: /tag/authoring, /tag/migration, /tag/deploy, /tag/appearance. Vlastní archiv webu, seřazený podle toho, na co se u stánku lidé ptají, byl přistávací stránkou. Nic, co by den po akci zastaralo, se pro ni nestavělo.

Číst dál

./blog.sh: blogovací engine, kterému stačí terminál

./blog.sh na Root.cz, mými slovy a česky: proč text i obrázky patří na vlastní disk, co je sestavení a kde může výsledek bydlet, jak komentáře jedou po Fediverse a proč dvaadvacet importérů čte export, ne API. A rovnou i to, pro koho engine není: pro víc autorů, pro webovou administraci, pro pluginy.

Číst dál

Co žádné sestavení nevrátí

Notebook doslouží, nebo se server buduje znovu od nuly, a otázka, na které skutečně záleží, je užší, než zní. Ne „jak to zálohovat" – „které z těchto souborů po mně engine opravdu potřebuje chránit, protože nic jiného tady je znovu nevyrobí."

Většina instalace tuto podmínku nesplňuje. Engine sám je git klon; stáhni ho znovu a je to tentýž engine. Všechno, co sestavení zapisuje, se vrátí jedním sestavením, pokud zbylo to ostatní.

Instinkt velí zálohovat samotný web – složku, kterou prohlížeč skutečně vidí. To je přesně ta jediná věc, kterou tento seznam vynechává. public.nosync, vlastní výstup sestavení, je stejně postradatelný jako engine, který ho vytvořil: jedno sestavení ho zopakuje bajt po bajtu, takže držet jeho kopii by znamenalo ukládat tutéž informaci dvakrát pod dvěma jmény a omylem věřit té starší.

Co zbývá, když odložíme engine i web, který sestavuje, je krátký seznam.

Co je skutečně nenahraditelné

Nejdřív samotné příspěvky – jediná položka celého seznamu, kterou žádné sestavení, nový klon ani nové nasazení nevrátí za žádných okolností.

Číst dál

Zámek, který nečeká

Dvakrát za hodinu na dost velkém webu sáhnou dvě úlohy cronu do téže složky téměř ve stejnou chvíli: naplánované publikování, které sestaví a nasadí příspěvek, jehož čas právě nastal, a obnova postranního panelu, která přepisuje několik týchž výstupních souborů čerstvými čísly. Většina tiků se mine o minuty. Na archivu tak velkém, že sestavení s úplným nasazením trvá déle než interval mezi nimi, se některé neminou.

Nasazení, které prochází strom, zatímco ho jiný běh ještě přepisuje, může vydat stránku zapsanou napůl. Běh, který maže osiřelé soubory, může jako osiřelou smazat stránku, kterou druhý běh o sekundu dřív publikoval. Ani jedno selhání se neohlásí – web je prostě potichu špatně.

Zámek a co odmítá dělat

Mezi těmi dvěma běhy stojí jediný soubor, .blog-sh.lock v kořeni projektu – a rozhoduje se způsobem, který vypadá špatně, dokud neuvidíš alternativu. Běh, který najde zámek obsazený, na něj nečeká.

Číst dál

Co zbývá pokazit

Na tomto webu není /admin. Žádný přihlašovací formulář, na kterém by šlo zkoušet hesla, žádná relační cookie, kterou by šlo ukrást, žádný seznam pluginů, který by bylo nutné porovnávat s CVE z minulého měsíce – ne proto, že by je někdo podle kontrolního seznamu odstranil, ale proto, že nic z toho nikdy nevzniklo.

Architektonické poznámky enginu říkají zbytek bez obalu: model hrozeb je statický web bez vlastního serveru. Nic tu neposlouchá na žádném portu. Cílem nikdy nebylo zabezpečit složitý systém proti všemu, co se v něm může pokazit. Cílem bylo žádný takový systém nepotřebovat.

Seznam toho, co zbývá pokazit, je pak kratší než u běžného blogu – co nesou stránky, co leží v pracovním adresáři, a ta jediná věc, která vstupuje zvenčí.

Jediná věc, která vstupuje zvenčí

Psaní z telefonu znamená, že jedny dveře přece jen přijímají bajty z cizího zařízení: scripts/receive.sh, spuštěný jako vynucený příkaz SSH klíče. Neposlouchá nikde – žádný socket, žádný démon, jen klíč, který smí spustit přesně tento skript a nic jiného.

Číst dál

Co se načte poslední, vyhrává

Dva skutečné weby nosí vzhled, který tento engine nikdy nedodával. sh.cynicky.blog je oblečený jako Ghost — úvodní karta přes celou šířku, tečkovaný rámeček, dlaždice místo prostého seznamu. arch-linux.cz je oblečený jako téma pro Hugo, konkrétně Blowfish — kompaktní lišta, blok s profilem, karty rozložené jako úplně jiná domovská stránka. Ani jeden nevytvořil fork enginu. Ani jeden neupravil šablonu. Oba dál přijímají obyčejný git pull.

Jeden stylesheet, načtený jako poslední

site.extra_css v config/site.yml udává cestu nebo seznam cest, které se načtou po vlastních colors.css a site.css enginu. To je celý mechanismus — skin je jeden tvůj soubor, který překresluje, co už na stránce je, a engine do něj nikdy nezapisuje, takže s ním aktualizace nikdy nemůže být v konfliktu.

Jen místní cesty: každá stránka nese style-src 'self' a stylesheet z jiného hostitele by prohlížeč prostě zahodil bez jediné chyby, kterou by kdokoli uviděl — sestavení proto vzdálenou cestu odmítne nahlas, dřív než to ticho nastane.

Číst dál

Sestavení neví, kam míří

Někdo postaví celý web na notebooku, naimportuje do něj desetiletí starých příspěvků, prohlédne si každou stránku — a nic z toho ještě nikam nedorazilo. V env.sh není řečeno kam a nic dalšího na to nečeká: sestavení proběhne, archiv projde kontrolou, každá stránka se vykreslí na localhost. Sestavení neví, kde má web nakonec skončit, a vědět to nepotřebuje.

Totéž sestavení může začít na notebooku, o rok později se přestěhovat na pronajatý VPS a potom skončit na instanci Cloudronu. Mezi těmito přesuny se v obsahové pipeline nemění nic. Mění se odpověď na jedinou otázku — kam mají soubory jít — a odpovědět na ni může šest věcí.

Co o tom rozhoduje

DEPLOY_BACKEND v env.sh určuje cíl; hodnoty pod ním závisejí na tom, který to je. Všechno za tímto řádkem je u všech šesti stejné. Každý backend si vede vlastní manifest — SHA-256, velikost a čas změny každého souboru, který už nahrál — takže nasazení posílá vždy jen to, co se od minulého změnilo. deploy-web.sh --dry-run tento manifest přečte, aniž by se čehokoli dotkl.

Takhle vypadá výstup, spuštěný právě teď proti vlastnímu backendu tohoto webu:

Číst dál

Napsáno ve vlaku

Čtenář, který uvažoval o přechodu na tento engine, napsal, že zůstane tam, kde je. Svůj blog spravuje z telefonu, na cestách, a blog, se kterým se dá pracovat jen z terminálu, mu nebyl k ničemu, ať se sestavoval jakkoli rychle. Měl pravdu.

1.6 mu odpovídá dvěma věcmi a jednou věcí, kterou odmítá udělat.

Stránka na blogu samotném

Nastav write: true a sestavení zveřejní malý editor na /write/: titulek, text, štítky, fotografie a video, každý obrázek se svým popiskem. Obléká si blog, do kterého píše – jeho jméno, jeho paletu, jeho jazyk – a při psaní nabízí vlastní štítky blogu, napřed ty použité za posledních dvanáct měsíců, aby se štítek ťuknul, místo aby se napsal podruhé jinak. Náhled ukáže příspěvek ve stylech blogu samotného. Řada značek nad textem obalí, co je vybráno, a druhým ťuknutím zase odejde. Nad tlačítkem odeslání přepínač Koncept | Publikovat.

Všechno se mezi návštěvami drží v prohlížeči – text v local storage, bajty obrázků v IndexedDB, protože local storage dostane jen pět megabajtů a jedno video z telefonu je víc. Když tě někdo na nástupišti vyruší, nic se neztratí.

Číst dál

Archiv, který nevidíš, není prázdný

Schválené komentáře tohoto webu a jeho počítadla příspěvků jsou dva malé soubory JSON, které cron každou půlhodinu obnoví a nahraje se zbytkem webu.

Až do 1.4 by běh, který proběhl, zatímco archiv nebyl na místě, oba soubory zúžil na nic a nahrál je. Chybějící nebo prázdný adresář obsahu odpoví prázdným seznamem a žádnou chybou, a prázdný seznam se čte přesně jako „každý oznámený příspěvek byl smazán". Živý web přišel o každý schválený komentář a každé počítadlo naráz. Exit 0, nic řečeno, nic posláno mailem.

Archiv bydlí na synchronizovaném svazku. Svazek, který se ještě nepřipojil, nebo pracovní kopie, jejíž soubory teprve přicházejí, není neobvyklé úterý.

Nemožnost to rozeznat je důvod všechno nechat

Ta věta je oprava a běží teď ve dvou stupních.

Pokud adresář obsahu není na místě – nebo pokud se archiv čte jako prázdný, zatímco předchozí soubory obsahovaly cokoli – nic se nezužuje a běh řekne proč.

Číst dál

Příspěvky, které nikdo publikovat nechtěl

Nejhorší selhání importéru není ztráta příspěvku. Je to jeho publikování.

1.5 prošla všech jedenáct importních adaptérů a mechaniku pod nimi a hledala přesně tohle – a našla to třikrát, ve třech různých souborech, pokaždé vzniklé stejně.

Archiv je celý účet

Export z Mastodonu není tvoje veřejná časová osa. Outbox drží vedle veřejných příspěvků i ty jen pro sledující a přímé zprávy – a každý samostatný toot v něm se zapisoval jako publikovaný. Na archivu, na kterém se to měřilo, je to 141 příspěvků z 2 548, z nichž 132 jsou přímé zprávy, každá s vlastní stránkou, vlastní položkou v sitemapě a vlastní položkou ve feedu. V souhrnu běhu o tom nebylo ani slovo.

Rozsah viditelnosti byl v exportu celou dobu, přesně tam, kam ho ActivityPub dává. Public v to je veřejný. Public v cc je mimo výpisy – skutečná adresa, ale ne ve výpisech. Jen pro sledující a přímé se stávají koncepty: to nechá autorova slova v archivu a mimo web, což je jediné čtení „není veřejné", které nemůže nic publikovat omylem.

Číst dál

Testováno jako nepřítel

1.4 prošla před tagem čtyřmi koly nepřátelského auditu: flotily nezávislých revizorů nad skutečnými archivy – skutečné exporty, skutečný web se 4 400 příspěvky, skutečné cíle nasazení – každý s pokynem engine rozbít, ne potvrdit. Nálezů bylo přes sto. Každý byl před opravou zreprodukován a každá oprava je přišpendlená testem, který na kódu, který opravila, prokazatelně padá.

Vzorec, který se pořád opakoval

Nepříjemné poučení: opravy z každého kola byly tam, kde další kolo našlo své chyby. Oprava pravidla pro čtení komentářů smazala sto řádků něčí konfigurace; oprava toho skryla odpovědi widgetu; rada k obnově napsaná k záchraně příspěvků mohla jeden zničit. Čtyřikrát za sebou byl nejnovější kód nejnebezpečnějším kódem ve stromu – což je přesně důvod, proč poslední kolo auditovalo výhradně opravy samotné. Zachytilo tři moje regrese dřív, než mohl kterýkoli uživatel.

Osmnáct souborů

Číst dál

Co smí stát pád

Fronta teď umí prohodit dva naplánované příspěvky, které sdílejí slug napříč dvěma roky – i s jejich médii a historií úprav, protože ty jsou klíčované rokem a slugem přesně jako soubor příspěvku. Ta věta je krátká; udělat ji pravdivou zabralo většinu vývojového cyklu. Zajímavá část není prohození. Je to, co se stane, když uprostřed vypadne proud.

Rozpočet je nula

Přesun je několik přejmenování a pád může dopadnout mezi kterákoli dvě. Pravidlo, kterého se 1.4 drží: ať je to kdykoli, pád tě smí stát přesun – nikdy příspěvek. Soubory ustoupí stranou pod parkovací jména dřív, než cokoli zapisuje; neúspěšný zápis vrátí všechno zpět, bajt po bajtu; tvrdé zabití, které nechá zaparkovaný soubor stát, ho nechá tam, kde ho check najde a pravdivě řekne, co je.

Rada je součást systému

Číst dál