Tovább a tartalomhoz

Egy bizonyítékrekord olvasása

Egy drill pontosan egy rekordot állít elő, amit a bizonyítéknaplóhoz fűzünk. Ez a termék — a teszt csak az eszköz —, tehát megéri úgy elolvasni egyet, hogy közben nem kell a sémát nézegetni.

{
"adapter": {
"digest": "sha256:4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c",
"name": "postgres",
"protocol": "probavi-adapter/0",
"version": "0.1.0"
},
"backup": {
"checksum": "sha256:9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f",
"created_at": "2026-07-30T01:58:02.000Z",
"kind": "pgdump",
"size_bytes": 565248
},
"checks": [
{
"detail": "accepting connections",
"name": "service_healthy",
"ok": true
},
{
"detail": "100000 rows (min 100000)",
"name": "row_count:orders",
"ok": true
}
],
"drill": {
"config_hash": "sha256:7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d",
"name": "prod-orders-db",
"pitr_target": "2026-07-30T14:32:00.000Z"
},
"env": {
"arch": "amd64",
"host_id": "3f7a9c2e5b1d8e04",
"os": "linux",
"probavi_digest": "sha256:1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d",
"probavi_version": "0.1.0"
},
"error": null,
"outcome": "pass",
"prev_hash": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
"sandbox": {
"params": {
"image": "postgres:16",
"memory": "2GiB"
},
"provider": "docker"
},
"schema": "probavi-evidence/2",
"seq": 1,
"sig": {
"alg": "ed25519",
"key_id": "56475aa75463474c",
"sig_b64": "/f2ahJgj08qDVSvbTIZ/+XI7IfkCavp0WfS7ETIezC+E2PMzFzEmX/G8d4d+S42Q9ZegQDU8aMKJTBb/gS5KDQ=="
},
"timings_ms": {
"engine_ready": 1166,
"provision": 1170,
"restore": 190,
"total": 2840,
"transfer": 110,
"validate": 61
},
"ts": "2026-07-31T02:00:11.482Z"
}

Valódi rekord a formátum közzétett példanaplójából, olvashatóság kedvéért újratördelve — a lemezen egyetlen kanonikus sor, és az aláírás arra a formára vonatkozik.

Aláírva ezzel:03a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8

Hol áll a láncban. A seq a pozíció, a prev_hash az előtte lévő rekord SHA-256 lenyomata, a ts pedig az, amikor a drill befejeződött. A napló első rekordja csupa nulla prev_hash-t hord, mert nincs elődje. Változtass meg bármelyik rekordot, és utána minden láncszem ellenőrzése megbukik — ettől lesz a napló a gyakorlatban is csak bővíthető, nem pusztán szándék szerint.

Mi volt a drill célpontja. A drill mező nevezi meg, és hordozza a használt konfiguráció lenyomatát, így két rekordról eldönthető, hogy ugyanaz a drill állította-e elő őket, vagy egy drill, amit közben valaki átírt. Ahol időpontra visszaállítást kértek, a célidőpont is bekerül mellé.

Miből állítottunk vissza. A backup rögzíti a forrásfajtát, az archívum méretét, az ellenőrzőösszegét, és azt, mikor készült. A készítés ideje az a mező, amit elsőként érdemes elolvasni, ha egy siker túl szépnek tűnik: az a rekord, ami egy három hónapos archívumról bizonyítja, hogy visszaállítható, igaz állítás egy olyan mentésről, amire senkinek nem kellene támaszkodnia.

Hova állítottuk vissza. A sandbox megnevezi a szolgáltatót és a kapott paramétereket — az image-et, a memóriakorlátot —, szó szerint rögzítve. Az adapter megnevezi az adaptert, a verzióját, az általa beszélt protokollverziót, és a digest-et: a mag által indított adapter futtatható állományának SHA-256 lenyomatát. A verzió és a digest nem ugyanaz a tény: a verzió olyan szám, amit az adapter magáról jelent, tehát két különböző build oszthat egyet — a digest az, ami megkülönbözteti őket. Együtt ezek teszik egy visszaállítási időt egy másikkal összehasonlíthatóvá: azonos image, azonos korlátok, azonos adapter-build — vagy a két szám nem ugyanazt méri.

Mi történt. Az outcome a drill ítélete, az error pedig az indokot hordozza, ha van. A checks minden ellenőrzést felsorol névvel, az eredményével és egy rövid részlettel — a sorok talált számával és a korláttal, amihez mérték, nem pusztán annyival, hogy átment.

Mennyi ideig tartott. A timings_ms fázisokra bontja a drillt: a sandbox létrehozása, az archívum átvitele, maga a visszaállítás, a motor elindulására várakozás, és az ellenőrzés. A visszaállítási fázist szándékosan az ellenőrzésektől külön mérjük, hogy az a szám, amit a helyreállítási célidődhöz hasonlítasz, ne attól legyen nagyobb, hogy mennyi SQL-t futtatsz utána.

Hol futott. Az env rögzíti az operációs rendszert, az architektúrát, egy állandó gépazonosítót, a Probavi verzióját, és a probavi_digest-et: a rekordot kiíró probavi futtatható állomány SHA-256 lenyomatát. Elég ahhoz, hogy két, drilleket futtató gépet megkülönböztess, hogy egy időbeli változást megmagyarázz, és hogy megmondd, melyik build állította elő a bizonyítást; nem elég semmi máshoz, mert a drillről semmi nem hagyja el a gépet. Bármelyik digest null, ha a fájlt nem lehetett elolvasni — egy digest megéri, hogy rögzítsük, de soha nem éri meg, hogy megbuktasson egy drillt.

Az aláírás. A sig hordozza az algoritmust, a kulcsazonosítót és magát az aláírást, a rekord kanonikus egysoros formája fölött.

Egy napló ellenőrzéséhez a fájl kell és egy publikus kulcs — nincs hálózat, nincs licenc, és még Probavi-telepítés sincs, mivel a formátumhoz önállóan kiadott ellenőrző tartozik.

Amit megállapít, az pontos: hogy ezeket a rekordokat annak a kulcsnak a birtokosa állította elő, és azóta nem változtak. Minden aláírás érvényes, és minden prev_hash egyezik azzal a rekorddal, amire mutat — tehát a napló közepéből semmit nem szerkesztettek, rendeztek át vagy vettek ki. Ez pontos megfogalmazás, és egyetlen fajta eltávolítást hagy az alábbi szakaszra.

Az egyetlen eltávolítás, amit a fájl nem mutat meg

Szekció neve “Az egyetlen eltávolítás, amit a fájl nem mutat meg”

A kivétel a napló vége. Ha a legfrissebb rekordokat töröljük, ami marad, az egy rövidebb napló: a sorszámozása továbbra is 1-nél kezdődik, a lánca töretlen, így a csak a fájlt olvasó ellenőrző VALID választ ad — helyesen, mert pusztán ebből a fájlból semmi nem tudja megkülönböztetni a levágott naplót attól, amelyik egyszerűen rövidebb. A következő drill a megrövidült láncra fűz tovább, tehát az eltávolítás utólag sem hagy nyomot.

Ezt egyetlen, a fájlon kívül tartott érték zárja le. Minden ellenőrzés kiírja a head értékét: a legmagasabb elért seq-et, mellette annak a rekordnak a tárolt sorára számított SHA-256 értéket, <seq>:sha256:<hex> alakban. Az egyik futás által kiírt head-et őrizd meg, és add át a következőnek --anchor néven.

Az a napló, amelyik ezt a sort változatlanul hordozza, azóta nőtt — ez a szokásos eset, és a válasz ugyanaz, amit a fájl horgony nélkül is adott volna. Amelyik e seq előtt véget ér, abból a végéről hiányoznak rekordok. Amelyikben az adott seq-en álló sor másra hasheelődik, azt levágták, majd újra megnövesztették — elég hosszú, és ott hibás, ahol számít. Az utóbbi kettő INVALID, kilépési kód 2: a horgonyánál rövidebb napló nem az a napló, amelyikről a horgony készült, és ez ugyanaz a megállapítás, mint egy belülről kivett rekord.

A horgony ereje teljes egészében azon múlik, hol tartják, ezért oda tedd, ahová a napló írója nem ér el — egy ticketbe, egy levélbe, egy olyan forráskódba tett commitba, amelybe a drill gépe nem írhat, vagy egy probavi push fogadóba, amelyik megőrzi, amit kapott. A Probavi egyiket sem tartja meg: kiírja az értéket, és ennyi. Aláírni sem érdemes, mert a támadó, aki ellen ez a formátum készült, épp az aláíró kulcs birtokosa.

Ez semmit nem mozdít a formátumon. Nem kerül bele mező, nem íródik alá semmi további, és a rekordok továbbra is probavi-evidence/2 szerintiek — a horgony az ellenőrzés bemenete, és nélküle a fájl és a publikus kulcs továbbra is elegendő mindahhoz, amit maga a lánc bizonyítani tud.

Az ellenőrzés a rekordról szóló állítás, nem a helyreállítási helyzetedről. Nem mondja meg, hogy a mentés a jó adatbázisról készült, hogy az ellenőrzések a jó ellenőrzések voltak, vagy hogy a visszaállítási idő a célidődön belül van. Egy tökéletesen érvényes aláírás egyetlen healthcheckből és semmi más állításból álló drill rekordján azt bizonyítja, hogy a drill megtörtént, és beszámol arról a kevésről, amit állított.

A két digest szűkebb, mint amilyennek látszik, és a specifikáció ezt ki is mondja ahelyett, hogy feltételezésre hagyná. Mindkettő egy fájl lenyomata, amit közvetlenül a futtatása előtt olvastunk be — tehát azokat a bájtokat nevezi meg, amiket a Probavi kiválasztott, nem azt bizonyítja, hogy azok a bájtok voltak a végrehajtott utasítások. Egy fájl, amit a lenyomat és az indítás között kicseréltek, rögzítetlenül maradna. Ennek az ablaknak a bezárása a futó folyamat saját image-ének olvasását jelentené, ami nem létezik minden platformon, amit a Probavi támogat — így a rekord azt a szűkebb állítást teszi, amit meg tud tartani.

Ez nem megkerülendő korlát. Ez a munkamegosztás: az aláírás teszi a történetet megbízhatóvá, és az általad választott ellenőrzések döntik el, hogy a történet mit ér.