urMQTT na skutočnom VPS: ISO, podanie rúk za 0,24 ms, vlastný disk a spevnené jadro
Č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é.

V prvom texte o urMQTT sme opísali unikernel, ktorý je 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.
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.
Ako to vyzeralo v čase
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 Co-Authored-By: Claude. Š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.
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).
| Míľnik | Commit | Čas | Riadkov Rustu | Testov | ADR |
|---|---|---|---|---|---|
| M0 | 9412a9c | 28.10. 23:48 | 1 592 | 27 | 13 |
| M5 | a61083b | 29.10. 01:32 | 9 314 | 88 | 24 |
| M9 | 4be8cc9 | 29.10. 02:21 | 15 729 | 149 | 28 |
| M11 | 4800904 | 29.10. 03:43 | 22 737 | 180 | 30 |
| M13 | a558d5e | 29.10. 05:22 | 26 556 | 193 | 32 |
| M17 | 7df4f98 | 29.10. 08:48 | 33 504 | 232 | 36 |
| S1 | ecf72ec | 30.10. 00:46 | 41 910 | 299 | 45 |
| X8 | fb1504a | 30.10. 19:08 | 56 341 | 456 | 59 |
| PERF | 76f7c36 | 31.10. 14:19 | 72 511 | 618 | 76 |
| dnes | HEAD | 31.10. 23:00 | 89 790 | 739 | 104 |
Riadky sú všetky súbory .rs mimo vendor/ a fuzz/, testy sú funkcie #[test]. Čo ktorá časť pridala, podľa dátumu z histórie gitu:
| Dátum | Míľniky | Čo pribudlo |
|---|---|---|
| 29.10. | M0 až M17 | jadro, sieť, TLS, broker, SQLite, viac jadier, vlastný TCP, spevnenie, reprodukovateľné vydanie |
| 29.10. večer a 30.10. | X0 až X10, S1, H2, H3 (X0 začína 29.10. o 20:54) | port na x86_64, univerzálny ISO, jeden disk a samoinštalačný ISO, kontroly TLS, útočné klienty |
| 31.10. | O1 až O4, T1, PERF, H4, H5, H9, S2 až S6 | prehľad a konzola cez MQTT, živý režim a diagnostika, SNTP, zrýchlenia, spevnenie, verifikácia, dodávateľský reťazec, úložisko |
Sľub 1: jeden ISO pre viac platforiem
Čo sme plánovali. Jeden súbor, ktorý sa zavádza na aarch64 UEFI, x86_64 UEFI a x86_64 BIOS. Čo sme postavili. 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).

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.
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í WIPE; na snímke nižšie sa to preskočilo.

Čo je otvorené. 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č.
Podanie rúk: z 1,2 ms na 0,24 ms
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.
- Pre P-256 sme prevzali asembler
nistz256z OpenSSL a ring (ADR-130), spolu s tabuľkami s pevnou bázou na podpisovanie a overovanie podpisov CA (ADR-110, ADR-111). - X25519 bol prepísaný na štyri 64-bitové končatiny s inštrukciami MULX/ADX (ADR-131).
- Potom sme profilovali aj zvyšok (ADR-132, ADR-200).
| Meranie na VPS (server, mikrosekundy) | Pred | Po |
|---|---|---|
| podanie rúk mTLS, celá kryptografia | 1200 | okolo 240 (237 v meraní z grafu, 244 na snímke) |
| X25519 | 146 | 48 |
| podpis ECDSA | 673 | 34 |
| overenie ECDSA | 309 | 90 (107 na snímke) |
| podanie rúk, starý memcpy, rovnaký boot | 278,8 | 229,4 (-17,7%) |
Čí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.
Čo sme sa naučili o memcpy. Keď kryptografia klesla, profil ukázal niečo, čo sme nečakali: funkcie mem* (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.
| Cesta | Podiel mem* pred | Rýchlejšie po |
|---|---|---|
| podanie rúk | 7% | 17,7% |
| záznam TLS 64 B | 39% | 26,4% |
| záznam TLS 256 B | 37% | 19,6% |
| záznam TLS 1 KiB | 24% | 16,1% |
| záznam TLS 4 KiB | 13% | 6,6% |
| rozdeľovanie správ | 17 až 34% | 7 až 34% |
Čí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ží.
Č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.
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.
Sľub 2: surový disk, ale opatrný
Čo sme plánovali. 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. Prečo nie súborový systém. 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.
Čo sme postavili (S2 až S6).
- Rozloženie v2 (S2). 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
urmqtt-disk migrate(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. - Rast (S3). 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).
- Discard a write-zeroes (S4). 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ý.
- Záloha databázy na disku (S5). 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
quick_checkpri zavádzaní zlyhá, databáza sa obnoví z kópie. - Čistenie a obmedzené zavádzanie (S6). 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.
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.
Čo prekvapilo. 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.
Čo je otvorené. 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é.
Sľub 3: dokázateľne bezpečné, nakoľko sa nám to podarí
Spevnenie (H4). 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 (-Zstack-protector=strong, 1315 funkcií, a SQLite s -fstack-protector-strong, 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).
Cena je zmeraná, nie predpokladaná. Na ceste podania rúk (dp-bench, aarch64 HVF, plné podania rúk TLS 1.3 + mTLS + CONNECT):
| Krok | 1 jadro | 4 jadrá | Zmena |
|---|---|---|---|
| bez spevnenia | 1941 | 7620 | |
| + alokátor | 1898 | 7429 | -2,2% až -2,5% |
| + ochrana zásobníka | 1845 | 7243 | spolu -4,9% až -5% |
| + PIE a značky | okolo 1800 | okolo 6750 | na zaťaženom počítači, zašumené |
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.
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č.
Ako je to overené, a čo nie. 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 record_len v TLS, parse v TCP a dve aritmetiky kolesa časovačov. Nedokázané (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, cargo-vet 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 VULNERABILITY-HANDLING.md. Ú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.
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.
Na Macu: aarch64 natívne a x86_64 emuláciou
Vyvíjame na Macu a ten má dva spôsoby, ako spustiť ten istý kód, a sú veľmi odlišné.
| aarch64 pod HVF | x86_64 pod TCG | |
|---|---|---|
| Čo to je | natívne vykonávanie, rýchlosť blízka skutočnému procesoru | QEMU emuluje inštrukcie |
| Podanie rúk v hosťovi | okolo 0,58 ms (prvýkrát merané okolo M13); dp-bench dnes 1941/s na jadro |
okolo 3,3 ms |
| Scenáre dymových testov TLS | rýchle | okolo 15 minút na scenár |
| MTE | nie (na HVF ho niet) | x86 ho nemá; označovanie heapu funguje len pre hosťa aarch64 pod TCG |
| Načo slúži | skutočné merania | správnosť: cesta x86 sa overuje, čísla nie sú výkon |
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.
Lekcie
- Merajte na skutočnom stroji skôr, než začnete optimalizovať. 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.
- Preťaženie je samostatná vlastnosť. Systém, ktorý je rýchly pod záťažou, sa môže zrútiť nad ňou; odmietnutie musí byť lacnejšie než prijatie.
- Profil prekvapí. Najväčší jednotlivý zisk pre malé správy nebol v kryptografii, ale v kopírovaní pamäte.
- Disk bez súborového systému chce viac práce, nie menej. 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.
- Úprimnosť v číslach. 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%.
- Rýchlosť vývoja nie je zrelosť. Testy a ADR rastú s kódom, ale štyri dni nie sú skutočná prevádzka.
Čo bude ďalej
- H6: SQLite izolovaný na úrovni EL0, aby chyba v databáze neotvorila jadro.
- H7: zvyšok bezpečnosti času (NTS a spracovanie skokov); SNTP je len prvý krok.
- X11: porovnanie s Linuxom a Mosquittom na tom istom VPS, až vtedy má zmysel hovoriť o relatívnej rýchlosti.
- Vydanie: prvé verejné vydanie pre VPS (KVM), s podpísanými artefaktmi a návodmi.
- Po vydaní: bare metal (X12 až X16), IOMMU a Secure Boot a šifrovanie disku.
Kód je na github.com/zeljan-alduk/urMQTT a docs/PROGRESS.md 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 SECURITY.md sú vítané.
Komentáre
Pravidlá komentovania