<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Blog ALDO TECH</title>
    <link>https://aldo.tech/sk/blog/</link>
    <atom:link href="https://aldo.tech/sk/blog/feed.xml" rel="self" type="application/rss+xml"/>
    <description>Poznámky z praxe: verejná doprava, odbavenie cestujúcich, hardvér a softvér, ktoré musia fungovať na ceste.</description>
    <language>sk</language>
    <item>
      <title>urMQTT po VPS: SQLite v používateľskom režime, porovnanie s Mosquittom, konfigurácia ako dáta a zostavovač v prehliadači</title>
      <link>https://aldo.tech/sk/blog/urmqtt-config-builder-plaintext-mosquitto/</link>
      <guid isPermaLink="true">https://aldo.tech/sk/blog/urmqtt-config-builder-plaintext-mosquitto/</guid>
      <pubDate>Sun, 01 Nov 2026 08:00:00 +0000</pubDate>
      <description>Čo sa stalo po druhom texte o urMQTT: SQLite izolovaný mimo jadra, merania proti Mosquittu na tom istom VPS (aj to, čo nešlo v náš prospech), nájdené príčiny pomalého jedného toku, konfigurácia ako dáta s webovým zostavovačom ISO a emulátorom v prehliadači, voliteľný obyčajný MQTT a čo ešte nie je dokázané.</description>
      <content:encoded><![CDATA[<p>Druhý text, <a href="https://aldo.tech/blog/urmqtt-vps-handshake-disk-hardening/">urMQTT na skutočnom VPS</a>, skončil zoznamom „Čo bude ďalej": H6 (SQLite mimo jadra), H7 (bezpečnosť času), X11 (porovnanie s Mosquittom na tom istom VPS) a prvé vydanie. Tento text sleduje ten zoznam v rovnakom poradí a potom pridáva dve veci, ktoré na zozname neboli, lebo sa objavili popri práci: konfiguráciu ako dáta s webovým zostavovačom ISO a voliteľný obyčajný MQTT bez TLS. Ako predtým: čo sme plánovali, čo sme postavili, čo nás prekvapilo a čo je otvorené. V tomto texte je aj niekoľko výsledkov, ktoré nejdú v prospech urMQTT; nechali sme ich, lebo sú súčasťou pravdy.</p>
<p>Poznámka vopred ostáva rovnaká: projekt je pred-alfa, prvé vydanie je len pre VPS s KVM a čísla merajú kód, testy a určité pokusy, nie zrelosť v produkcii.</p>
<h2 id="ako-to-vyzeralo-v-case">Ako to vyzeralo v čase</h2>
<p>Od predchádzajúceho textu (31.10. o 23:00) do dnes (1.11. o 20:41) pribudlo v repozitári 59 nových commitov, spolu 178. Všetky vznikli v jednom kalendárnom dni, takže platí rovnaké upozornenie ako predtým: projekt sa buduje s Claude Code (commity nesú <code>Co-Authored-By: Claude</code>), plán, kód, testy a ADR vznikajú v krátkych uzavretých slučkách a čísla nižšie hovoria o množstve kódu a overovaní, nie o tom, koľko ľudskej práce v nich je.</p>
<figure><img loading="lazy" alt="Rast od predchádzajúceho textu: riadkov Rustu od 89 790 do 112 408, funkcií #[test] od 739 do 926, ADR od 104 do 126 a commitov od 119 do 178. Každý riadok má vlastnú stupnicu." src="https://aldo.tech/blog/urmqtt-config-builder-plaintext-mosquitto/img/growth-since.svg" /><figcaption>Rast od predchádzajúceho textu: riadkov Rustu od 89 790 do 112 408, funkcií #[test] od 739 do 926, ADR od 104 do 126 a commitov od 119 do 178. Každý riadok má vlastnú stupnicu.</figcaption></figure>
<p>Čísla sú vypočítané rovnakou metódou ako v minulom texte: všetky súbory <code>.rs</code> mimo <code>vendor/</code> a <code>fuzz/</code>, funkcie <code>#[test]</code>, nadpisy <code>## ADR-</code> v <code>docs/DECISIONS.md</code>.</p>
<table>
<thead>
<tr>
<th>Okamih</th>
<th>Commit</th>
<th>Čas</th>
<th style="text-align: right;">Riadkov Rustu</th>
<th style="text-align: right;">Testov</th>
<th style="text-align: right;">ADR</th>
</tr>
</thead>
<tbody>
<tr>
<td>minulý text</td>
<td>794f607</td>
<td>31.10. 23:00</td>
<td style="text-align: right;">89 790</td>
<td style="text-align: right;">739</td>
<td style="text-align: right;">104</td>
</tr>
<tr>
<td>H6 hotový</td>
<td>a0423cc</td>
<td>1.11. 03:59</td>
<td style="text-align: right;">95 078</td>
<td style="text-align: right;">754</td>
<td style="text-align: right;">107</td>
</tr>
<tr>
<td>X11</td>
<td>a29f49b</td>
<td>1.11. 07:56</td>
<td style="text-align: right;">95 579</td>
<td style="text-align: right;">754</td>
<td style="text-align: right;">108</td>
</tr>
<tr>
<td>webový zostavovač a zrkadlo vydaní</td>
<td>582783f</td>
<td>1.11. 16:49</td>
<td style="text-align: right;">105 645</td>
<td style="text-align: right;">848</td>
<td style="text-align: right;">122</td>
</tr>
<tr>
<td>obyčajný MQTT, dokumentácia</td>
<td>8f959f1</td>
<td>1.11. 19:05</td>
<td style="text-align: right;">112 408</td>
<td style="text-align: right;">926</td>
<td style="text-align: right;">125</td>
</tr>
<tr>
<td>dnes</td>
<td>d21e3ec</td>
<td>1.11. 20:41</td>
<td style="text-align: right;">112 408</td>
<td style="text-align: right;">926</td>
<td style="text-align: right;">126</td>
</tr>
</tbody>
</table>
<p>Pracovalo sa paralelne na viacerých vetvách, takže poradie commitov nie je presne poradím práce. ADR sú číslované až do 232, ale číslovanie má medzery (časť sa pri zlučovaní vetiev premenovala); počítame nadpisy, nie najvyššie číslo. Vydania: v0.2.0 (1.11., 18:45 UTC) a v0.2.1 (1.11., 20:22 UTC), obe označené ako pre-release.</p>
<h2 id="h6-sqlite-v-pouzivatelskom-rezime">H6: SQLite v používateľskom režime</h2>
<p><strong>Čo sme plánovali.</strong> SQLite je 254 268 riadkov C, ktoré parsujú databázu, WAL a žurnál z disku, a disk môže byť vyrobený alebo zmenený. Dovtedy bežal v jadre, s právami jadra. <strong>Čo sme postavili</strong> (ADR-210 až ADR-212): SQLite, jeho časti libc a heap sa presunuli do samostatného programu, <code>dbworker</code>, ktorý beží na EL0 (aarch64), resp. ring 3 (x86_64), vo vlastnom adresnom priestore. Jadro už SQLite nelinkuje. Worker komunikuje s jadrom cez spoločné okno pamäte a dvanásť systémových volaní a každá dĺžka a posun, ktoré sa vrátia, sa pred použitím kontrolujú. Chyba vo workeri (nepovolený prístup, nedefinovaná inštrukcia, prekročený časový rozpočet 30 s) ukončí volanie, worker sa znova spustí a databáza sa otvorí z WAL ako pri zavádzaní; po 5 opätovných spusteniach za minútu sa úložisko vyhlási za chybné a nespúšťa sa donekonečna.</p>
<p>Cena, nameraná na aarch64 pod HVF, tá istá VM, jadro so zabudovaným SQLite oproti jadru s workerom, šesť striedavých spustení každého:</p>
<table>
<thead>
<tr>
<th>Meranie</th>
<th>Predtým (v jadre)</th>
<th>Potom (worker)</th>
<th>Rozdiel</th>
</tr>
</thead>
<tbody>
<tr>
<td>vstup do používateľského režimu a výstup z neho</td>
<td>-</td>
<td>1,1 us na kolo</td>
<td></td>
</tr>
<tr>
<td><code>db-bench</code>, 5 000 vložení v 50 transakciách, najlepší / medián</td>
<td>61,9k / 58,4k op/s</td>
<td>54,7k / 53,3k op/s</td>
<td>-11,6% / -8,7%</td>
</tr>
<tr>
<td>jedno vloženie na transakciu, najlepší / medián</td>
<td>2 319 / 2 148 op/s</td>
<td>2 307 / 2 126 op/s</td>
<td>-0,5% / -1%</td>
</tr>
<tr>
<td>celá záloha jedným volaním (23 MiB)</td>
<td>514 ms</td>
<td>635 ms</td>
<td>+23%</td>
</tr>
</tbody>
</table>
<p>Zápisy na disk (1 572) a flushe (319) sú identické, ako to má byť. Toto nie je izolované: <strong>jadro stále verí tomu, čo worker vráti</strong>, po kontrole tvaru. Kompromitovaný worker môže klamať (napríklad vrátiť prázdny zoznam zrušených certifikátov a tým ich „odrušiť"), ale nemôže čítať pamäť jadra, kľúče TLS ani kľúče tiketov. Nie je tu KPTI, takže úniky typu Spectre/Meltdown nie sú zabránené a nič o nich netvrdíme; kľúč PAC zdieľajú EL0 a EL1. Na x86_64 sme cenu prepínania na KVM nemerali (pod TCG by bolo číslo nezmyselné) a samotné fuzzovanie workera a protokolu nie je hotové. Súbor testov pre úložisko (79 scenárov, každý točí SQLite vo workeri) prechádza na aarch64 HVF a x86_64 TCG, na aarch64 TCG 76 (jeden sa preskakuje, lebo TCG nemá MSI-X).</p>
<p><strong>H7 je uzavretý, ale nie tak, ako sme ohlásili.</strong> Dolná hranica času (ADR-050) a SNTP s konsenzom (T1) pokrývajú rozsah H7; NTS (RFC 8915) ostáva mimo rozsahu a je zapísaný ako zvyškové riziko, nie ako niečo, čo funguje. Nepovažujeme bezpečnosť času za „vyriešenú", ale za hotovú v jej dokázateľnej časti.</p>
<p><strong>Nová sieťová možnosť.</strong> Contabo nedáva DHCP vlastnému ISO, takže VPS dovtedy potreboval iný obraz než laboratórna VM. Teraz <code>network.mode = "auto"</code> (ADR-214) najprv sa opýta DHCP a ak prenájom nepríde v danej lehote (<code>dhcp_timeout_s</code>, predvolene 15 s) alebo ho stratí, použije zadanú statickú adresu. Vyskúšané ručne na QEMU (s DHCP použije prenájom, bez siete sa po 5 s vráti na statickú adresu); smoke scenár na to ešte nie je.</p>
<h2 id="x11-porovnanie-s-mosquittom-na-tom-istom-vps">X11: porovnanie s Mosquittom na tom istom VPS</h2>
<p>Rovnaký druh stroja ako v minulom texte (Contabo, 4 vCPU AMD EPYC, 8 GiB), záťaž z linuxového hostiteľa vzdialeného 15 ms, vzájomné TLS 1.3 s rovnakými certifikátmi na oboch stranách, Mosquitto 2.0.21 na Debiane 13 bez trvanlivosti. Metóda a rozhodnutia sú v ADR-213, surové dáta v <code>docs/perf-results/</code>. Medián z troch spustení.</p>
<figure><img loading="lazy" alt="Úplné podania rúk za sekundu (TLS 1.3 + klientsky certifikát + CONNECT, bez obnovenia relácie) pri 32, 64 a 128 klientoch: urMQTT 169, 278, 413; Mosquitto 82, 140, 184. Sú to dolné hranice: generátor záťaže bol blízko vlastného limitu." src="https://aldo.tech/blog/urmqtt-config-builder-plaintext-mosquitto/img/mosquitto.svg" /><figcaption>Úplné podania rúk za sekundu (TLS 1.3 + klientsky certifikát + CONNECT, bez obnovenia relácie) pri 32, 64 a 128 klientoch: urMQTT 169, 278, 413; Mosquitto 82, 140, 184. Sú to dolné hranice: generátor záťaže bol blízko vlastného limitu.</figcaption></figure>
<table>
<thead>
<tr>
<th></th>
<th>urMQTT</th>
<th>Mosquitto 2.0.21</th>
</tr>
</thead>
<tbody>
<tr>
<td>podania rúk/s, 32 / 64 / 128 klientov (bench profil)</td>
<td>169 / 278 / 413</td>
<td>82 / 140 / 184</td>
</tr>
<tr>
<td>to isté, s limitmi, aké sa dodávajú</td>
<td>185 / 262 / 333 (pri 128: 202 až 286 chýb, reset podania rúk)</td>
<td>-</td>
</tr>
<tr>
<td>pamäť na neaktívne spojenie (20 000 spojení)</td>
<td>0,50 KB haldy jadra (0,66 KB pri 5 000)</td>
<td>26,8 KB rastu procesu</td>
</tr>
<tr>
<td>latencia p50, QoS 0 / QoS 1, 800 správ/s, 64 B</td>
<td>14,0 / 14,8 ms</td>
<td>21,4 / 39,5 ms</td>
</tr>
<tr>
<td>QoS 1, 4 800 správ/s, doručené/s (p50)</td>
<td>4 209 (77 ms)</td>
<td>4 235 (197 ms)</td>
</tr>
<tr>
<td>100 odberateľov, 50 správ/s: p50</td>
<td>16,6 ms</td>
<td>99 ms</td>
</tr>
<tr>
<td>jeden tok, QoS 0, 64 B, publikované/s</td>
<td>2 962 (0 až 7 733)</td>
<td>22 749</td>
</tr>
</tbody>
</table>
<p>Čo to znamená a čo nie. Podania rúk sú <strong>dolné hranice pre oba servery</strong>: generátor záťaže je zdieľaná VM s 60 až 70% ukradnutého procesorového času a pri 128 vláknach bol blízko vlastného limitu, kým pracovné jadrá urMQTT boli zaneprázdnené iba 12 až 13%. Pomer pri 128 klientoch (približne 2,2-krát) je najzmysluplnejší; pri 32 a 64 väčšinou meria čakanie klientov. Čísla pamäte nepočítajú to isté (halda jadra oproti RSS procesu), takže „približne 50-krát menej" je rádovo, nie presný pomer. Latencia 14 ms je jedno kolo siete (približne 15 ms), teda „žiadny merateľný čas brokera"; chvosty (p99 približne 100 až 200 ms) sú pri oboch brokeroch ovládané zdieľanými hostiteľmi. Rozdiely pod približne 20% netvrdíme.</p>
<p><strong>Čo nešlo v náš prospech</strong> a zostalo v tabuľkách: jeden TCP tok cez urMQTT niesol 2 až 8-krát menej než cez Mosquitto, so zmenami od nuly do 7,7 tisíc správ za sekundu medzi spusteniami, a 20 ms watchdog handlera <strong>prerušoval živé spojenia</strong>, keď hypervízor zastavil vCPU (v produkčnom nastavení <code>action = "drop"</code>, 6 až 8 spojení na spustenie pri zaplavení QoS 0). Obe veci sú opísané v ďalšej časti, lebo mali príčiny v našom kóde.</p>
<h2 id="preco-bol-jeden-tok-pomaly-priciny-nie-vyhovorky">Prečo bol jeden tok pomalý: príčiny, nie výhovorky</h2>
<p>Najjednoduchšie by bolo povedať „zdieľaný VPS". Nepovedali sme to, ale pri tom istom VPS a tej istej metóde sme hľadali príčiny. Našlo sa ich päť, štyri v dátovej ceste a jedna vo watchdogu (ADR-221 až ADR-224):</p>
<table>
<thead>
<tr>
<th>Príznak v X11</th>
<th>Príčina v urMQTT</th>
<th>Zmena</th>
</tr>
</thead>
<tbody>
<tr>
<td>jeden tok 1,9k správ/s, často <strong>0 doručených</strong></td>
<td><strong>Stratené prebudenie:</strong> výstupná slučka vracala zoznam pripravených spojení cez práve vypísaný; odberateľ, ktorému sa raz zaplnil výstupný medzipamäť, už nebol nikdy obslúžený, kým ho neprebudil PINGRESP</td>
<td>opravené (ADR-223)</td>
</tr>
<tr>
<td>niekoľko percent straty mení 48k na 14k správ/s</td>
<td><strong>každý segment po stratenom bol zahodený</strong> (príjem iba v poradí), takže obnova stála kolo siete na segment</td>
<td>rad segmentov mimo poradia, SACK (ADR-222)</td>
</tr>
<tr>
<td>strop 4,4 MB/s pri 15 ms</td>
<td>16-bitové okno, rad odosielania 64 KiB a pomalý štart, ktorý počítal ACK, nie bajty</td>
<td>škálovanie okna, časové značky, počítanie bajtov (ADR-222)</td>
</tr>
<tr>
<td>každé kolo toku, ktoré sa zmestí, čaká 0 až 10 ms</td>
<td><strong>dispečer spal do ďalšieho tiku 10 ms</strong> (virtio-net nemá prerušenie pre príjem)</td>
<td>dopytovanie NIC každých 200 us, kým existujú spojenia (ADR-224)</td>
</tr>
<tr>
<td>1 až 8 prerušených spojení na spustenie</td>
<td><strong>watchdog handleru účtoval uplynulý čas</strong>, a VPS zastavuje vCPU často a dlho</td>
<td>rozpočet je čas, ktorý jadro skutočne bežalo (ADR-221)</td>
</tr>
</tbody>
</table>
<p>Zastavenia sú skutočné: na tomto VPS mali jadrá 0 až 2 medián 36 až 80 oneskorených tikov za 5 sekúnd a najhoršie zastavenie v okne bolo v mediáne približne 70 ms a až 0,9 s (jadro 3, databáza a žurnál: až 1,55 s), kým hostiteľ nehlási ukradnutý čas (<code>host steal 0 us</code>). Preto watchdog potreboval druhé hodiny, nie väčší rozpočet.</p>
<figure><img loading="lazy" alt="Pred a po, na tom istom VPS s rovnakou metódou, medián troch až šiestich spustení: jeden tok QoS 0, 64 B: publikované 1,9k, 23,7k (Mosquitto 29,3k), doručené 0 (medián), 12,6k (29,2k). 16 párov, zaplavenie, 64 B: publikované 24,7k, 43,2k (31,4k), doručené 20,0k, 37,4k (30,7k)." src="https://aldo.tech/blog/urmqtt-config-builder-plaintext-mosquitto/img/before-after.svg" /><figcaption>Pred a po, na tom istom VPS s rovnakou metódou, medián troch až šiestich spustení: jeden tok QoS 0, 64 B: publikované 1,9k, 23,7k (Mosquitto 29,3k), doručené 0 (medián), 12,6k (29,2k). 16 párov, zaplavenie, 64 B: publikované 24,7k, 43,2k (31,4k), doručené 20,0k, 37,4k (30,7k).</figcaption></figure>
<table>
<thead>
<tr>
<th></th>
<th>kód X11 (7f9517e)</th>
<th>teraz (86a71ff)</th>
<th>Mosquitto</th>
</tr>
</thead>
<tbody>
<tr>
<td>jeden tok, QoS 0, 64 B, publikované / doručené za s</td>
<td>1,9k / 0</td>
<td>23,7k / 12,6k</td>
<td>29,3k / 29,2k</td>
</tr>
<tr>
<td>jeden tok, QoS 0, 1 KiB</td>
<td>0 / 0</td>
<td>3,9k / 2,3k</td>
<td>5,4k / 5,4k</td>
</tr>
<tr>
<td>16 párov, zaplavenie, QoS 0, 64 B</td>
<td>24,7k / 20,0k</td>
<td>43,2k / 37,4k</td>
<td>31,4k / 30,7k</td>
</tr>
<tr>
<td>16 párov, zaplavenie, QoS 0, 1 KiB</td>
<td>3,7k / 2,9k</td>
<td>5,0k / 4,5k</td>
<td>5,9k / 5,7k</td>
</tr>
<tr>
<td>stratené spojenia na spustenie (16 párov, 64 B)</td>
<td>13 (7 až 16)</td>
<td>0 (0 až 0)</td>
<td>0</td>
</tr>
</tbody>
</table>
<p>Watchdog: 148 prerušených spojení v 30 zaťažených spusteniach predtým, 0 v 24 spusteniach potom; na lokálnom zariadení, ktoré oneskoruje a stráca rámce, má pri strate 0,1% jeden tok 64 B s 14,3k správ/s (jadro X11) teraz 41,0k, a pri 0,5% 5,0k namiesto 17,7k.</p>
<p><strong>Čo toto nehovorí.</strong> urMQTT <strong>stále nie je rýchlejší než Mosquitto na jednom toku</strong>: pri 64 B publikuje približne 80% jeho rýchlosti a doručuje približne 40%, lebo zahadzuje QoS 0, keď je tok odberateľa pomalší než tok vydavateľa (Mosquitto bez hranice zaraďuje do radu). Pri 1 KiB v 16-párovom zaplavení je Mosquitto popredu približne o 15%, čo je v rámci šumu. Generátor bol znova zdieľaná VM s 58 až 77% ukradnutého času. Druhé kolo (zamykanie haldy mimo dátovej cesty, SACK na strane odosielania a ďalšie) je na neintegrovanej vetve <code>perf-single-flow-wip</code>; jeho čísla zatiaľ nie sú tvrdenia a neuvádzame ich tu.</p>
<h2 id="slub-ktory-sme-nedali-ale-splnili-sme-ho-konfiguracia-je-data">Sľub, ktorý sme nedali, ale splnili sme ho: konfigurácia je dáta</h2>
<p>Doteraz bola každá konfigurácia novým zostavením: kľúče a limity boli zabudované v obraze. C1 to obracia (ADR-215 až ADR-219): jadro sa zostavuje raz a konfigurácia sú dáta.</p>
<ul>
<li><strong>Blob <code>urmqtt-cfg</code></strong> (C1a): verzovaný, kanonický zápis (polia podľa rastúcich značiek, odtlačok SHA-256, voliteľný oddelený podpis Ed25519), 70 polí s predvolenými hodnotami a hranicami, dekodér, ktorý odmieta všetko chybné.</li>
<li><strong>Jadro ho číta ako modul Limine</strong> (C1b), pred čímkoľvek iným, na aarch64 a x86_64. Existuje <strong>jedno vydané jadro pre každé <code>ENV</code> okrem <code>dev</code></strong> (ELF-y aarch64 pre <code>release</code>, <code>prod</code> a <code>bench</code> majú rovnaké SHA-256; overené iba na aarch64). Vydané jadro odmieta vývojovú konfiguráciu a bez konfigurácie napíše „refusing to serve" a nič neposkytuje.</li>
<li><strong>Nainštalovaný disk nesie konfiguráciu</strong> a aktualizácia ju zachováva (C1c).</li>
<li><strong><code>urmqtt-config</code> a šablóna ISO</strong> (C1d): <code>pack</code>, <code>show</code>, <code>verify</code>, a <code>pack --iso</code> nahrádza obe kópie miesta pre konfiguráciu a 24 súhrnov modulov v hotovom ISO. Výsledok je bajt po bajte rovnaký ako ISO, ktoré zostavuje <code>mkiso.sh --config</code>. Šablóna vydania v0.2.0 má 63 111 168 bajtov.</li>
<li><strong>Vyhlásenie o dôvere</strong> (ADR-219): nakonfigurovaný obraz pri zavádzaní vypíše odtlačok konfigurácie (<code>config: fingerprint ...</code>) a podpis je voliteľný a <strong>predvolene vypnutý</strong>. Zmenené ISO nemá oficiálny súhrn; odtlačok je spôsob, ako vidieť, čo je v ňom.</li>
</ul>
<h2 id="zostavovac-na-aldotech">Zostavovač na aldo.tech</h2>
<p>To isté, čo robí <code>urmqtt-config pack --iso</code>, robí aj webová stránka, tým istým kódom: knižnica nástroja preložená do WebAssembly (1,06 MB, bez importov a bez bloku <code>unsafe</code>). Stránka je na <a href="https://aldo.tech/sk/produkty/urmqtt-builder/">aldo.tech/sk/produkty/urmqtt-builder/</a> a existuje vo všetkých 15 jazykoch stránky (napríklad <a href="https://aldo.tech/proizvodi/urmqtt-builder/">hr</a>).</p>
<ul>
<li><strong>Nič neopúšťa prehliadač.</strong> Certifikáty, súkromné kľúče a hotové ISO ostávajú v počítači; stránka sťahuje iba súbory vydania z tohto miesta, nie je tu sledovanie ani skripty tretích strán. V teste naživo žiadna požiadavka nenies kľúčový materiál.</li>
<li><strong>Zrkadlo vydaní</strong> (ADR-227): hodinu po hodine (systemd timer, nízka priorita) kopíruje vydania z GitHubu, každé skontroluje pred zverejnením a stránka v prehliadači overuje podpis <code>SHA256SUMS</code> kľúčom zabudovaným v stránke, a až potom zapíše konfiguráciu do šablóny. Neúspešná kontrola blokuje stiahnutie.</li>
<li><strong>Jednoducho podľa predvoleného, pokročilo podľa potreby</strong> (ADR-232): prvá obrazovka sú tri kroky (kde to pobeží, vaše certifikáty, názov obrazu). Jedno pole na pretiahnutie súborov roztriedi certifikát, kľúč a CA podľa obsahu. Pokročilé zobrazenie otvára každé zo 70 polí, vyhľadávanie, import a export TOML; obe zobrazenia upravujú rovnaký stav. Šablóna (63 MB) sa sťahuje, kým používateľ vypĺňa formulár. V skúške naživo, so skúšobným zrkadlom a skúšobným kľúčom, zostavenie ISO v prehliadači trvalo 5 až 18 s.</li>
</ul>
<figure><img loading="lazy" alt="Jednoduché zobrazenie zostavovača: tri kroky, „Kde to pobeží?&quot;, „Vaše certifikáty&quot; a „Názov obrazu&quot;, s jednou ďalšou akciou v bočnom paneli." src="https://aldo.tech/blog/urmqtt-config-builder-plaintext-mosquitto/img/builder-simple.png" /><figcaption>Jednoduché zobrazenie zostavovača: tri kroky, „Kde to pobeží?&quot;, „Vaše certifikáty&quot; a „Názov obrazu&quot;, s jednou ďalšou akciou v bočnom paneli.</figcaption></figure>
<figure><img loading="lazy" alt="Pokročilé zobrazenie, zachytené na skúšobnom zrkadle so skúšobným kľúčom (dátum vydania a podpisový kľúč nie sú skutočné): vydanie s overeným podpisom, šablóny, každé pole konfigurácie." src="https://aldo.tech/blog/urmqtt-config-builder-plaintext-mosquitto/img/builder-advanced.png" /><figcaption>Pokročilé zobrazenie, zachytené na skúšobnom zrkadle so skúšobným kľúčom (dátum vydania a podpisový kľúč nie sú skutočné): vydanie s overeným podpisom, šablóny, každé pole konfigurácie.</figcaption></figure>
<p><strong>Emulátor, voliteľne.</strong> Vedľa „Stiahnuť ISO" stojí „Vyskúšať v emulátore": QEMU preložené do WebAssembly spustí práve zostavený obraz v karte prehliadača. Nič z neho sa nesťahuje pred kliknutím (približne 3 MB prvýkrát). Emulátor je len BIOS, <strong>bez siete</strong> (aby prevádzka používateľa neprešla cez nejakého sprostredkovateľa a nenarušila „nič neopúšťa prehliadač"), vyžaduje približne 1,5 GB pamäte a počítačový prehliadač (testovaný Chrome; Firefox a Safari nie). V ňom urMQTT dosiahne <code>URMQTT READY</code> približne za 8 s (s preskočeným odpočítavaním) a vypíše rovnaký odtlačok konfigurácie ako stránka, čo je dôkaz, že sa zaviedla práve tá konfigurácia. Zavedenie je približne 2-krát pomalšie než pôvodné QEMU na tom istom Macu (0,82 s oproti 1,9 s) a kryptografia približne 58-krát pomalšia, lebo hardvérové inštrukcie sú emulované. Preto <strong>z emulátora nevyvodzujeme žiadne tvrdenia o rýchlosti</strong> a stránka to hovorí pred spustením. Pri zostavení bolo treba záplatovať štyri bajty v <code>.wasm</code> kvôli chybe v preklade inštrukcie POPCNT v pôvodnom emulátore (ešte sme ju nenahlásili); samotný emulátor je pod GPL, s ponukou zdrojového kódu.</p>
<figure><img loading="lazy" alt="Diagnostický vstup v emulátore naživo na aldo.tech: kernel vypísal rovnaký odtlačok konfigurácie ako stránka a obrazovka zobrazuje diagnostiku (v emulátore skončí približne za 20 s; jej čísla opisujú emulátor, nie urMQTT)." src="https://aldo.tech/blog/urmqtt-config-builder-plaintext-mosquitto/img/emulator-diagnostic.png" /><figcaption>Diagnostický vstup v emulátore naživo na aldo.tech: kernel vypísal rovnaký odtlačok konfigurácie ako stránka a obrazovka zobrazuje diagnostiku (v emulátore skončí približne za 20 s; jej čísla opisujú emulátor, nie urMQTT).</figcaption></figure>
<h2 id="obycajny-mqtt-len-pre-siete-ktorym-doverujete">Obyčajný MQTT, len pre siete, ktorým dôverujete</h2>
<p>Obyčajný MQTT (TCP 1883, bez TLS) je v IoT veľmi bežný: je predvolený v Mosquittu, vo väčšine firmvérov pre Arduino a ESP, v Tasmote, Zigbee2MQTT a Home Assistante a používa sa aj vo vnútri segmentovaných priemyselných sietí, za VPN a na súkromných LAN, ba aj na zariadeniach bez hodín (certifikáty vyžadujú dátum), bez dostatku flash pamäte alebo procesora pre TLS. Cez nespoľahlivú sieť neprichádza do úvahy: kto je na ceste, číta a mení prevádzku. Preto je P1 (ADR-229 až ADR-231) výslovná, obmedzená voľba, <strong>predvolene vypnutá</strong>, a port TLS ostáva; TLS sa tým nevypína, obyčajný port prichádza popri ňom.</p>
<ul>
<li><strong>Zoznam povolených sietí je povinný</strong> (<code>allow_from</code>, CIDR) a kontroluje sa pri SYN: mimo zoznamu nie je SYN-ACK ani stav. Celý internet (<code>0.0.0.0/0</code>) vyžaduje samostatnú výslovnú možnosť.</li>
<li><strong>Tri spôsoby prihlásenia:</strong> heslo (predvolené, keď je zapnuté; PBKDF2-HMAC-SHA-256, soľ, 10 000 iterácií, porovnanie v konštantnom čase, zamykanie podľa adresy), anonymne (jedna nakonfigurovaná identita s vlastným ACL) a podľa adresy (<code>ip</code>). Zostavovač heslo rozptyľuje v prehliadači a v pamäti drží iba súhrn.</li>
<li><strong>Menej práv než mTLS:</strong> obyčajný klient nikdy nemá servisné, operátorské ani riadiace práva, nemá <code>$SYS</code> ani konzolu, má vlastné relácie (<code>$plain/&lt;identita&gt;/&lt;id klienta&gt;</code>, iba čisté), vlastné limity a register povolených ho stále zaväzuje.</li>
<li><strong>Čo sa stráca, je napísané v <code>SECURITY.md</code>:</strong> dôvernosť, integrita, autentickosť brokera a opakovanie zachyteného prihlásenia. To sa konfiguráciou nedá opraviť; tak to je bez TLS.</li>
</ul>
<p>Namerané na laptope (Apple silicon, HVF, používateľská sieť QEMU), 16 párov, 64 B, tri spustenia po 10 s: pri 8 000 správach za sekundu TLS aj obyčajná cesta doručia všetky správy s rovnakou latenciou (p50 0,17 ms oproti 0,13 až 0,18 ms), nové spojenia za sekundu: TLS približne 2 600 až 2 750, anonymné obyčajné približne 4 900 až 7 800, a <strong>prihlásenie heslom je drahá časť</strong>: jeden PBKDF2 stojí približne 2,5 ms, takže počet nových prihlásení je obmedzený na približne 200 na dátové jadro za sekundu (namerané 308 až 342 na dvoch jadrách). Zaplavenie na tomto stroji nie je stabilný nástroj (20k až 440k doručených za sekundu medzi spusteniami) a <strong>z neho nevyvodzujeme záver</strong>. Porovnanie obyčajnej cesty s Mosquittom na VPS ešte nie je urobené. Testy: smoke skupina <code>plaintext</code> 7 zo 7 na aarch64 HVF a na x86_64 TCG, nový fuzz cieľ <code>plain_input</code> (2,3 milióna spustení bez nálezov).</p>
<h2 id="chyby-ktore-nasla-praca-nazivo">Chyby, ktoré našla práca naživo</h2>
<p>Dve veci neodchytili testy, ale prvé zapnutie na skutočnom mieste:</p>
<ul>
<li>Stránka hľadala súbory vydania o <strong>jeden adresár vyššie</strong>, takže každé overenie podpisu odpovedalo 404 (oprava 13af301). Test používal skúšobné zrkadlo na inej ceste.</li>
<li><strong>Klávesnica emulátora nikdy nedorazila k hosťovi</strong>: príčinou nebol zlý preklad kláves, ale to, že dialóg klávesy vôbec nepočúval (oprava v ADR-232, spolu so vstupom na zavedenie bez kláves).</li>
</ul>
<h2 id="lekcie">Lekcie</h2>
<ul>
<li><strong>Hľadajte príčinu, neobviňujte prostredie.</strong> Väčšina slabín jedného toku a všetky prerušené spojenia boli naše; hypervízor je skutočný, ale bolo ho treba merať, nie predpokladať.</li>
<li><strong>Porovnanie s iným produktom musí zahŕňať aj straty.</strong> Tabuľka, v ktorej sú iba naše víťazstvá, by nebola porovnaním.</li>
<li><strong>Merací prístroj má hranice.</strong> Zdieľaný generátor so 60 až 70% ukradnutého času mení, čo čísla znamenajú; preto píšeme „dolná hranica" a netvrdíme rozdiely pod 20%.</li>
<li><strong>Izolácia nie je dôkaz.</strong> SQLite v EL0 zmenšuje, čo môže chyba v ňom, nie to, čo môže klamať.</li>
<li><strong>Skutočná inštalácia nájde to, čo testy nehľadajú</strong>: cestu, ktorá bola nesprávne poskladaná, a klávesy, ktoré nikto nepočúva.</li>
</ul>
<h2 id="co-este-nie-je-dokazane">Čo ešte nie je dokázané</h2>
<ul>
<li>Podpisový kľúč vydaní je zatiaľ skúšobný kľúč; v0.2.0 a v0.2.1 sú pre-release a obrazy k nim sú vývojové so skúšobnými certifikátmi (okrem šablóny, ktorá je bez konfigurácie).</li>
<li>13 prekladov stránky zostavovača (a tohto textu) napísala AI a overili ich testy a vzorky, nie rodení hovoriaci.</li>
<li>Porovnanie s Mosquittom je jedna relácia, jedna cesta a jeden zdieľaný generátor, na zdieľanom VPS; rozdiely pod 20% nie sú tvrdenia.</li>
<li>Obyčajná cesta nebola meraná na VPS ani proti Mosquittu.</li>
<li>Lighthouse na novom jednoduchom zobrazení nie je potvrdený (staré zobrazenie: dostupnosť 95 alebo viac).</li>
<li>Bare metal, cena workera na x86_64 na KVM, Firefox a Safari pre emulátor: nemerané.</li>
</ul>
<h2 id="co-bude-dalej">Čo bude ďalej</h2>
<ul>
<li><strong>D1: verejný demo broker.</strong> Emulátor z prehliadača nemôžeme sprístupniť verejne: žije v karte jedného návštevníka, je 20 až 100-krát pomalší než KVM, nemá prichádzajúcu sieť a zmizne so zatvorením karty; presmerovanie internetu do cudzieho prehliadača by bolo aj zneužitie. Možný je skutočný demo broker: urMQTT na VPS za menom na aldo.tech, so zverejnenou skúšobnou CA, prísnym ACL a malými limitmi. Je to zatiaľ len nápad, nie stavba, a vyžaduje výslovný súhlas na verejné vystavenie.</li>
<li><strong>Druhé kolo pre jeden tok</strong> (vetva <code>perf-single-flow-wip</code>): až keď bude namerané na VPS a zlúčené.</li>
<li><strong>Obyčajná cesta proti Mosquittu na VPS</strong>, prihlásenie a zrušenie účtu pre obyčajný MQTT cez podpísaný riadiaci kanál.</li>
<li><strong>Po vydaní:</strong> bare metal (X12 až X16), IOMMU a Secure Boot, šifrovanie disku.</li>
</ul>
<p>Kód je na <a href="https://github.com/zeljan-alduk/urMQTT">github.com/zeljan-alduk/urMQTT</a> a <code>docs/PROGRESS.md</code> je naďalej jediným zdrojom pravdy o tom, čo skutočne funguje, <code>docs/PERFORMANCE.md</code> o tom, čo je namerané. Pred-alfa, ako predtým; hlásenia chýb a bezpečnostné hlásenia podľa pokynov v <code>SECURITY.md</code> sú vítané.</p>]]></content:encoded>
      <category>rust</category>
      <category>mqtt</category>
      <category>iot</category>
      <category>unikernel</category>
      <category>open source</category>
    </item>
    <item>
      <title>urMQTT na skutočnom VPS: ISO, podanie rúk za 0,24 ms, vlastný disk a spevnené jadro</title>
      <link>https://aldo.tech/sk/blog/urmqtt-vps-handshake-disk-hardening/</link>
      <guid isPermaLink="true">https://aldo.tech/sk/blog/urmqtt-vps-handshake-disk-hardening/</guid>
      <pubDate>Sat, 31 Oct 2026 08:00:00 +0000</pubDate>
      <description>Čo sa stalo s tromi sľubmi z prvého textu o urMQTT (jeden ISO, surový disk bez súborového systému, dokázateľná bezpečnosť) po prvých štyroch dňoch vývoja, s meraniami, tabuľkami a zoznamom toho, čo ešte nie je hotové.</description>
      <content:encoded><![CDATA[<p>V <a href="https://aldo.tech/blog/urmqtt-unikernel-mqtt-broker/">prvom texte o urMQTT</a> sme opísali unikernel, ktorý <em>je</em> MQTT broker, po míľnik M17, a skončili sme tromi sľubmi pod nadpisom „Čo bude ďalej": jeden ISO, ktorý sa zavádza na viacerých platformách, surový disk bez súborového systému a bezpečnosť, ktorú možno dokázať, a nielen tvrdiť. Nič z toho vtedy ešte nebolo postavené. Tento text sleduje, čo sa stalo s každým sľubom, v rovnakom poradí ako predtým: čo sme plánovali, čo sme postavili, čo nás prekvapilo a čo je ešte otvorené. Popritom ukážeme aj to, ako dlho to trvalo, lebo aj to patrí k príbehu.</p>
<p>Jedna poznámka vopred: projekt je stále pred-alfa. Čísla v texte merajú kód a testy, nie zrelosť v produkcii, a prvé vydanie je zámerne obmedzené na VPS s KVM. Bare metal ostáva na neskôr.</p>
<h2 id="ako-to-vyzeralo-v-case">Ako to vyzeralo v čase</h2>
<p>Prvý commit vznikol 28. októbra 2026 o 23:48 (M0). Do 31. októbra do 23:00 má repozitár 119 commitov, rozložených ako 3, 39, 38 a 39 podľa kalendárnych dní. Hneď povedzme aj to, ako je to možné: projekt sa buduje s Claude Code a commity nesú trailery <code>Co-Authored-By: Claude</code>. Štyri kalendárne dni histórie neznamenajú, že jeden človek napísal za štyri dni 90 000 riadkov; znamenajú, že plán, implementácia, testy a ADR sa píšu v krátkych, uzavretých slučkách a každý krok sa končí rovnakým rituálom overenia ako predtým. Čísla nižšie hovoria o množstve kódu a testov, nie o tom, koľko z toho prešlo skutočnou prácou.</p>
<figure><img loading="lazy" alt="Rast projektu počas štyroch dní s míľnikmi od M0 po dnes: tisíce riadkov Rustu (od 1,6 do 89,8), počet funkcií #[test] (od 27 do 739) a počet ADR (od 13 do 104), spolu so stĺpcami commitov za deň (39, 38 a 39 za 29., 30. a 31. október)." src="https://aldo.tech/blog/urmqtt-vps-handshake-disk-hardening/img/growth.svg" /><figcaption>Rast projektu počas štyroch dní s míľnikmi od M0 po dnes: tisíce riadkov Rustu (od 1,6 do 89,8), počet funkcií #[test] (od 27 do 739) a počet ADR (od 13 do 104), spolu so stĺpcami commitov za deň (39, 38 a 39 za 29., 30. a 31. október).</figcaption></figure>
<p>Graf ukazuje tri krivky, ktoré rastú takmer spolu, čo je zámer: testy a zapísané rozhodnutia idú popri kóde, nezaostávajú za ním. Sklon je najstrmší 29. októbra, keď vzniká celá cesta od jadra po broker. Neskôr sa riadky pridávajú pomalšie, ale testy rastú rýchlejšie než kód (27 testov na 1,6 tisíc riadkov na začiatku, 739 na 89,8 tisíc dnes).</p>
<table>
<thead>
<tr>
<th>Míľnik</th>
<th>Commit</th>
<th>Čas</th>
<th style="text-align: right;">Riadkov Rustu</th>
<th style="text-align: right;">Testov</th>
<th style="text-align: right;">ADR</th>
</tr>
</thead>
<tbody>
<tr>
<td>M0</td>
<td>9412a9c</td>
<td>28.10. 23:48</td>
<td style="text-align: right;">1 592</td>
<td style="text-align: right;">27</td>
<td style="text-align: right;">13</td>
</tr>
<tr>
<td>M5</td>
<td>a61083b</td>
<td>29.10. 01:32</td>
<td style="text-align: right;">9 314</td>
<td style="text-align: right;">88</td>
<td style="text-align: right;">24</td>
</tr>
<tr>
<td>M9</td>
<td>4be8cc9</td>
<td>29.10. 02:21</td>
<td style="text-align: right;">15 729</td>
<td style="text-align: right;">149</td>
<td style="text-align: right;">28</td>
</tr>
<tr>
<td>M11</td>
<td>4800904</td>
<td>29.10. 03:43</td>
<td style="text-align: right;">22 737</td>
<td style="text-align: right;">180</td>
<td style="text-align: right;">30</td>
</tr>
<tr>
<td>M13</td>
<td>a558d5e</td>
<td>29.10. 05:22</td>
<td style="text-align: right;">26 556</td>
<td style="text-align: right;">193</td>
<td style="text-align: right;">32</td>
</tr>
<tr>
<td>M17</td>
<td>7df4f98</td>
<td>29.10. 08:48</td>
<td style="text-align: right;">33 504</td>
<td style="text-align: right;">232</td>
<td style="text-align: right;">36</td>
</tr>
<tr>
<td>S1</td>
<td>ecf72ec</td>
<td>30.10. 00:46</td>
<td style="text-align: right;">41 910</td>
<td style="text-align: right;">299</td>
<td style="text-align: right;">45</td>
</tr>
<tr>
<td>X8</td>
<td>fb1504a</td>
<td>30.10. 19:08</td>
<td style="text-align: right;">56 341</td>
<td style="text-align: right;">456</td>
<td style="text-align: right;">59</td>
</tr>
<tr>
<td>PERF</td>
<td>76f7c36</td>
<td>31.10. 14:19</td>
<td style="text-align: right;">72 511</td>
<td style="text-align: right;">618</td>
<td style="text-align: right;">76</td>
</tr>
<tr>
<td>dnes</td>
<td>HEAD</td>
<td>31.10. 23:00</td>
<td style="text-align: right;">89 790</td>
<td style="text-align: right;">739</td>
<td style="text-align: right;">104</td>
</tr>
</tbody>
</table>
<p>Riadky sú všetky súbory <code>.rs</code> mimo <code>vendor/</code> a <code>fuzz/</code>, testy sú funkcie <code>#[test]</code>. Čo ktorá časť pridala, podľa dátumu z histórie gitu:</p>
<table>
<thead>
<tr>
<th>Dátum</th>
<th>Míľniky</th>
<th>Čo pribudlo</th>
</tr>
</thead>
<tbody>
<tr>
<td>29.10.</td>
<td>M0 až M17</td>
<td>jadro, sieť, TLS, broker, SQLite, viac jadier, vlastný TCP, spevnenie, reprodukovateľné vydanie</td>
</tr>
<tr>
<td>29.10. večer a 30.10.</td>
<td>X0 až X10, S1, H2, H3 (X0 začína 29.10. o 20:54)</td>
<td>port na x86_64, univerzálny ISO, jeden disk a samoinštalačný ISO, kontroly TLS, útočné klienty</td>
</tr>
<tr>
<td>31.10.</td>
<td>O1 až O4, T1, PERF, H4, H5, H9, S2 až S6</td>
<td>prehľad a konzola cez MQTT, živý režim a diagnostika, SNTP, zrýchlenia, spevnenie, verifikácia, dodávateľský reťazec, úložisko</td>
</tr>
</tbody>
</table>
<h2 id="slub-1-jeden-iso-pre-viac-platforiem">Sľub 1: jeden ISO pre viac platforiem</h2>
<p><strong>Čo sme plánovali.</strong> Jeden súbor, ktorý sa zavádza na aarch64 UEFI, x86_64 UEFI a x86_64 BIOS. <strong>Čo sme postavili.</strong> Port na x86_64 (X0 až X10) a univerzálny ISO s bootloaderom Limine, jeden hybridný ISO, z ktorého sa zavádzajú všetky tieto kombinácie; dymové testy ho zavádzajú na štyroch virtuálnych strojoch. Ponuka má tri položky: urMQTT (živý režim, beží z RAM, nič nezapisuje na disk), inštalácia na disk počítača, na ktorom beží (inštalácia zapíše GPT a dáta a potom stroj vypne) a Diagnostika (detekcia hardvéru a merania).</p>
<figure><img loading="lazy" alt="Zavádzacia ponuka ISO zachytená na VPS: položky „urMQTT&quot;, „Install urMQTT on this machine's disk&quot; a „Diagnostic: hardware detection and benchmarks&quot; s odpočítavaním do automatického zavedenia. Snímka je z testovacieho ISO s dlhším odpočítavaním; predvolená hodnota je 10 sekúnd." src="https://aldo.tech/blog/urmqtt-vps-handshake-disk-hardening/img/boot-menu.png" /><figcaption>Zavádzacia ponuka ISO zachytená na VPS: položky „urMQTT&quot;, „Install urMQTT on this machine's disk&quot; a „Diagnostic: hardware detection and benchmarks&quot; s odpočítavaním do automatického zavedenia. Snímka je z testovacieho ISO s dlhším odpočítavaním; predvolená hodnota je 10 sekúnd.</figcaption></figure>
<p>Prvým skutočným cieľom bol VPS, nie fyzický počítač: rozhodli sme, že prvé vydanie podporuje len VPS (KVM) a bare metal príde po ňom. Testovali sme na VPS u Contabo s procesorom AMD EPYC (rodina 23, Zen 1), 4 vCPU a 8 GiB RAM, so sieťou virtio-net a diskom virtio-scsi. Prvý pokus nás prekvapil: ISO pripojený ako virtuálny CD zaviedol predvolenú položku, produkčný obraz odmietol bežať z média len na čítanie a resetoval sa, takže sme dostali nekonečnú slučku ponuka, jadro, reset bez čohokoľvek čitateľného. Dnes fatálna chyba v produkčnom obraze necháva správu na konzole 60 sekúnd pred resetom a z optického média vypíše, že sa zaviedlo z média len na čítanie, a zastaví sa. Živý režim v RAM (O3) neskôr tento problém pre bežný prípad obišiel.</p>
<p>Diagnostická položka vznikla z tejto skúsenosti: stroj, ktorý nevidíme, sa musíme vedieť opýtať, čo má. Vypíše CPU, RAM, sieť, disky a zariadenia PCI, zmeria priepustnosť pamäte, disk a kryptografiu a tie isté merania vypíše ako JSON na sériový port. Zapisovať na disk sa smie len do nepridelenej oblasti a až po zadaní <code>WIPE</code>; na snímke nižšie sa to preskočilo.</p>
<figure><img loading="lazy" alt="Diagnostická obrazovka na VPS u Contabo (AMD EPYC, KVM, 4 vCPU): systém, pamäť, disk, zariadenia PCI, priepustnosť a latencia RAM a kryptografické merania vrátane 244 us na podanie rúk mTLS, X25519 48 us, podpis ECDSA 35,7 us a overenie 107 us." src="https://aldo.tech/blog/urmqtt-vps-handshake-disk-hardening/img/diag-vps.png" /><figcaption>Diagnostická obrazovka na VPS u Contabo (AMD EPYC, KVM, 4 vCPU): systém, pamäť, disk, zariadenia PCI, priepustnosť a latencia RAM a kryptografické merania vrátane 244 us na podanie rúk mTLS, X25519 48 us, podpis ECDSA 35,7 us a overenie 107 us.</figcaption></figure>
<p><strong>Čo je otvorené.</strong> Skutočný hardvér. Po prvom vydaní príde bare metal (zavádzanie z USB, NVMe a AHCI, sieťová karta skutočného počítača) spolu s IOMMU a Secure Bootom, lebo bez IOMMU môže škodlivé alebo chybné zariadenie zapisovať do celej pamäte. V tejto verzii o tom netvrdíme nič.</p>
<h2 id="podanie-ruk-z-12-ms-na-024-ms">Podanie rúk: z 1,2 ms na 0,24 ms</h2>
<p>Prvé meranie diagnostiky na VPS dalo nepríjemné číslo: podanie rúk mTLS, len kryptografia na strane servera, trvalo 1,2 ms. Na aarch64 sme už používali hardvérové inštrukcie AES a SHA, ale x86_64 nemal nič podobné pre verejný kľúč: RustCrypto je prenosný a constant-time, a teda aj pomalý. Znížili to tri zmeny.</p>
<ul>
<li>Pre P-256 sme prevzali asembler <code>nistz256</code> z OpenSSL a ring (ADR-130), spolu s tabuľkami s pevnou bázou na podpisovanie a overovanie podpisov CA (ADR-110, ADR-111).</li>
<li>X25519 bol prepísaný na štyri 64-bitové končatiny s inštrukciami MULX/ADX (ADR-131).</li>
<li>Potom sme profilovali aj zvyšok (ADR-132, ADR-200).</li>
</ul>
<figure><img loading="lazy" alt="Podanie rúk na VPS: 1200 us predtým, 237 us po asembleri a tabuľkách; porovnanie A/B memcpy v tom istom behu, 278,8 us so starým, 229,4 us s novým; a pred a po pre X25519 (146 na 48 us), podpis ECDSA (673 na 34 us) a overenie (309 na 90 us)." src="https://aldo.tech/blog/urmqtt-vps-handshake-disk-hardening/img/handshake.svg" /><figcaption>Podanie rúk na VPS: 1200 us predtým, 237 us po asembleri a tabuľkách; porovnanie A/B memcpy v tom istom behu, 278,8 us so starým, 229,4 us s novým; a pred a po pre X25519 (146 na 48 us), podpis ECDSA (673 na 34 us) a overenie (309 na 90 us).</figcaption></figure>
<table>
<thead>
<tr>
<th>Meranie na VPS (server, mikrosekundy)</th>
<th style="text-align: right;">Pred</th>
<th style="text-align: right;">Po</th>
</tr>
</thead>
<tbody>
<tr>
<td>podanie rúk mTLS, celá kryptografia</td>
<td style="text-align: right;">1200</td>
<td style="text-align: right;">okolo 240 (237 v meraní z grafu, 244 na snímke)</td>
</tr>
<tr>
<td>X25519</td>
<td style="text-align: right;">146</td>
<td style="text-align: right;">48</td>
</tr>
<tr>
<td>podpis ECDSA</td>
<td style="text-align: right;">673</td>
<td style="text-align: right;">34</td>
</tr>
<tr>
<td>overenie ECDSA</td>
<td style="text-align: right;">309</td>
<td style="text-align: right;">90 (107 na snímke)</td>
</tr>
<tr>
<td>podanie rúk, starý memcpy, rovnaký boot</td>
<td style="text-align: right;">278,8</td>
<td style="text-align: right;">229,4 (-17,7%)</td>
</tr>
</tbody>
</table>
<p>Čísla sa medzi behmi líšia asi o 10%, preto sme zapísali rozsah namiesto jednej hodnoty. Spolu je to približne päťkrát menej času CPU na podanie rúk.</p>
<p>Čo sme sa naučili o memcpy. Keď kryptografia klesla, profil ukázal niečo, čo sme nečakali: funkcie <code>mem*</code> (memcpy, memmove, memset) zaberali 7% podania rúk, 13 až 39% cesty záznamu TLS (39% pre 64 B, 13% pre 4 KiB) a 17 až 34% rozdeľovania PUBLISH. Nahradili sme ich asemblerom SSE2 pre x86_64 (ADR-200), s diferenciálnymi testami voči referenčnej implementácii pre všetky veľkosti, zarovnania a prekrytia. Na volanie je to 55 až 75 cyklov oproti 7 až 12 pre 1 až 64 bajtov.</p>
<figure><img loading="lazy" alt="Vľavo: podiel času servera vo funkciách mem* pred prepísaním (podanie rúk 7%, záznam TLS 64 B 39%, 256 B 37%, 1 KiB 24%, rozdeľovanie 17 až 34%). Vpravo: o koľko je každá cesta rýchlejšia po ňom (podanie rúk 17,7%, 64 B 26,4%, 256 B 19,6%, 1 KiB 16,1%, 4 KiB 6,6%, rozdeľovanie 7 až 34%)." src="https://aldo.tech/blog/urmqtt-vps-handshake-disk-hardening/img/hotpaths.svg" /><figcaption>Vľavo: podiel času servera vo funkciách mem* pred prepísaním (podanie rúk 7%, záznam TLS 64 B 39%, 256 B 37%, 1 KiB 24%, rozdeľovanie 17 až 34%). Vpravo: o koľko je každá cesta rýchlejšia po ňom (podanie rúk 17,7%, 64 B 26,4%, 256 B 19,6%, 1 KiB 16,1%, 4 KiB 6,6%, rozdeľovanie 7 až 34%).</figcaption></figure>
<table>
<thead>
<tr>
<th>Cesta</th>
<th style="text-align: right;">Podiel mem* pred</th>
<th style="text-align: right;">Rýchlejšie po</th>
</tr>
</thead>
<tbody>
<tr>
<td>podanie rúk</td>
<td style="text-align: right;">7%</td>
<td style="text-align: right;">17,7%</td>
</tr>
<tr>
<td>záznam TLS 64 B</td>
<td style="text-align: right;">39%</td>
<td style="text-align: right;">26,4%</td>
</tr>
<tr>
<td>záznam TLS 256 B</td>
<td style="text-align: right;">37%</td>
<td style="text-align: right;">19,6%</td>
</tr>
<tr>
<td>záznam TLS 1 KiB</td>
<td style="text-align: right;">24%</td>
<td style="text-align: right;">16,1%</td>
</tr>
<tr>
<td>záznam TLS 4 KiB</td>
<td style="text-align: right;">13%</td>
<td style="text-align: right;">6,6%</td>
</tr>
<tr>
<td>rozdeľovanie správ</td>
<td style="text-align: right;">17 až 34%</td>
<td style="text-align: right;">7 až 34%</td>
</tr>
</tbody>
</table>
<p>Čítanie grafu: čím menší záznam, tým väčšia časť času ide na kopírovanie, a preto je aj zisk väčší. Pre malé správy, ktorých je v premávke MQTT väčšina, to je to, na čom záleží.</p>
<p>Čo to neznamená. Na natívnom x86_64 sme OpenSSL 3.5.5 merali len ako referenciu pre asembler (ADR-130), nie ako porovnanie produktov. Porovnanie Linux plus Mosquitto na tom istom VPS (úloha X11) sme ešte neurobili. Preto netvrdíme, že urMQTT je rýchlejší než Linux: nie je to zmerané. Čísla vyššie porovnávajú urMQTT sám so sebou.</p>
<p>Preťaženie bolo druhým výsledkom. Na VPS, z jedného Macu (RTT 35 ms), 128 klientov dalo 618 podaní rúk za sekundu, 256 klientov 393 a 512 klientov 67, s tisíckami odmietnutých spojení. Nebola to plošina, ale zrútenie. Príčiny sme našli v kóde (dve sú potvrdené v QEMU/HVF, na VPS sa nedali zopakovať), viacero naraz: odmietnuté spojenie vracalo klienta cez ďalší segment, ktorý nesie platnú SYN cookie, odmietnutie pomocou RST hovorilo klientovi, aby to hneď skúsil znova, a príjem bol O(rad). Oprava (ADR-080 až ADR-082) zahadzuje SYN, ktorý by nebol prijatý, pamätá si odmietnuté toky, drží príjem v O(1) a počíta podania rúk na jadro. Je možné (hypotéza, neoverené), že časť príčiny bola aj na klientovi: nástroj na záťaž zatváral spojenie ako prvý, takže jeden Mac so 16 384 efemérnymi portmi a TIME_WAIT 30 sekúnd je obmedzený na približne 550 spojení za sekundu. Preto skutočný strop servera sme ešte nezmerali.</p>
<h2 id="slub-2-surovy-disk-ale-opatrny">Sľub 2: surový disk, ale opatrný</h2>
<p><strong>Čo sme plánovali.</strong> Zachovať vlastné rozloženie bez súborového systému a pridať všetko, čo súborový systém inak dáva: zarovnanie, rast, discard, zálohy a kontroly. <strong>Prečo nie súborový systém.</strong> Diskutovali sme o tom a rozhodnutie sa potvrdilo: urMQTT zapisuje jednu databázu, jeden telemetrický denník a vlastné metaúdaje, nepotrebuje adresáre, oprávnenia ani názvy súborov, a každá ďalšia vrstva by bola ďalší kód v dôveryhodnej časti a ďalší spôsob, ako môže zápis ticho zmiznúť. Disk je GPT s partíciou ESP a jednou dátovou partíciou urMQTT, na ktorej je vlastné rozloženie.</p>
<p><strong>Čo sme postavili (S2 až S6).</strong></p>
<ul>
<li><strong>Rozloženie v2 (S2).</strong> Topológia disku, zarovnanie na 1 MiB alebo optimálnu veľkosť I/O, celé fyzické bloky, pár superblokov A/B s generáciou (ADR-140). Rozloženie v1 jadro odmieta s jasnou správou a migruje ho offline nástroj <code>urmqtt-disk migrate</code> (ADR-141): predvolený je skúšobný prechod, kopíruje sa a až potom prepína, s návratom a vstrekovaním chýb pri každom zápise.</li>
<li><strong>Rast (S3).</strong> Pri zavádzaní môže disk rásť (nikdy sa nezmenšuje) a na virtio-blk aj počas behu, cez prerušenie zmeny konfigurácie (ADR-142, ADR-143).</li>
<li><strong>Discard a write-zeroes (S4).</strong> Tenko alokované disky vracajú miesto hostiteľovi. V teste klesol súbor qcow2 na hostiteľovi zo 62 na 16 MiB po vypršaní telemetrických segmentov a obnova nikdy neverí obsahu, ktorý bol zahodený.</li>
<li><strong>Záloha databázy na disku (S5).</strong> Kópia A/B pomocou online backup API SQLite v krokoch po 16 stránok: najdlhší krok 4,6 ms oproti 445 ms pri kopírovaní jedným volaním. Commity počas kopírovania mali maximum 2,4 ms oproti 2,2 ms bez nej. Ak <code>quick_check</code> pri zavádzaní zlyhá, databáza sa obnoví z kópie.</li>
<li><strong>Čistenie a obmedzené zavádzanie (S6).</strong> Scrubber číta disk s obmedzeným rozpočtom I/O a hlási poškodené záznamy, a zavádzanie číta len najnovší segment každého oddielu, takže čas zavádzania nerastie s veľkosťou telemetrickej oblasti. Tým sme uzavreli problém, ktorý sme vopred vypočítali: na veľkom disku by úplná obnova trvala od minút po desiatky minút.</li>
</ul>
<p>Celá skupina dymových testov úložiska prechádza na oboch architektúrach: 79 úspešných, 0 neúspešných, na aarch64 pod HVF a na x86_64 pod TCG. Merali sme aj to, že S4 databázu nespomalil: 53,3 tisíc vložení za sekundu v dávkach pred aj po, bez meratelného rozdielu.</p>
<p><strong>Čo prekvapilo.</strong> Koľko z „jednoduchého" rozloženia tvoria okrajové prípady: virtio-scsi a virtio-blk hlásia topológiu rôznymi spôsobmi a nedokončený zápis uprostred migrácie musí vždy zanechať aspoň jednu platnú verziu. Preto každý zápis na ceste migrácie a rastu prešiel vstrekovaním chýb.</p>
<p><strong>Čo je otvorené.</strong> Stránky SQLite nemajú kontrolný súčet, takže zmenený bit vnútri hodnoty v riadku databáza neodhalí; telemetrické záznamy a obsah kópií majú CRC a odhalia sa. Šifrovanie disku (S7) nie je hotové.</p>
<h2 id="slub-3-dokazatelne-bezpecne-nakolko-sa-nam-to-podari">Sľub 3: dokázateľne bezpečné, nakoľko sa nám to podarí</h2>
<p><strong>Spevnenie (H4).</strong> Alokátor maže uvoľnenú pamäť a kontroluje ju pri opätovnom použití, odhaľuje dvojité uvoľnenie, viaže ukazovatele voľného zoznamu cez XOR a pri uvoľnení nuluje kľúče (ADR-170). Ochrana zásobníka (<code>-Zstack-protector=strong</code>, 1315 funkcií, a SQLite s <code>-fstack-protector-strong</code>, ADR-171). Hlásenia o chybách v produkcii neobsahujú registre ani adresy (ADR-172). KASLR na oboch architektúrach: jadro je PIE a Limine ho posúva asi o 19 bitov, náhodne sa umiestňujú heap, zásobníky a okno MMIO (ADR-173). Na aarch64 MTE označuje heap (ADR-174), ale len pod TCG: HVF a x86 ho nemajú. kCFI sme posúdili a neprijali, lebo prekladač na pripnutom nightly padá s vnútornou chybou; IOMMU je odložený spolu s bare metalom (ADR-175, ADR-176).</p>
<p><strong>Cena je zmeraná, nie predpokladaná.</strong> Na ceste podania rúk (<code>dp-bench</code>, aarch64 HVF, plné podania rúk TLS 1.3 + mTLS + CONNECT):</p>
<table>
<thead>
<tr>
<th>Krok</th>
<th style="text-align: right;">1 jadro</th>
<th style="text-align: right;">4 jadrá</th>
<th>Zmena</th>
</tr>
</thead>
<tbody>
<tr>
<td>bez spevnenia</td>
<td style="text-align: right;">1941</td>
<td style="text-align: right;">7620</td>
<td></td>
</tr>
<tr>
<td>+ alokátor</td>
<td style="text-align: right;">1898</td>
<td style="text-align: right;">7429</td>
<td>-2,2% až -2,5%</td>
</tr>
<tr>
<td>+ ochrana zásobníka</td>
<td style="text-align: right;">1845</td>
<td style="text-align: right;">7243</td>
<td>spolu -4,9% až -5%</td>
</tr>
<tr>
<td>+ PIE a značky</td>
<td style="text-align: right;">okolo 1800</td>
<td style="text-align: right;">okolo 6750</td>
<td>na zaťaženom počítači, zašumené</td>
</tr>
</tbody>
</table>
<p>Celkovo je teda cena na ceste podania rúk približne 5% pre časti merané čisto, a s PIE a značkami vychádza okolo 7% na jednom a okolo 11% na štyroch jadrách, ale toto meranie je zašumené. Považujeme ju za prijateľnú, no neopakovali sme ju na VPS s x86_64.</p>
<p>Oplatí sa porovnať s číslom z prvého textu (862 podaní rúk za sekundu na jednom jadre). Ten istý nástroj dnes, s ovládačom hardvérového šifrovania na aarch64 (X0b) a radom zrýchlení, ktoré od tej doby pribudli, dáva 1941 pred spevnením. Neizolovali sme príspevok každej zmeny, takže o príčine netvrdíme nič.</p>
<p><strong>Ako je to overené, a čo nie.</strong> H5 pridal Miri na podmnožinu hostiteľských balíkov, dôkazy Kani, sanitizery, model-fuzzer pre ACL a správy o pokrytí. Kani dokázal <code>record_len</code> v TLS, <code>parse</code> v TCP a dve aritmetiky kolesa časovačov. <strong>Nedokázané</strong> (vypršal čas): oba dekodéry MQTT, obe parsovania konfigurácie a správanie kolesa časovačov; úloha CI pre Kani preto zatiaľ neblokuje. Miri nevie vykonávať asembler. Dodávateľský reťazec (H9): CycloneDX SBOM s 215 komponentmi, <code>cargo-vet</code> prechádza s 39 plne preskúmanými a 140 výnimkami a cargo-deny je nastavený tak, aby upozornenia na zraniteľnosti považoval za pád CI (tieto úlohy CI sa zatiaľ overili len lokálne, nie na GitHube, a negatívny test sa nespustil), spolu s <code>VULNERABILITY-HANDLING.md</code>. Úprimný stav: zo 67 verzií cudzích balíkov zlinkovaných do jadra je 53 výnimiek, nie revízií. Vet dokazuje, že sa množina závislostí nemôže zmeniť bez viditeľného rozdielu, nie že TCB bola preskúmaná. Vlastnú revíziu žiadneho balíka sme nenapísali.</p>
<p>SNTP (T1) je v tomto zmysle malý príbeh o bezpečnosti: je voliteľný, len voči súkromným serverom IPv4, s maximálnym skokom a podmienkou, že sa zdroje zhodujú (medián, väčšina v rámci jednej sekundy). Hodiny, ktoré môže ktokoľvek na ceste posunúť, nie sú hodiny, ktorým smieme veriť pri platnosti certifikátov.</p>
<h2 id="na-macu-aarch64-nativne-a-x86_64-emulaciou">Na Macu: aarch64 natívne a x86_64 emuláciou</h2>
<p>Vyvíjame na Macu a ten má dva spôsoby, ako spustiť ten istý kód, a sú veľmi odlišné.</p>
<table>
<thead>
<tr>
<th></th>
<th>aarch64 pod HVF</th>
<th>x86_64 pod TCG</th>
</tr>
</thead>
<tbody>
<tr>
<td>Čo to je</td>
<td>natívne vykonávanie, rýchlosť blízka skutočnému procesoru</td>
<td>QEMU emuluje inštrukcie</td>
</tr>
<tr>
<td>Podanie rúk v hosťovi</td>
<td>okolo 0,58 ms (prvýkrát merané okolo M13); <code>dp-bench</code> dnes 1941/s na jadro</td>
<td>okolo 3,3 ms</td>
</tr>
<tr>
<td>Scenáre dymových testov TLS</td>
<td>rýchle</td>
<td>okolo 15 minút na scenár</td>
</tr>
<tr>
<td>MTE</td>
<td>nie (na HVF ho niet)</td>
<td>x86 ho nemá; označovanie heapu funguje len pre hosťa aarch64 pod TCG</td>
</tr>
<tr>
<td>Načo slúži</td>
<td>skutočné merania</td>
<td>správnosť: cesta x86 sa overuje, čísla nie sú výkon</td>
</tr>
</tbody>
</table>
<p>Pravidlo je jednoduché: čísla pod TCG sú emulácia a necitujeme ich ako výkon. Výkon x86 meriame len na skutočnom VPS. Preto kryptografické čísla vyššie pochádzajú z VPS u Contabo, nie z Macu.</p>
<h2 id="lekcie">Lekcie</h2>
<ul>
<li><strong>Merajte na skutočnom stroji skôr, než začnete optimalizovať.</strong> Prvý prístup bol optimalizovať to, o čom sme si mysleli, že je pomalé; až diagnostika na VPS ukázala 1,2 ms a kam presne odchádza čas.</li>
<li><strong>Preťaženie je samostatná vlastnosť.</strong> Systém, ktorý je rýchly pod záťažou, sa môže zrútiť nad ňou; odmietnutie musí byť lacnejšie než prijatie.</li>
<li><strong>Profil prekvapí.</strong> Najväčší jednotlivý zisk pre malé správy nebol v kryptografii, ale v kopírovaní pamäte.</li>
<li><strong>Disk bez súborového systému chce viac práce, nie menej.</strong> To, čo súborový systém inak dáva (zarovnanie, rast, discard, kópie, kontroly), musíme napísať a overiť sami, každý krok so vstrekovaním chýb.</li>
<li><strong>Úprimnosť v číslach.</strong> Merania pamäte na zdieľanom VPS kolíšu až 10-násobne medzi behmi a o kóde nehovoria nič, preto ich neuvádzame. Kryptografické čísla sú stabilné do asi 10%.</li>
<li><strong>Rýchlosť vývoja nie je zrelosť.</strong> Testy a ADR rastú s kódom, ale štyri dni nie sú skutočná prevádzka.</li>
</ul>
<h2 id="co-bude-dalej">Čo bude ďalej</h2>
<ul>
<li><strong>H6:</strong> SQLite izolovaný na úrovni EL0, aby chyba v databáze neotvorila jadro.</li>
<li><strong>H7:</strong> zvyšok bezpečnosti času (NTS a spracovanie skokov); SNTP je len prvý krok.</li>
<li><strong>X11:</strong> porovnanie s Linuxom a Mosquittom na tom istom VPS, až vtedy má zmysel hovoriť o relatívnej rýchlosti.</li>
<li><strong>Vydanie:</strong> prvé verejné vydanie pre VPS (KVM), s podpísanými artefaktmi a návodmi.</li>
<li><strong>Po vydaní:</strong> bare metal (X12 až X16), IOMMU a Secure Boot a šifrovanie disku.</li>
</ul>
<p>Kód je na <a href="https://github.com/zeljan-alduk/urMQTT">github.com/zeljan-alduk/urMQTT</a> a <code>docs/PROGRESS.md</code> je naďalej jediným zdrojom pravdy o tom, čo naozaj funguje. Pred-alfa, ako predtým; hlásenia chýb a bezpečnostné hlásenia podľa pokynov v <code>SECURITY.md</code> sú vítané.</p>]]></content:encoded>
      <category>rust</category>
      <category>mqtt</category>
      <category>iot</category>
      <category>unikernel</category>
      <category>open source</category>
    </item>
    <item>
      <title>urMQTT: budujeme MQTT broker, ktorý je zároveň operačným systémom</title>
      <link>https://aldo.tech/sk/blog/urmqtt-unikernel-mqtt-broker/</link>
      <guid isPermaLink="true">https://aldo.tech/sk/blog/urmqtt-unikernel-mqtt-broker/</guid>
      <pubDate>Thu, 29 Oct 2026 08:00:00 +0000</pubDate>
      <description>Vývojárske poznámky o urMQTT, unikernel napísanom v Ruste, ktorý je sám osebe MQTT broker: jeden ELF s vlastným TCP/IP, TLS 1.3, brokerom a SQLite, spustený z jedného ISO.</description>
      <content:encoded><![CDATA[<p>Zariadenia vo vozidlách, na parkoviskách a v skriniach s elektronikou majú jednu spoločnú vlastnosť: posielajú malé správy, zriedka, ale je ich veľa a sú neustále pripojené. Roky ich pripájame na MQTT brokery a roky nás trápi tá istá otázka. Prečo na niečo také jednoduché potrebujeme celý Linux, OpenSSL, runtime nejakého jazyka, SSH, správcu balíkov a až potom broker? Každá z týchto vrstiev prináša vlastné zraniteľnosti, spotrebúva vlastnú pamäť a pridáva ďalšiu vec, ktorá sa môže pokaziť, a flotila zariadení od servera potrebuje len tri veci: aby držal veľa takmer nečinných spojení, aby spoľahlivo overil totožnosť každého zariadenia a aby preniesol správy bez straty.</p>
<p>Tak vznikol <a href="https://github.com/zeljan-alduk/urMQTT">urMQTT</a>: unikernel, ktorý <em>je</em> MQTT broker. Jadro, TCP/IP, TLS 1.3, MQTT a vstavaná databáza v jedinom binárnom programe v Ruste, ktorý sa spúšťa z jedného ISO. Žiadne procesy, žiadne systémové volania, žiadny shell. Prvou platformou, na ktorej sa buduje a testuje, je aarch64, a ten istý kód smeruje na x86_64, cez UEFI aj klasický BIOS, a na skutočný hardvér: cieľom je jeden ISO, ktorý sa zavedie na všetkých z nich. Projekt je open source pod licenciou MIT alebo Apache 2.0 a v tomto texte chceme zapísať, kam sme sa dostali, ako je vo vnútri poskladaný a čo sme sa popritom naučili.</p>
<h2 id="co-presne-znamena-unikernel">Čo presne znamená „unikernel"</h2>
<p>Bežný broker je program, ktorý beží na operačnom systéme. urMQTT pod sebou operačný systém nemá: samo sa zavedie cez UEFI firmware a bootloader Limine, nastaví si vlastné tabuľky stránok, naprogramuje kontrolér prerušení a časovač, nájde zariadenia virtio cez PCIe, získa adresu cez DHCP a počúva na jedinom porte. Všetko, čo by inak robilo jadro, ovládače, sieťový stack a aplikácia, je tu jeden ELF, ktorý beží na úrovni EL1.</p>
<p>Porovnanie s typickou inštaláciou hovorí samo za seba:</p>
<table>
<thead>
<tr>
<th></th>
<th>Typický broker na Linuxe</th>
<th>urMQTT</th>
</tr>
</thead>
<tbody>
<tr>
<td>Vystavené služby</td>
<td>SSH, broker, často aj port pre metriky alebo administráciu</td>
<td><strong>iba TCP 8883</strong> (MQTT cez TLS 1.3 s mTLS)</td>
</tr>
<tr>
<td>Kód v obraze</td>
<td>jadro, userland, libc, OpenSSL, runtime</td>
<td><strong>jeden ELF v Ruste</strong> (plus vendorovaný SQLite)</td>
</tr>
<tr>
<td>Vzdialená správa</td>
<td>shell, konfiguračné súbory, aktualizácie balíkov</td>
<td><strong>nič.</strong> Nová verzia je nový ISO</td>
</tr>
<tr>
<td>Pamäť na nečinné spojenie</td>
<td>kilobajty (vyrovnávacie pamäte socketov, objekty TLS, heap)</td>
<td><strong>cieľ ≤ 512 B</strong></td>
</tr>
<tr>
<td>Viacjadrový model</td>
<td>vlákna a zámky alebo jedna slučka udalostí</td>
<td><strong>vlákno na jadro, bez zdieľaného stavu, spracovanie do konca</strong></td>
</tr>
<tr>
<td>Integrita kódu</td>
<td>sú možné mapovania, ktoré sú zapisovateľné aj spustiteľné</td>
<td><strong>W^X, zamknuté tabuľky stránok, PAC/BTI</strong></td>
</tr>
</tbody>
</table>
<p>Priority sú v tomto poradí: bezpečnosť, škálovateľnosť, viacjadrový výkon, malá stopa a nemenná inštalácia. Keď sú dva ciele v konflikte, vyhráva ten, ktorý je v tomto zozname vyššie.</p>
<h2 id="kde-sme-teraz">Kde sme teraz</h2>
<p>Projekt je vo fáze pred-alfa, ale uzavretých je sedemnásť vývojových míľnikov, od M0 po M17. Dnes urMQTT obsluhuje MQTT 3.1.1 cez TLS 1.3 s povinnými klientskymi certifikátmi a stav uchováva v SQLite na surovom disku. Stručne, čo už funguje:</p>
<ul>
<li><strong>Jadro:</strong> vlastné vektory výnimiek s dekódovanými hláseniami, vlastné tabuľky stránok s vynúteným W^X, ochranné stránky, PAC a BTI tam, kde ich procesor má, GICv3 a GICv2, generický časovač, SMP až do 64 jadier.</li>
<li><strong>Zisťovanie hardvéru</strong> z DTB alebo ACPI do jedného popisu platformy, s identickým výsledkom z oboch zdrojov.</li>
<li><strong>Ovládače:</strong> PCIe ECAM, MSI-X, virtio-net, virtio-blk, virtio-scsi a virtio-rng, a CSPRNG zasiaty hardvérovou entropiou.</li>
<li><strong>Sieť:</strong> vlastný DHCPv4, ARP, ICMP a prísny IPv4, potom vlastný TCP len pre serverovú stranu: SYN cookies, hierarchické koleso časovačov, NewReno, spájanie ACK, bez stavu TIME_WAIT.</li>
<li><strong>TLS:</strong> iba TLS 1.3, povinné mTLS, identita zariadenia z CN alebo SAN URI; podanie rúk robí rustls a po ňom vlastná vrstva záznamov s AES-128-GCM a ChaCha20-Poly1305. Obnovenie relácie lístkami PSK, zoznam odvolaní, riadenie prijatia už pri podaní rúk.</li>
<li><strong>MQTT:</strong> kodek bez kopírovania, fuzzovaný a testovaný s pravými klientmi mosquitto; QoS 0 a 1, zástupné znaky, zachované správy, závet (LWT), trvalé relácie, ACL s obmedzením na <code>devices/{id}/#</code>, obmedzenia rýchlosti na zariadenie.</li>
<li><strong>Úložisko:</strong> SQLite vnútri jadra cez VFS nad surovým diskom, v režime WAL, s kontrolou integrity pri zavádzaní; register zariadení, odvolania, zachované správy, trvalé relácie s frontmi správ pre zariadenia, ktoré sú offline; na každé jadro samostatný telemetrický denník s CRC na záznam a obnovou po páde.</li>
<li><strong>Prevádzka a dohľad:</strong> strážny časovač pre obslužné rutiny, kontrola stavu, obmedzené logovanie, syslog a statsd cez UDP, prijatie, ktoré prežije búrky opätovných pripojení.</li>
<li><strong>Dodanie:</strong> bitovo identické opakované zostavenia overené v CI, ISO a surový GPT obraz disku, podpisy minisign, návody pre KVM, Proxmox a Hetzner.</li>
</ul>
<p>Každý krok je označený míľnikom, ktorý ho priniesol, a súbor <code>docs/PROGRESS.md</code> v repozitári je jediným zdrojom pravdy o tom, čo naozaj funguje.</p>
<h2 id="architektura-v-troch-obrazkoch">Architektúra v troch obrázkoch</h2>
<p>Celý návrh je popísaný v <code>docs/ARCHITECTURE.md</code> a dôvody každého dôležitejšieho rozhodnutia sú zapísané ako ADR v <code>docs/DECISIONS.md</code>. Tu je prehľad.</p>
<h3 id="vrstvy">Vrstvy</h3>
<figure><img loading="lazy" alt="Vrstvy urMQTT: od brokera cez kodek MQTT, TLS, TCP a sieť až po ovládače virtio a architektonickú vrstvu, všetko v jedinom ELF na úrovni EL1." src="https://aldo.tech/blog/urmqtt/img/layers.svg" /><figcaption>Vrstvy urMQTT: od brokera cez kodek MQTT, TLS, TCP a sieť až po ovládače virtio a architektonickú vrstvu, všetko v jedinom ELF na úrovni EL1.</figcaption></figure>
<p>Zhora nadol: broker (relácie, strom tém, QoS, zachované správy, závety, ACL, obmedzenia), kodek MQTT, TLS, TCP, sieťová vrstva a potom ovládače. Popri tejto hlavnej ceste stojí úložisko (SQLite a telemetrický denník) a pod všetkým správa pamäte, zisťovanie platformy a konfigurácia, ktorá je v čase zostavenia preložená do konštánt, spolu so vstavaným certifikátom CA, serverovým certifikátom a kľúčom. Hypervízor so zariadeniami virtio je jediné, čo je mimo obrazu.</p>
<h3 id="vlakno-na-jadro-bez-zdielaneho-stavu">Vlákno na jadro, bez zdieľaného stavu</h3>
<figure><img loading="lazy" alt="Model vykonávania: každé dátové jadro má svoju slučku a svoj diel brokera a najvyššie jadro spúšťa iba SQLite. Jadrá komunikujú výhradne prstencami SPSC." src="https://aldo.tech/blog/urmqtt/img/cores.svg" /><figcaption>Model vykonávania: každé dátové jadro má svoju slučku a svoj diel brokera a najvyššie jadro spúšťa iba SQLite. Jadrá komunikujú výhradne prstencami SPSC.</figcaption></figure>
<p>Každé dátové jadro točí vlastnú slučku s vlastným zásobníkom TCP, stavmi TLS, dielom brokera (relácie a strom tém), vyrovnávacou pamäťou heapu a oddielom telemetrie. Jadrá nezdieľajú žiadny meniteľný stav, iba ohraničené prstence bez zámkov, po jednom pre každý usporiadaný pár jadier. Najvyššie jadro spúšťa SQLite a nič iné.</p>
<p>Sieťová karta má dnes jeden pár front, ktorý drží jadro 0. To každý tok MQTT podľa hashu Toeplitz jeho štvorice nasmeruje na jedno z pracovných jadier a posiela to, čo pracovné jadrá vrátia. Relácia žije na svojom domovskom jadre, určenom hashom identifikátora klienta, takže prevzatie, závety a trvanlivosť nikdy neprekračujú hranice jadier. Spojenie, ktoré pristane na inom jadre, si tam ponechá svoj TCP a TLS, a bežný MQTT preposiela domovskému jadru s riadením toku založeným na kreditoch. Každá smerovaná správa sa rozosiela aj na ostatné diely brokera, z ktorých každý drží repliku zachovaných správ.</p>
<p>Tento model drží pohromade tri pravidlá. <strong>Spracovanie do konca:</strong> prijatý paket sa spracuje celý na jednom jadre, od TCP cez dešifrovanie TLS na mieste, parsovanie MQTT a smerovanie až po šifrovanú odpoveď, bez frontov úloh a bez kopírovania do aplikačných vyrovnávacích pamätí. <strong>Prispôsobivé vzorkovanie:</strong> pri záťaži sa fronty na príjem aktívne vzorkujú a v pokoji sa jadro vracia k prerušeniam a <code>WFI</code>; obslužné rutiny prerušení iba potvrdia prerušenie a označia prácu. <strong>Strážny časovač:</strong> tik 10 ms na jadro zachytí obslužné rutiny, ktoré prekročia svoj rozpočet.</p>
<h3 id="zivot-jedneho-publish">Život jedného PUBLISH</h3>
<figure><img loading="lazy" alt="Cesta jednej správy PUBLISH s QoS 1: od záznamu TLS na sieťovej karte cez dešifrovanie, kontrolu ACL a smerovanie až po PUBACK spojený s TCP ACK v jedinom segmente." src="https://aldo.tech/blog/urmqtt/img/publish.svg" /><figcaption>Cesta jednej správy PUBLISH s QoS 1: od záznamu TLS na sieťovej karte cez dešifrovanie, kontrolu ACL a smerovanie až po PUBACK spojený s TCP ACK v jedinom segmente.</figcaption></figure>
<p>Zariadenie pošle záznam TLS. Sieťová karta ho odovzdá slučke jadra deskriptorom, bez kopírovania. Slučka skontroluje TCP, dešifruje TLS na mieste a parsuje MQTT z požičanej vyrovnávacej pamäte, potom v pamäti skontroluje ACL, obmedzenie rýchlosti a cestu stromom tém. Ak sa iné jadro zaujíma o túto tému, dostane správu na smerovanie s odkazom na obsah a identifikátorom témy; databáza dostane telemetrický záznam alebo aktualizáciu zachovanej správy, v balíku. Odpoveď PUBACK sa zašifruje priamo do odchádzajúcej vyrovnávacej pamäte a spojí sa s TCP ACK, takže zariadeniu odíde jeden segment. Iné jadro stojí správa jedno šifrovanie na odberateľa v okamihu odoslania.</p>
<h2 id="kolko-stoji-jedno-spojenie">Koľko stojí jedno spojenie</h2>
<p>Cieľom je <strong>najviac 512 bajtov na nečinné spojenie TLS</strong>, s ambíciou 256. Kým spojenie odpočíva, nič sa pre ňu nedrží vo vyrovnávacej pamäti: prijaté dáta sa spracúvajú priamo zo sieťového rámca a vyrovnávacie pamäte na odosielanie, záznamy a MQTT existujú len dovtedy, kým je niečo nedokončené alebo nepotvrdené. Jadro to aj samo meria: presné počítadlá heapu v požadovaných bajtoch aj v blokoch, ktoré zaberajú, zapisované každých desať sekúnd ako heap nad základnou úrovňou na otvorené spojenie.</p>
<table>
<thead>
<tr>
<th>Časť</th>
<th style="text-align: right;">Veľkosť</th>
<th>Ako</th>
</tr>
</thead>
<tbody>
<tr>
<td>Riadiaci blok TCP</td>
<td style="text-align: right;">96 B</td>
<td>zabalené príznaky, 16-bitové RTT/RTO, slot v kolese časovačov, bez vyrovnávacích pamätí v pokoji</td>
</tr>
<tr>
<td>Slot v tabuľke TCP</td>
<td style="text-align: right;">4–8 B</td>
<td>iba index spojenia, tabuľka najviac na polovicu plná</td>
</tr>
<tr>
<td>Spojenie a stav záznamov TLS 1.3</td>
<td style="text-align: right;">152 B</td>
<td>tajomstvo a poradové číslo na smer (40 B); kľúč a IV sa odvodzujú na záznam</td>
</tr>
<tr>
<td>Spojenie MQTT</td>
<td style="text-align: right;">48 B</td>
<td>identita a závet na heape iba dovtedy, kým existujú</td>
</tr>
<tr>
<td>Relácia MQTT, horúca podoba</td>
<td style="text-align: right;">64 B</td>
<td>jeden odber v riadku, internované identifikátory uzlov</td>
</tr>
<tr>
<td>Odber <code>devices/&lt;id&gt;/cmd/#</code></td>
<td style="text-align: right;">0 uzlov stromu</td>
<td>uložený relatívne voči identifikátoru relácie, spoločný všetkým zariadeniam</td>
</tr>
</tbody>
</table>
<p>Zmerané pod HVF so štyrmi virtuálnymi jadrami, z toho tri pracovné, pričom každé zariadenie drží spojenie mTLS a jeden odber s QoS 1, vrátane zaokrúhľovania alokátora:</p>
<table>
<thead>
<tr>
<th style="text-align: right;">Spojenia</th>
<th style="text-align: right;">Bajtov na spojenie</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right;">960</td>
<td style="text-align: right;">531 B</td>
</tr>
<tr>
<td style="text-align: right;">9 922</td>
<td style="text-align: right;">494 B</td>
</tr>
<tr>
<td style="text-align: right;">10 197</td>
<td style="text-align: right;">474 B</td>
</tr>
</tbody>
</table>
<p>Fond sieťových rámcov je pevný, okolo 2 MiB, čo je zhruba 200 B na spojenie pri desaťtisíc spojeniach a okolo 2 B pri milióne. Viac sme zmerať nedokázali: používateľská sieť QEMU na macOS prestala prijímať spojenia pri 10 197, zhruba na hranici 10 240 deskriptorov procesu QEMU. Číslo klesá, keď sa pevné časti rozdeľujú na viac spojení, takže s ich počtom nerastie.</p>
<h2 id="bezpecnostny-model">Bezpečnostný model</h2>
<p>Bezpečnosť je prvou prioritou a vyhráva všetky konflikty s ostatnými cieľmi. Model má niekoľko vrstiev:</p>
<ul>
<li><strong>Jeden port.</strong> TCP 8883, iba TLS 1.3, povinné klientske certifikáty podpísané CA vstavanou do obrazu. Identifikátor klienta MQTT sa musí rovnať identifikátoru zariadenia z certifikátu.</li>
<li><strong>Žiadny vstup.</strong> Žiadny shell, konzolový vstup, SSH, administračné API, dynamický kód, SQL dostupný cez sieť ani súborový systém.</li>
<li><strong>Pamäťová bezpečnosť.</strong> Rust, s <code>forbid(unsafe_code)</code> v protokolových balíkoch; každý blok <code>unsafe</code> má komentár <code>// SAFETY:</code>, ktorý kontroluje lint, a na vonkajší vstup neexistuje panika: clippy v balíkoch na dátovej ceste zakazuje <code>unwrap</code>, <code>expect</code>, indexovanie a <code>panic!</code>.</li>
<li><strong>Zmierňovanie zneužitia.</strong> W^X sa vynucuje kódovaním deskriptorov a bitom <code>SCTLR_EL1.WXN</code> a kontroluje sa pri každom zavedení. Tabuľky stránok sú po zavedení iba na čítanie, bez zapisovateľného aliasu. Dolná polovica adresného priestoru je vypnutá. Každý zásobník má ochranné stránky a výnimky bežia na samostatnom zásobníku. PAC a BTI sa zapínajú, keď ich procesor podporuje, a overujú to dymové testy.</li>
<li><strong>Odolnosť voči zneužitiu.</strong> SYN cookies; prijatie odmieta nové spojenia, ešte pred akoukoľvek kryptografiou, keď je heap nízky, keď databáza zaostáva, keď je príliš veľa podaní rúk v priebehu na jadro alebo na zdrojovú adresu, alebo keď je rýchlosť podaní rúk príliš vysoká. Obmedzenia rýchlosti na zariadenie, ohraničené fronty, okno TCP viazané na spätný tlak brokera, obmedzené logovanie, strážny časovač a kontrola stavu.</li>
<li><strong>Nemennosť.</strong> Konfigurácia, CA a serverový kľúč sú vstavané do obrazu. Zmena čohokoľvek znamená nový obraz.</li>
</ul>
<p>Model hrozieb je zapísaný v <code>docs/SECURITY.md</code>.</p>
<h2 id="ulozisko-bez-suboroveho-systemu">Úložisko bez súborového systému</h2>
<p>Dáta žijú na samostatnom disku, virtio-blk alebo virtio-scsi, na ktorom nie je súborový systém. Rozloženie je minimálne a vlastné:</p>
<pre><code>LBA 0      [ superblok: magické číslo · verzia rozloženia · uuid · tabuľka oblastí · CRC-32C ]
LBA 8, 16  [ tabuľka veľkostí súborov, sloty A/B (vyhráva najnovší platný) ]
LBA 64     [ SQLite hlavná databáza ½ ][ SQLite WAL ⅛ ][ denník 1 MiB ][ telemetrický denník: 8 oddielov … ]
posledné 4 KiB [ priestor na samokontrolu blokového zariadenia pri zavádzaní ]
</code></pre>
<p>SQLite (vendorovaný 3.54.1, s kontrolou odtlačku pri zostavení) je preložený do jadra pomocou clang, bez štandardnej knižnice, a používa pevný heap <code>memsys5</code>. Jeho VFS mapuje tri pevné súbory na oblasti disku. Beží v režime WAL s <code>locking_mode=EXCLUSIVE</code>, takže nepotrebuje index v zdieľanej pamäti, a so <code>synchronous=FULL</code>; ak <code>quick_check</code> pri zavádzaní nájde chybu, systém sa bezpečne zastaví. V databáze sú register zariadení, odvolania, zachované správy a trvalé relácie s frontmi správ QoS 1 pre zariadenia, ktoré nie sú pripojené.</p>
<p>Jadro pre databázu, to s najvyšším číslom, spúšťa SQLite a nič iné. Dátová rovina mu posiela zmeny stavu cez prstenec SPSC bez zámkov; ono ich zbiera do jednej transakcie každých 5 ms alebo každých 512 operácií a potvrdenia vracia druhým prstencom. Prázdny disk sa naformátuje pri prvom zavedení; nič iné sa nikdy nepreformátuje.</p>
<p>Telemetria ide inou cestou. Každý prijatý PUBLISH na témach zo <code>storage.telemetry_topics</code> odchádza do oddielu telemetrického denníka svojho jadra: prstenec segmentov s CRC na záznam, osolený poradovým číslom segmentu, takže sa staré prechody prstenca nikdy znova neobjavia. Jadrá odovzdávajú záznamy databáze vlastným prstencom, bez kopírovania, a ona ich zapisuje každých 5 ms alebo každých 256 KiB. Pri zavádzaní obnova zachová každý zapísaný záznam, odreže nedokončený chvost a pokračuje v novom segmente. Uchovávanie je podľa veľkosti, cez prstenec, aj podľa veku, cez <code>retention_days</code>. Overili sme to s miliónom záznamov a s <code>kill -9</code> uprostred zápisu.</p>
<h2 id="cisla">Čísla</h2>
<p>Nástroj na záťaž v <code>tools/loadgen</code> robí tri veci: búrku podaní rúk TLS 1.3 s mTLS a CONNECT, dlhodobé spojenia s PINGREQ, ktoré každú sekundu publikujú telemetriu, a búrky opätovných pripojení. <code>make bench</code> na Apple M4 Pro pod HVF dáva:</p>
<table>
<thead>
<tr>
<th>Pracovné jadrá</th>
<th>podania rúk/s v hosťovi</th>
<th>škálovanie</th>
<th>cez používateľskú sieť QEMU (spojení/s)</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>862</td>
<td>1,00×</td>
<td>789</td>
</tr>
<tr>
<td>2</td>
<td>1 724</td>
<td>2,00×</td>
<td>636</td>
</tr>
<tr>
<td>4</td>
<td>3 438</td>
<td>3,99×</td>
<td>536</td>
</tr>
</tbody>
</table>
<p>Číslo v hosťovi, kde sú klient aj server v tom istom virtuálnom stroji a každé pracovné jadro beží samo za seba, ukazuje, že dátová rovina škáluje lineárne. Sieťové číslo je obmedzené jednovláknovou používateľskou sieťou QEMU, nie hosťom; aj to je zapísané ako ADR-032.</p>
<p>Búrky opätovných pripojení testujeme tak, že sa všetky zariadenia pripoja naraz, opakujú odmietnuté pokusy so oneskorením a potom všetky spadnú bez DISCONNECT. Parameter <code>heap_limit_mb=</code> vo vývojovom režime donúti prijatie považovať heap za menší, než je, takže sa búrka väčšia než heap dá vyskúšať s málo socketmi. V jednom takom kole s 3 000 zariadeniami sa pripojilo 1 368 za 36 sekúnd a 5 619 pokusov bolo riadne odmietnutých, pri obmedzení 8 MiB; pamäť sa po búrke vrátila.</p>
<h2 id="ako-pracujeme">Ako pracujeme</h2>
<p>Projekt vedie písaná špecifikácia, <code>PROMPT.md</code>, s plánom míľnikov, a každý míľnik sa končí tým istým rituálom: <code>make check</code> a <code>make smoke</code> zelené, jeden commit so značkou <code>M&lt;n&gt;</code>, záznam v <code>docs/PROGRESS.md</code> a ADR pre každé rozhodnutie, ktoré nie je zrejmé. Pravidlá sú krátke a nevyjednávame o nich: žiadna panika na vonkajší vstup, <code>unsafe</code> iba v hardvérových a FFI balíkoch a vždy s komentárom, žiadne alokácie na horúcej ceste, komentár ku každému poradiu atomických operácií, každé obmedzenie pochádza z konfigurácie a zámerné dočasné časti padajú nahlas.</p>
<p>CI robí viac, než by sme stihli ručne: fmt, clippy pre hosta aj pre <code>aarch64-unknown-none</code>, typos, actionlint a shellcheck; testy na Linuxe a macOS na arm64; zostavenie jadra pre vývojovú aj produkčnú konfiguráciu s veľkosťou ELF v súhrne; zavedenie pod QEMU so všetkými scenármi dymového testu a uložením sériových záznamov; cargo-deny pre upozornenia, licencie a zdroje; minútu fuzzingu na každý z jedenástich cieľov a v noci desať minút na cieľ s korpusom v cache. K tomu CodeQL, OpenSSF Scorecard, Dependabot a vydanie s pôvodom SLSA. Každá cudzia akcia je pripnutá na plný commit SHA.</p>
<p>Reprodukovateľnosť chápeme doslovne: <code>make repro</code> zostaví HEAD dvakrát v čerstvých pracovných stromoch a porovná ELF, ISO a surový obraz bajt po bajte. Vydania sú podpísané pomocou minisign a artefakty sa dajú overiť cez <code>gh attestation verify</code>. Produkčný obraz obsahuje súkromný kľúč servera, takže samotný ISO je tajomstvo; obrazy pri vydaniach na GitHube sú vývojové, s dočasnými kľúčmi, a slúžia len na vyskúšanie.</p>
<h2 id="co-sme-sa-naucili">Čo sme sa naučili</h2>
<p>Niekoľko lekcií, ktoré zmenili plán:</p>
<ul>
<li><strong>Vlastný TCP sa napokon oplatil.</strong> Prototyp bežal na smoltcp a dobre nám slúžil až do M13. Ale server, ktorý drží státisíce spojení, nepotrebuje klientsky TCP, potrebuje SYN cookies, koleso časovačov a stav s 96 bajtmi. V M14 sme smoltcp nahradili vlastným stackom len pre serverovú stranu, s testami simulovanej straty a fuzzingom segmentov.</li>
<li><strong>Merajte pamäť zvnútra.</strong> Odhady na spojenie boli bezcenné, kým jadro samo nezačalo počítať heap v blokoch alokátora, a nie v požadovaných bajtoch. Až vtedy sme uvideli, kam sa 512 bajtov naozaj podeje, a klesli sme na 494.</li>
<li><strong>Nástroj na testovanie je súčasť produktu.</strong> Najväčším obmedzením nebolo jadro, ale používateľská sieť QEMU a počet efemérnych portov na vývojovom počítači. Pre sto tisíc spojení a viac treba <code>vmnet-shared</code> na macOS alebo Linux s KVM, rozhraním tap a viacerými klientskymi adresami.</li>
<li><strong>SQLite bez operačného systému je možný</strong>, ale len za prísnych podmienok: pevný heap, exkluzívne zamykanie bez zdieľanej pamäte, VFS, ktorý nevie o súboroch, a prijatie, ktoré nenechá databázu zaostať.</li>
<li><strong>Búrky opätovných pripojení sú normálny stav</strong>, nie výnimka. Keď spadne mobilná sieť a vráti sa, tisíce zariadení klopú súčasne. Preto prijatie odmieta spojenie ešte pred akoukoľvek kryptografiou a každé odmietnuté spojenie stojí takmer nič.</li>
</ul>
<h2 id="co-bude-dalej">Čo bude ďalej</h2>
<p>Práca po M17 je plánovaná v troch smeroch a poradie medzi nimi je zapísané v <code>docs/PLAN.md</code>. Nič z toho ešte nie je postavené.</p>
<ul>
<li><strong>Jeden ISO pre viac platforiem.</strong> Ten istý súbor <code>urmqtt-&lt;env&gt;-&lt;verzia&gt;.iso</code> a jeden surový obraz GPT, ktoré sa zavádzajú na aarch64 UEFI, x86_64 UEFI a x86_64 BIOS, vo virtuálnych strojoch aj na skutočnom hardvéri, počnúc jedným počítačom Intel. Naďalej zariadenie so všetkým vnútri, reprodukovateľné a podpísané.</li>
<li><strong>Surový disk, bez súborového systému.</strong> Špecifikácia formátu na úrovni bajtov, hostiteľský nástroj <code>urmqtt-disk</code>, ktorý zdieľa parsery s jadrom, zarovnanie oblastí na 1 MiB, rast disku s dátami, discard pre tenko alokované disky, snímky databázy A/B, kontrola bitov a podľa výberu šifrovanie AES-256-XTS s hardvérovým AES.</li>
<li><strong>Dokázateľne bezpečné.</strong> Odvolanie a zoznam povolených zariadení za behu cez podpísaný riadiaci kanál, bez nového obrazu; diferenciálne a konformančné testovanie vlastného TLS a TCP; KASLR, ochrana zásobníka, kCFI, MTE a spevnený alokátor; SQLite izolovaný na úrovni EL0; Secure Boot; SBOM a cargo-vet; a nakoniec balík pre externý audit.</li>
</ul>
<p>Platformy, ktoré ponúkajú iba zariadenia, ktoré nie sú virtio, ako AWS ENA alebo Azure MANA, zatiaľ nie sú podporované. Raspberry Pi 4 a 5 ako hostiteľ KVM, Hetzner CAX a Oracle Ampere A1 sú zdokumentované a podporované kódom, ale ešte neboli vyskúšané na skutočnej službe.</p>
<h2 id="vyskusajte">Vyskúšajte</h2>
<p>Repozitár je na <a href="https://github.com/zeljan-alduk/urMQTT">github.com/zeljan-alduk/urMQTT</a>. S Rustom s pripnutou nightly sadou nástrojov a QEMU stačia <code>make certs</code>, <code>make run</code> a jeden klient mosquitto na prvý PUBLISH cez mTLS na vlastnom počítači. Upozornenie z úvodu platí aj na konci: toto je pred-alfa a všetko, čo funguje, je napísané v <code>docs/PROGRESS.md</code>. Hlásenia chýb, otázky k návrhu a bezpečnostné hlásenia podľa pokynov v <code>SECURITY.md</code> sú vítané. O každom ďalšom míľniku sa tu ozveme.</p>]]></content:encoded>
      <category>rust</category>
      <category>mqtt</category>
      <category>iot</category>
      <category>unikernel</category>
      <category>open source</category>
    </item>
    <item>
      <title>Spúšťame blog: poznámky z praxe</title>
      <link>https://aldo.tech/sk/blog/spustame-blog/</link>
      <guid isPermaLink="true">https://aldo.tech/sk/blog/spustame-blog/</guid>
      <pubDate>Sat, 17 Oct 2026 08:00:00 +0000</pubDate>
      <description>Prečo firma, ktorá stavia terminály, aplikácie a elektroniku pre verejnú dopravu, začína písať, a o čom tu budete čítať.</description>
      <content:encoded><![CDATA[<p>Väčšinu toho, čo robíme, nikto nevidí. Softvér v termináli pri dverách autobusu, firmvér v zariadení, ktoré nikdy nedostane displej, server, ktorý v noci mení denné transakcie na reporty. Keď všetko funguje, funguje potichu. Keď nie, všimnú si to všetci.</p>
<p>Na tomto blogu si zapisujeme, čo sme sa cestou naučili. Nie marketingové texty, ale konkrétne poznámky: čo sa pokazilo, ako sme to vyriešili a čo by sme dnes urobili inak.</p>
<h2 id="o-com-budeme-pisat">O čom budeme písať</h2>
<ul>
<li><strong>Odbavenie cestujúcich vo verejnej doprave.</strong> Bezkontaktné karty, moduly SAM, práca bez siete a všetko, čo musí terminál vydržať vo vozidle, ktoré sa dvanásť hodín denne trasie.</li>
<li><strong>Mobilné aplikácie v teréne.</strong> Android a Flutter na zariadeniach, ktoré nie sú obyčajné telefóny: s čítačkami kariet, tlačiarňami a batériami, ktoré musia vydržať celú zmenu.</li>
<li><strong>Backend a dohľad nad flotilou.</strong> Ako zistiť, že zariadenie vo vozidle má problém skôr, než ho nahlási vodič.</li>
<li><strong>Hardvér.</strong> Od prototypu na stole po dosku, ktorá ide do sériovej výroby, a všetky chyby medzi tým.</li>
<li><strong>Lokálne modely AI.</strong> Kde majú zmysel modely na vlastnom hardvéri a kde sú cloud a API jednoducho lepšia voľba.</li>
</ul>
<h2 id="ako-budeme-pisat">Ako budeme písať</h2>
<p>Krátko a konkrétne, s kódom a číslami tam, kde ich máme. Publikovať budeme, keď budeme mať čo povedať, nie podľa rozvrhu. Príspevky píšeme po chorvátsky a prekladáme ich do všetkých jazykov tohto webu; sledovať nás môžete aj cez <a href="https://aldo.tech/sk/blog/feed.xml">RSS</a>.</p>
<p>Ak vás zaujíma nejaká téma alebo riešite podobný problém, napíšte nám na <a href="mailto:info@aldo.tech">info@aldo.tech</a>.</p>]]></content:encoded>
      <category>uvod</category>
    </item>
  </channel>
</rss>
