163. Server poskytuje jen podíl své vlastní verze, s povrchy, které nabízí
Kontext
Dne 2026-10-14 vlastník otevřel /ui/kiosk na sdíleném serveru primárního checkoutu a dostal tmavou stránku s hlavičkou a pod ní nic. Konzole prohlížeče řekla proč, čtyřikrát: ui.js, ui.css, observatory.js a favicon.svg odpovědělo 404. Samotný server byl zdravý a aktuální podle všech měřítek, která měl:
| co | hodnota |
|---|---|
| proces | pid 40191, ~/.local/share/majordomus/versions/0.26.0/libexec/majordomus-cli serve |
| verze programu | 0.26.0, stav ready |
MAJORDOMUS_SHARE ve svém prostředí | <primary checkout>/share |
version.txt tohoto sdílení | version=0.21.0 |
ui/ tohoto sdílení | chybí: primární checkout byl o 638 commitů za origin/master |
nainstalované share/ vedle spustitelného souboru | version=0.26.0, ui/ dokončeno |
| co o tom řekl server | jeden řádek WARN v jeho logu při startu, nic na žádném povrchu |
Spustitelný soubor 0.26.0 obsluhoval share ve verzi 0.21.0. Jeho UI nemělo co servírovat a vše ostatní, co čte z share/ — druhy, schémata, allow-listy, inventář příkazů — bylo také o pět releasů staré. Nic v serve status, zdraví serveru, nástěnce peerů ani UI to neřeklo.
Vektorem je repozitář, který dohlíží sám na sebe. lib/rust_bin.sh (mj_rust_share) způsobuje, že bin/majordomus-cli a bin/majordomus-env exportují MAJORDOMUS_SHARE=<checkout>/share a .envrc spouští to druhé, takže jej nese každý shell checkoutu. To je správné pro executable sestavený ze stejného checkoutu, jehož verze je verzí share z konstrukce. Je to špatně pro jakýkoli jiný executable spuštěný z toho shellu: nainstalovaný release, který spouští ~/.local/bin/majordomus a ze kterého hook na začátku relace serve ensure spouští sdílený server. Share::locate respektuje proměnnou před čímkoli jiným, takže nainstalovaný executable převzal share zastaralého checkoutu beze slova.
Obě zjevné opravy selhaly týž večer, což je součást záznamu:
serve --share <installed share> ensurespustil server bez--share:ensureto nepředá procesu, který spouští, takže nový server našel share znovu ze svého prostředí.env -u MAJORDOMUS_SHARE serve ensurenalezlo<repository>/share, protože držíkinds.yaml, což je vše, coShare::locateod sdílení žádá.
Server se vrátil, teprve když bylo MAJORDOMUS_SHARE ručně nastaveno na nainstalovaný share.
Rozhodnutí
-
Share má verzi a plochy a server zná obojí. Verze sharu je to, co uvádí jeho
version.txt(version=X.Y.Z, projekce manifestu crate, ADR 0019). Jeho plochy jsou adresáře assetů, z nichž server obsluhuje stránky:ui/pro/ui(ADR 0135) acockpit/pro/cockpit. Share je pro spustitelný soubor odchýlený, když jehoversion.txtchybí, je nečitelný nebo uvádí jinou verzi než spustitelný soubor sám, nebo když plocha, kterou spustitelný soubor obsluhuje, v něm nemá adresář. Rovnost verzí je měřítko, protože je to to, které release už razítkuje na obě poloviny; dva commity jedné verze se mohou stále lišit a tuto jemnější odchylku ponechávágenerate --checkcommit hooku, který neodvozený strom už odmítá. -
Kde se share hledá podle konvence, zkreslený se přeskočí ve prospěch takového, který odpovídá.
Share::locatesi zachovává pořadí —--share,MAJORDOMUS_SHARE,share/repozitáře,share/instalace vedle spustitelného souboru — a získává jedno posouzení.--shareje slovo člověka a použije se tak, jak je zadáno, zkreslené či ne.MAJORDOMUS_SHAREashare/repozitáře jsou konvence: kandidát z kteréhokoli z nich, který je zkreslený, se přeskočí, pokud odpovídá pozdější kandidát, a použitý share to uvede ve svém původu, přičemž pojmenuje přeskočeného kandidáta a důvod (installation; MAJORDOMUS_SHARE=<path> states 0.21.0, this executable is 0.26.0). Je to případ prostředí checkoutu zasahujícího do nainstalovaného spustitelného souboru a je to jediný případ, kdy posouzení mění odpověď: spustitelný soubor sestavený z checkoutu odpovídá share tohoto checkoutu z konstrukce. -
Když neodpovídá žádný share, server obsluhuje v degradovaném stavu a říká to všude; není odmítnut. Odmítnutý server nic pod
project.shared-server-resilienceneopraví: každý klient by obsluhoval sám sebe, ze stejného share, v tolika procesech, kolik je klientů, a odchylka by byla hůře viditelná, ne lépe. První kandidát se tedy používá jako dnes a odchylka se stává faktem serveru místo řádku v jeho logu:server.status(serve status,majordomus_server,/api/v1/server) hlásí obsluhovaný share — cestu, původ, verzi, přítomné plochy — a nový stav,skewed, pro server, který odpovídá za tento checkout z verze tohoto spustitelného souboru, ale obsluhuje share, který není jeho vlastní. UI a Cockpit nesou tentýž fakt na každé stránce, kterou obsluhují, amajordomus doctorjej hlásí. -
Pozorovatelem je to, co už běží; na pozadí nebeží nic nového. Nepřidává se žádný démon a nic nepolluje, jak
project.the-server-sees-the-current-treeod dlouho žijícího procesu vyžaduje.serve ensure, který už spouští hook startu relace a.envrc, zachází sskewedod I2163 tak, jak zachází soutdated: spustí server, jehož vlastní nalezený share odpovídá, a volba tohoto serveru převezme lease. Nahrazený server si ponechá relace, které má, a skončí s nimi; klient nikdy nezůstane bez serveru. Když nelze odpovídající share nalézt,ensureohlásí nesoulad s jeho nápravou, místo aby jeden nesouladný server nahradil jiným.- Běžící server vezme otisk svého share — obsahu
version.txta přítomnosti adresáře každého povrchu — ve vlastním vlákně požadavku, vedle otisku stromu, který už bere, nejvýše jednou za omezený interval. Když se změní, server znovu rozhodne o svých adresářích assetů a jeho stav ho následuje: share opravený pod běžícím serverem se servíruje bez restartu a share, který pod ním zhavaruje, se nahlásí při dalším požadavku.
-
Vývojový režim je share nalezený z checkoutu. Share, jehož původ je
MAJORDOMUS_SHAREnebo repozitář je tento repozitář, případně adopter, který se dozoruje ze zdroje; tam se setkávají prostředí checkoutu a nainstalovaný spustitelný soubor a tam toto rozhodnutí váže. Share, jehož původ je instalace dodaná s jeho spustitelným souborem (ADR 0019): rozdíl verzí je tam rozbitá instalace adoctorji tak hlásí. -
Pravidlem je
project.a-server-serves-its-own-share. Body 1–4 uvádí jako požadované chování, je blokující a jako své testy jmenuje případy, které přidají implementující změny. Server, který odpovídáready, zatímco poskytuje sdílený podíl jiné verze, je přesně ta vada, kterou odmítá.
Co toto rozhodnutí přináší a co ne
Tento záznam je dodán samostatně, aby změny, o kterých rozhoduje, mohly přistát jako malé, odděleně posuzované pull requesty v tomto pořadí:
| krok | změna | závisí na |
|---|---|---|
| S1 | serve ensure předává --share serveru, který spouští | — |
| S2 | Observatory čte svou stránku znovu, když se stane viditelnou, takže skrytý displej netvrdí, že server neodpovídá | — |
| S3 | server.status hlásí obsluhovaný podíl a stav skewed (bod 3) | — |
| S4 | Share::locate projde přes zkosený konvenční share pro odpovídající (bod 2) | S3 |
| S5 | serve ensure nahradí server skewed a běžící server znovu přečte otisk svého share (bod 4) | S3 |
| S6 | pravidlo project.a-server-serves-its-own-share, přičemž doctor hlásí skew (body 5, 6) | S4, S5 |
| S7 | příručka, úvodní stránka a publikovaný web popisují obsluhovaný share | S6 |
S2 není důsledkem zkosení. Bylo nalezeno při jeho diagnostice, dotýká se téže obrazovky a je uvedeno, aby se neztratilo.
Zamítnuté alternativy
Odmítnout spustit server na zkresleném sdílení. Vypadá to přísně a není: pod project.shared-server-resilience pak každý klient obsluhuje pouze sám sebe z téhož sdílení a vada se násobí, zatímco každý povrch, který by ji nahlásil, zmizí se sdíleným serverem.
Přestat exportovat MAJORDOMUS_SHARE z launcherů checkoutu. Proměnná je způsob, jakým vlastní program a skripty checkoutu nacházejí jeho share, a je jedním z několika způsobů, jak se proměnná dostane do procesu: shell člověka, prostředí hooku, rodičovská relace. Odstranění z jednoho launcheru nechá otevřený každý další způsob. Posouzení patří tam, kudy projde každý volající, Share::locate.
Zkompilovat assety UI do spustitelného souboru. Skrylo by to tento incident a nic jiného: druhy a schémata by byla stále stará pět releasů. share/ se čte za běhu záměrně (apps/majordomus-cli/src/share.rs) a assety jsou totéž.
Hlídací proces, který restartuje servery. Druhý dlouho žijící proces, který dohlíží na první, je démon, který ADR 0003 odmítlo: sdílený server je potomek klienta a nic ho nedrží naživu kromě klientů. serve ensure už běží při každém startu relace a každém vstupu do shellu, tedy tak často, jak často může kdokoli začít číst odpovědi serveru.
Důsledky
- Nainstalovaný spustitelný soubor spuštěný z shellu checkoutu servíruje vlastní share a říká, který share přeskočil. Nejběžnější nesoulad se tak opraví při dalším startu relace, aniž by se po někom cokoli chtělo.
- Zkreslení, které nelze opravit, je stav, který vidí každý čtenář
serve status, fakt na každé stránce UI a nálezdoctor, místo jednoho řádku logu. - Výměna zastaralého serveru ukončí jeho mesh spojení a naváže je znovu z nového procesu, jako každá výměna serveru; žurnál běhu tím není dotčen (ADR 0067).
skewedje páté slovo, které musí zpracovat každý spotřebitelServerStanding: briefing relace,serve status, sběr serverů jiných checkoutů nástěnkou (ADR 0044) a případy, které se na slova ptají.