qub-protokolspesifikasie

qub is 'n protokol vir kriptografiese temporale verbintenisse: 'n stelsel om woorde tot 'n toekomstige datum te verseël en later presies te verifieer wat verseël is, watter drand-rondte die vrystelling daarvan beheer het en—wanneer 'n bergingstransaksie of deursigtigheidslog-bewys beskikbaar is—'n onafhanklik getydstempelde boonste grens vir wanneer die syferteks verbind is.

Drie primitiewe laat dit werk. drand is 'n gedesentraliseerde willekeurigheidsbaken—die onthullingsdatum word kriptografies afgedwing eerder as deur qub se welwillendheid. Duursame berging plus 'n slegs-aanheg-deursigtigheidslog bewaar verseëlde grepe en anker gebondelde verbintenisse in permanente openbare berging; die betaalde T3-pad skryf ook 'n individuele permanenteberging-transaksie. ML-DSA-65 is 'n post-kwantum digitale handtekening—wanneer outeurskap geaktiveer is, is die qub gekoppel aan 'n sleutelpaar waarvan die geheim nooit die outeur se toestel verlaat nie.

Saam maak hierdie primitiewe 'n stelling wat tydgesluit en peuteraanduidend, opsioneel toeskryfbaar en onafhanklik tydstempelbaar is—'n kwitansie waarvan die waarde toeneem namate die wêreld se vermoë om die verlede te vervals verbeter.

Die res van hierdie dokument is die normatiewe spesifikasie wat vir interoperabele implementasies vereis word.


qub-protokolspesifikasie

Veld Waarde
Dokumentvrystelling 1.0.0 (protocol-v1.0.0)
Draadprotokol 0x01
Buitenste omhulsel 0x01
Effektiewe datum 2026-09-23
Status Huidig
Hersien deur 2026-09-23

Hierdie dokument is die normatiewe protokolspesifikasie vir die qub tydverbintenis-stelsel. Dit definieer datastrukture, serialisering-reëls, afleidingsformules, en verifikasieprosedures wat vir interoperabele implementasies vereis word.

Omvang: die protokol-laag is opsetlik taal-neutraal — die qub-liggaam is ondeurskynende plaintext / markdown / verbond-grepe, en lokale-bewuste lewering is die kyker se verantwoordelikheid (qub.social-webtoepassing, <qub-embed> iframe, MCP-kliënte, ens.).


1. Notasie en konvensies

Notasie Betekenis
u8, u64, i64 Onderteken/getekende heelgetalle van gespesifiseerde bisbreedte
[u8; N] Vaste-lengte grepe-skikking van N grepe
Vec<u8> Veranderlike-lengte grepe-skikking
Option<T> Waarde van tipe T, of afwesig
String UTF-8 teksstring, NFC genormaliseer
`
SHA3-256(x) NIST SHA3-256 hash van greep-string x (FIPS 202)
ceil(x) Plafonfunksie: kleinste heelgetal ≥ x
CBOR Concise Binary Object Representation (RFC 8949)
big-endian Mees beduidende greep eerste

Alle heelgetalle in preimage-konstruksies word as big-endian vaste-breedte grepe-skikkings gekodeer (i64 → 8 grepe, u8 → 1 greep) tensy anders gespesifiseer.

Alle tydstempels is Unix-sekondes in UTC.


2. Datastrukture

2.1 ComposeQub (skepper-geheue-toestand)

Nie na CBOR geserialiseer nie. Nie in permanente berging gestoor nie. Plaaslik tot die skepper se toepassing.

ComposeQub {
    draft_id:       [u8; 16],        // Random, generated locally
    created_at:     i64,             // Unix seconds UTC
    unlock_at:      Option<i64>,     // Unix seconds UTC; None while composing
    visibility:     u8,              // 0x00 = private; 0x01 = public
    content_type:   u8,              // 0x01 text; 0x03 pact; 0x04 verdict
    plaintext:      Vec<u8>,         // Raw body bytes (UTF-8 for text)
    sender_label:   Option<String>,  // Display name; V2-signed when authorship is enabled
    title:          Option<String>,  // Plaintext countdown title; bound via title_hash
    reply_to:       Option<[u8; 32]>,// Parent qub_id; V2-signed when authorship is enabled
    outcome_at:     Option<i64>,     // Optional future judgment time; bound to qub_id
    status:         DraftStatus,     // Composing | Sealed | Uploaded | Failed
}

2.2 QubEnvelope (gedekripteerde data-vrag)

Geserialiseer met kanonieke CBOR (§3). Geënkripteer binne die SealedQub. Dit is die struktuur wat inhouds-integriteit ná dekripsie bewys.

QubEnvelope {
    version:             u8,              // Protocol major version (0x01 for v1)
    qub_id:              [u8; 32],        // Derived (see §4.1)
    content_type:        u8,              // Content type registry (see §6)
    created_at:          i64,             // Unix seconds UTC
    unlock_at:           i64,             // Unix seconds UTC
    outcome_at:          Option<i64>,     // When reality renders judgment; bound to qub_id
    sender_label:        Option<String>,  // Not in qub_id; V2-signed when authorship is enabled
    reply_to:            Option<[u8; 32]>,// Parent qub_id; not in qub_id; V2-signed when present
    body:                Vec<u8>,         // UTF-8 text or canonical CBOR pact/verdict body
    body_hash:           [u8; 32],        // SHA3-256(body) (see §4.2)
    sig_alg:             u8,              // Signature algorithm (see §9.2)
    author_signature:    Option<Vec<u8>>, // Set when sig_alg != 0x00
    author_pubkey:       Option<Vec<u8>>, // Set when sig_alg != 0x00
    cosigner_pubkey:     Option<Vec<u8>>, // Set for cosigned pact bilateral agreements
    cosigner_signature:  Option<Vec<u8>>, // Set for cosigned pact bilateral agreements
}

Basislyn (ongetekende teks-qub): version = 0x01, content_type = 0x01, sig_alg = 0x00; handtekening- en mede-ondertekenaarvelde is afwesig. Ander opsionele metadatavelde mag teenwoordig wees.

Ander v1-konfigurasies: content_type = 0x03 (verbond-liggaam, sien §6.1); sig_alg = 0x01 (ML-DSA-65) met author_signature en author_pubkey teenwoordig (sien §9.3); cosigner_pubkey en cosigner_signature saam teenwoordig vir mede-onderteken verbonde (sien §9.7); reply_to gestel op die ouer-qub se qub_id vir antwoord-ketting qubs (sien §9.3 vir die handtekening-omvang implikasies).

2.3 SealedQub (kanonieke draadformaat)

Geserialiseer met kanonieke CBOR (§3). Dit is die binneste draadartefak: openbare aflewering berg hierdie grepe kaal, terwyl private aflewering hulle voor berging in OuterWrapper omhul (§13).

SealedQub {
    version:           u8,              // Protocol major version (0x01 for v1)
    qub_id:            [u8; 32],        // Same as QubEnvelope.qub_id
    visibility:        u8,              // 0x00 = private/wrapped; 0x01 = public/bare
    unlock_at:         i64,             // Unix seconds UTC
    outcome_at:        Option<i64>,     // Surfaced on the verdict-watch CTA
                                        //   before reveal; mirrors QubEnvelope.outcome_at;
                                        //   bound to qub_id via the §4.1 preimage.
    drand_chain_id:    String,          // drand chain hash (hex string)
    drand_round:       u64,             // Target drand round number
    drand_chain_version: Option<u8>,    // W3 — chain-migration version. Absent / 0 = quicknet
                                        //   (the only chain today). Lets a future chain swap
                                        //   be expressed on the wire without a breaking format
                                        //   change. NOT part of the §4.1 qub_id preimage, so its
                                        //   addition never alters an existing qub's identity.
    tlock_ciphertext:  Vec<u8>,         // tlock-encrypted QubEnvelope CBOR bytes
    recipient_pubkey:  Option<[u8; 32]>,// Reserved field; accepted by canonical CBOR
                                        //   but not interpreted by the v1 reference viewer
    title:             Option<String>,  // Plaintext title surfaced on the viewer
                                        //   countdown before reveal. Bound to qub_id
                                        //   via title_hash (§4.1). 1..=100 NFC code
                                        //   points, no hostile/control code points.
}

2.4 RevealedQub (kyker-toepassingstoestand)

Nie na CBOR geserialiseer nie. Plaaslik tot die kyker se toepassing. Gekonstrueer ná suksesvolle dekripsie en verifikasie.

RevealedQub {
    qub_id:              [u8; 32],
    arweave_tx_id:       String,
    visibility:          u8,
    content_type:        u8,
    created_at:          i64,
    unlock_at:           i64,
    outcome_at:          Option<i64>,       // Carried from both wire layers; drives the verdict-watch block
    drand_chain_id:      String,
    drand_round:         u64,
    sender_label:        Option<String>,
    title:               Option<String>,    // Carried forward from SealedQub.title
    reply_to:            Option<[u8; 32]>,
    body:                Vec<u8>,
    body_hash:           [u8; 32],
    body_hash_verified:  bool,
    author_signature:    Option<Vec<u8>>,
    author_pubkey:       Option<Vec<u8>>,
    signature_verified:  Option<bool>,
    cosigner_pubkey:     Option<Vec<u8>>,
    cosigner_signature:  Option<Vec<u8>>,
    cosigner_verified:   Option<bool>,
}

3. Kanonieke CBOR-profiel

Alle SealedQub- en QubEnvelope-serialisering MOET aan hierdie profiel voldoen. Twee implementasies wat dieselfde logiese struktuur ontvang MOET identiese grepe lewer.

3.1 Enkoderingsreëls

Reël Spesifikasie
Standaard RFC 8949 §4.2.1 (Core Deterministic Encoding Requirements)
Kaart-sleutel orde Gesorteer volgens gekodeerde greep-lengte eers (korter voor langer), dan leksikografies (greep-vir-greep vir gelyke-lengte enkoderings)
Heelgetal-enkodering Kortste vorm: 0–23 in aanvanklike greep; 24–255 in 2 grepe; 256–65535 in 3 grepe; ens.
Lengte-enkodering Slegs definitiewe lengtes. Geen onbepaalde-lengte skikkings, kaarte, greep-stringe, of teksstringe nie (additional info = 31 is verbode).
Etikette Geen CBOR-etikette nie (hoof-tipe 6 is verbode).
Drywende-punt Geen drywendes nie (hoof-tipes 7 waardes 0xF9–0xFB is verbode).
Teksstringe UTF-8 gekodeer, NFC genormaliseer (Unicode Normalization Form C).
Greep-stringe Rou grepe. Geen base64-enkodering by die CBOR-laag nie.
Duplikaat-sleutels Verwerp met fout. Parsers MOET NIE stilweg duplikaat kaart-sleutels aanvaar nie.
Onbekende sleutels Verwerp met fout. Parsers MOET NIE kaart-sleutels buite die tipe se kanonieke sleutel-stel duld nie — twee onderskeie kanonieke greep-stringe moet nooit na dieselfde waarde dekodeer nie (encode(decode(x)) == x), en vir ondertekende data-vragte sou 'n ekstra sleutel versteekte inhoud wees waartoe beide handtekeninge verbind. Skema-ontwikkeling gaan deur version, nooit deur ekstra sleutels nie.
Eenvoudige waardes Slegs true (0xF5), false (0xF4), en null (0xF6) word toegelaat.
Opsionele velde Afwesige opsionele velde word uitgelaat van die CBOR-kaart geheel (nie as null gekodeer nie). Teenwoordige opsionele velde word in gesorteerde sleutel-orde ingesluit.

3.2 Geverifieerde kanonieke sleutel-ordes

Hierdie sleutel-ordes is normatief. Implementasies MOET sleutels presies in hierdie orde uitstuur. Foutsoek-aansprake BEHOORT ordening in nie-vrystellingsbouë te verifieer.

QubEnvelope (weergawe 0x01, ongeteken, alle opsionele velde afwesig):

"body"                (5 encoded bytes)
"qub_id"              (7 encoded bytes)
"sig_alg"             (8 encoded bytes)
"version"             (8 encoded bytes)
"reply_to"            (9 encoded bytes)   ← only if present (reply chains)
"body_hash"           (10 encoded bytes)
"unlock_at"           (10 encoded bytes)
"created_at"          (11 encoded bytes)
"outcome_at"          (11 encoded bytes)  ← only if present (verdict mechanic)
"content_type"        (13 encoded bytes)
"sender_label"        (13 encoded bytes)  ← only if present
"author_pubkey"       (14 encoded bytes)  ← only if present
"cosigner_pubkey"     (16 encoded bytes)  ← only if present (pact cosign)
"author_signature"    (17 encoded bytes)  ← only if present
"cosigner_signature"  (19 encoded bytes)  ← only if present (pact cosign)

QubEnvelope sleutel-orde afleiding: elke sleutel is 'n CBOR-teksstring. Gekodeerde lengte = 1 greep kop + string-lengte (vir stringe onder 24 grepe). Sorteer eers volgens totale gekodeerde lengte, dan leksikografies vir sleutels van gelyke lengte.

SealedQub (weergawe 0x01, publiek, geen ontvanger):

"title"             (6 encoded bytes)   ← only if present
"qub_id"            (7 encoded bytes)
"version"           (8 encoded bytes)
"unlock_at"         (10 encoded bytes)
"outcome_at"        (11 encoded bytes)  ← only if present (verdict mechanic)
"visibility"        (11 encoded bytes)
"drand_round"       (12 encoded bytes)
"drand_chain_id"    (15 encoded bytes)
"recipient_pubkey"  (17 encoded bytes)  ← only if present
"tlock_ciphertext"  (17 encoded bytes)
"drand_chain_version" (20 encoded bytes) ← only if present (W3; absent = quicknet)

PactTerms (verbond-liggaam, content_type 0x03):

"notes"         (6 encoded bytes)  ← only if present
"terms"         (6 encoded bytes)
"title"         (6 encoded bytes)
"party_a"       (8 encoded bytes)
"party_b"       (8 encoded bytes)
"pact_version"  (13 encoded bytes)

PactTerm (ry van die terms-skikking):

"key"    (4 encoded bytes)
"value"  (6 encoded bytes)

PartyIdentifier (party_a / party_b kaart):

"label"    (6 encoded bytes)
"contact"  (8 encoded bytes)  ← only if present

3.3 Greep-enkodering verwysing

Tipe CBOR-enkodering Voorbeeld
SHA3-256 hash (32 grepe) 0x58 0x20 + 32 grepe body_hash, qub_id
Tydstempels (i64) Hoof-tipe 0 (positief) of 1 (negatief), kortste enkodering Unix-sekondes
Weergawe (u8, waarde 1) 0x01 (enkele greep)
Inhoudstipe (u8, waarde 1) 0x01 (enkele greep)
sig_alg (u8, waarde 0) 0x00 (enkele greep)
ML-DSA-65 handtekening (3 309 grepe) 0x59 0x0C 0xED + 3 309 grepe author_signature, cosigner_signature
ML-DSA-65 publieke sleutel (1 952 grepe) 0x59 0x07 0xA0 + 1 952 grepe author_pubkey, cosigner_pubkey

4. Normatiewe afleidings

4.1 qub_id

Die qub_id identifiseer 'n qub uniek en bind die QubEnvelope aan die SealedQub. Dit word deterministies afgelei van die koevert-inhoud.

qub_id = SHA3-256(
    "QUB_ID_V2"          ||  // domain separator: ASCII bytes [0x51 0x55 0x42 0x5F 0x49 0x44 0x5F 0x56 0x32] (9 bytes) + 0x00 padding (1 byte) = 10 bytes
    version              ||  // u8 (1 byte)
    content_type         ||  // u8 (1 byte)
    created_at           ||  // i64 big-endian (8 bytes)
    unlock_at            ||  // i64 big-endian (8 bytes)
    outcome_at_or_zero   ||  // i64 big-endian (8 bytes; 0 when outcome_at is absent)
    drand_round          ||  // u64 big-endian (8 bytes)
    body_hash            ||  // [u8; 32] (32 bytes)
    title_hash               // [u8; 32] (32 bytes; absent-sentinel = [0u8; 32])
)
// Total preimage: 108 bytes → 32-byte output

Domein-skeier enkodering: Die string "QUB_ID_V2" is 9 ASCII-grepe. 'n Enkele 0x00-vullinggreep word bygevoeg om 10 grepe vir belyning te bereik. Implementasies MOET presies hierdie 10 grepe gebruik: [0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00].

outcome_at-enkodering: 'n Voorvrystelling-implementeringshersiening het die preimage van 92 na 100 grepe uitgebrei om die opsionele outcome_at-veld in die binding in te vou. 'n Afwesige outcome_at word as 8 nulgrepe geënkodeer; die protokolvalideerders verwerp oral outcome_at <= 0, dus kan hierdie sentinel nie met 'n geldige waarde bots nie. Sien §3.2 (draadformaat) en die in-boom-tasks/verdict-uplift-plan.md vir die uitspraakmeganiek wat hierdie veld motiveer.

drand_round-enkodering: 'n Latere voorvrystelling-implementeringshersiening het die preimage van 100 na 108 grepe uitgebrei om drand_round (die teiken-drand-rondte, §4.3) in die binding in te vou, en die domeinskeier na QUB_ID_V2 verhoog. Dit bind die tydslot-rondte in die qub-identiteit: 'n poort kan nie die syferteks aan 'n ander (byvoorbeeld reeds verstreke) rondte herbind as wat die vertoonde unlock_at impliseer nie. Die ontsluitprosedure (§8) verifieer boonop dat die rondte wat in die tlock-syferteksstrofe ingebak is, ooreenstem met unlock_round(unlock_at), dus is die vertoonde ontsluittyd bewysbaar die rondte wat ontsleuteling beheer.

Eienskappe:

4.2 body_hash

body_hash = SHA3-256(body)

Waar body die rou Vec<u8> inhoud-vrag is. Vir teks-qubs is dit die UTF-8 gekodeerde qub-liggaam.

4.2.1 title_hash

title_hash = SHA3-256(NFC(title).utf8_bytes)   if title is present
title_hash = [0u8; 32]                         if title is absent

Waar title die opsionele plaintext-titel is wat op die kyker-aftelling voor onthulling verskyn (sien §3.2). NFC-normalisering loop by hash-tyd sodat die spysverteringskode stabiel is oor visueel-ekwivalente kodepunt-rye. Die alles-nulle sentinel is gereserveer vir die afwesige geval; 'n leë string word by die kanonieke CBOR-grens verwerp as 'n nie-kanonieke enkodering van "afwesig" (die kanonieke enkodering laat die veld heeltemal uit).

4.3 Ontsluitings-rondte kartering

drand_round = floor((unlock_at - chain_genesis_time) / chain_period_seconds) + 1
Parameter Bron Voorbeeld
unlock_at Gebruiker-gekose Unix-sekondes UTC 1735689600 (2025-01-01 00:00:00 UTC)
chain_genesis_time drand ketting-info (genesis_time) 1595431050
chain_period_seconds drand ketting-info (period) 30

Dit is die verwysings-tlock-kartering (drand se CurrentRound). drand publiseer rondte N by chain_genesis_time + (N - 1) * chain_period_seconds, dus kies die formule die rondte wat by unlock_at aktief is—die rondte waarvan die handtekening die eerste een is wat 'n kyker wat by unlock_at aankom, kan gebruik.

Belyningseienskap (die geval wat in die praktyk saak maak): wanneer (unlock_at - chain_genesis_time) presies deur chain_period_seconds deelbaar is, word die gekose rondte se handtekening presies by unlock_at, nooit voor dit nie, gepubliseer. Dit geld altyd vir die verwysingsontplooiing: quicknet se genesis-tyd (1692803367) is deur sy 3-sekonde-periode deelbaar, en die verwysingstoepassings pen ontsluittye op hele minute vas. Vir 'n nie-belynde unlock_at word die gekose rondte se handtekening streng minder as een periode voor unlock_at gepubliseer—die temporele presisie van die verbintenis is een bakenperiode.

Verouderde voorvrystelling-kartering en ontsluitkant-toleransie: die oorspronklike kartering was ceil((unlock_at - chain_genesis_time) / chain_period_seconds), wat—vir die periodebelynde geval hierbo—die rondte gekies het wat een volle periode voor unlock_at gepubliseer is en die syferteks presies een periode vroeg ontsleutelbaar gemaak het. Die twee karterings verskil met presies +1 wanneer die delta deur die periode deelbaar is, en stem andersins ooreen. Omdat drand_round in die onveranderlike qub_id-preimage (§4.1) ingevou is, kan artefakte wat onder die verouderde kartering verseël is, nie herafgelei word nie; verifieerders wat §8-stap 6a se rondte-kruiskontrole uitvoer, MOET daarom 'n gestoorde drand_round aanvaar wat gelyk is aan óf die afgeleide rondte óf die afgeleide rondte minus een (en MOET vereis dat die tlock-stroferondte presies aan die gestoorde rondte gelyk is). Die toleransie vervroeg die vroegste beheersende handtekening met hoogstens een periode. Die verbond-opvoerdiens pas dieselfde toleransie toe wanneer dit 'n opgevoerde verbond se qub_id heraflei (by opvoering en mede-ondertekening): indien die huidige kartering se rondte nie die gebinde qub_id reproduseer nie en die delta deur die periode deelbaar is, probeer dit weer met die rondte minus een, en verseël dit die gefinaliseerde verbond tot watter rondte die qub_id werklik bind—nooit blindelings tot die herberekende rondte nie, wat die artefak permanent onafleibaar sou maak.

Validasie: unlock_at MOET in die toekoms wees by seël-tyd. unlock_at MOET NIE meer as 10 jaar van created_at af wees nie (om langtermyn drand-afhanklikheidsrisiko te beperk; die UI BEHOORT te waarsku vir ontsluitings-datums verby 2 jaar).


5. Draadformaat newtypes

Draadformaat-newtypes verskaf kompilasie-tyd veiligheid teen die verwarring van CBOR-grepe met JSON, rou plaintext, of ander greep-enkoderings.

Tipe Bevat Geproduseer deur Verbruik deur
SealedQubCbor Kanonieke CBOR van SealedQub serialize_sealed_qub() Binneste draadartefak; kaal gestoor vir openbare aflewering of omhul vir private aflewering, en dan deur die kyker herwin
QubEnvelopeCbor Kanonieke CBOR van QubEnvelope serialize_qub_envelope() tlock-enkripsie-inset, tlock-dekripsie-uitset

5.1 Konstruksie-reëls

// Production code — only through CBOR serialisers:
let sealed = SealedQubCbor::from_encoded(cbor_bytes);

// There is deliberately NO From<Vec<u8>> implementation.
// You cannot accidentally wrap arbitrary bytes in a wire format type.

// Accessing raw bytes:
let bytes: &[u8] = sealed.as_bytes();
let bytes: Vec<u8> = sealed.into_bytes();

5.2 Validasie by konstruksie

from_encoded() BEHOORT te valideer dat die inset met 'n geldige CBOR-kaart kop begin. Volledige strukturele validasie gebeur by ontleed-tyd, nie konstruksie-tyd nie, om dubbele-ontleding te vermy.


6. Inhoudstipe-register

Waarde Tipe Maks. liggaam-grootte Notas
0x00 Gereserveer (ongeldig) — MOET NIE gebruik word nie
0x01 Plat teks (UTF-8, beperkte Markdown) 50 KB betaald / 10 KB gratis Sien §10 vir lewering-reëls. Die gratis / betaalde verdeling word deur die oplaai-diens afgedwing; die protokol-laag harde plafon is 50 KB.
0x02 Gereserveer (toekoms) — Toegeken vir 'n toekomstige inhoudstipe; nie geldig in v1 nie. Kykers MOET verwerp volgens die reël hieronder.
0x03 Verbond (bilaterale ooreenkoms, CBOR-liggaam) 100 KB Liggaam is kanonieke CBOR PactTerms (§6.1). Mede-ondertekenaar ondertekening per §9.7.
0x04 Uitspraak (skepper se self-beoordeling, CBOR-liggaam) 8 KB Liggaam is kanonieke CBOR VerdictBody (§6.2). Word slegs deur die stelsel-kant verdict-intensie uitgestuur. Ouer-verhouding is op die Parent-Tx-Id Arweave-merker, nie op die liggaam nie. Sien verdict-uplift-plan §3.4.

Kykers MOET onbekende inhoudstipes met 'n duidelike gebruiker-sigbare fout verwerp. Kykers MOET NIE poog om onbekende tipes as teks te lewer nie.

6.1 Verbond-liggaam (content_type = 0x03)

'n Verbond-liggaam is die kanonieke CBOR-enkodering van 'n PactTerms-waarde:

PactTerms {
    pact_version:  u8,                    // 0x01 for structured/v1
    title:         String,                // ≤ 200 bytes, NFC
    terms:         Vec<PactTerm>,         // ≤ 20 rows
    party_a:       PartyIdentifier,       // initiator
    party_b:       PartyIdentifier,       // counter-signer
    notes:         Option<String>,        // ≤ 5,000 bytes, NFC; absent key if none
}

PactTerm       { key: String (≤ 100 bytes), value: String (≤ 2,000 bytes) } // NFC
PartyIdentifier{ label: String (≤ 100 bytes), contact: Option<String (≤ 320 bytes)> }

Kanonieke CBOR-sleutel-ordes vir al drie kaarte word in §3.2 gegee. Totale geserialiseerde verbond-CBOR MOET NIE 100 KB oorskry nie (stem ooreen met §6).

Skema-onderskeider. Die eerste ry in terms vir 'n structured/v1-verbond MOET wees { key: "pact_schema", value: "structured/v1" }. Rye sonder hierdie merker is "pasgemaakte" verbonde en ontvang geen gestruktureerde validasie of skema-bewuste lewering nie.

Bevrore erkenning-gleuwe. structured/v1-verbonde dra presies vier erkenning-rye onder hierdie sleutels:

"initiator_standard_terms"
"initiator_capacity_terms"
"counterparty_standard_terms"
"counterparty_capacity_terms"

Die value vir elk is een van agt bevrore Engelse stringe gekies deur die (role, kind)-paar, waar role ∈ { seller, buyer, provider, client } en kind ∈ { standard, capacity }. Die stringe self is normatiewe protokol-data — beide partye se ML-DSA-65 handtekeninge verbind tot die presiese grepe via body_hash. Hulle word NIE gelokaliseer nie; die ondertekende liggaam is taal-neutraal. Enige bewoording-verandering vereis 'n nuwe skema-weergawe (structured/v2).

Die agt stringe, hul opsoek (acknowledgement_for(role, kind)), en die rasionaal vir elkeen is vasgepen deur die verwysing-implementasie. Konforme implementasies MOET greep-identiese erkenning-waardes uitstuur; goue-vasstellings SHA3-256 liggaam-hash toetse wat al vier rol-kombinasies dek vang enige drywing.

Kyker-vertoonsorde. Die erkenning-stringe bevat frases soos "described above", wat veronderstel dat die beskrywing- / omvang-rye voor die erkennings lewer. Kykers MOET die terms-skikking in CBOR-orde lewer; herrangskikking breek die prosa-semantiek.

Teen-party kontak. Wanneer Party B se contact 'n geldige e-posadres is, stuur die qub-oplaaidiens outomaties 'n hersiening- / mede-ondertekening uitnodig-e-pos op verhoog-tyd uit en bind die uiteindelike mede-ondertekening aan verifikasie van dieselfde adres (§9.7). Verbonde waarvan die Party B-kontak afwesig is kan steeds mede-onderteken word, maar slegs deur 'n buite-band kanaal — die diens weier mede-ondertekening-versoeke wat nie 'n ooreenstemmende 15-minute e-pos-verifikasie merker kan lewer nie.

6.2 Uitspraak-liggaam (content_type = 0x04)

'n Uitspraak-liggaam is die kanonieke CBOR-enkodering van 'n VerdictBody-waarde:

VerdictBody {
    verdict_version: u8,                  // 0x01 for structured/v1
    outcome:         u8,                  // 1=Right · 2=Partial · 3=Wrong · 4=Unfalsifiable
    reflection:      Option<String>,      // ≤ 2,000 bytes NFC; "what changed, what did you learn"
    evidence_url:    Option<String>,      // ≤ 2,048 bytes; HTTPS only; absent key when omitted
}

Kanonieke CBOR-sleutel-orde:

"outcome"          (8 encoded bytes)
"reflection"       (11 encoded bytes)  ← only if present
"evidence_url"     (13 encoded bytes)  ← only if present
"verdict_version"  (16 encoded bytes)

Totale geserialiseerde uitspraak-CBOR MOET NIE 8 KB oorskry nie (stem ooreen met die register-ry hierbo).

Uitkoms-opsomming. Die draadgreep is intensie-neutraal; die vier emmers Right / Partial / Wrong / Unfalsifiable dek elke uitspraak-draende intensie se uitkoms-ruimte. Per-intensie etikette ("Reg geraai" / "Nagekom" / "Gelewer" / "Bevestig" vir Right, ens.) is 'n kyker-kant lewering-aangeleentheid wat teen die ouer-qub se intensie opgelos word — die draad bly taal- en intensie-neutraal. Waardes buite 1..=4 MOET by dekodering verwerp word.

Ouer-koppeling. 'n Uitspraak-qub dra NIE die ouer-verwysing in sy liggaam nie. Die ouer-qub se Arweave-transaksie-id word as die Parent-Tx-Id berging-merker by oplaai-tyd uitgestuur (§7 berging-merker-laag). Dit hou die liggaam 'n self-bevattende ondertekende stelling van self-beoordeling; die oudit-ketting ("reg waaroor?") word via die Arweave-merker-opsoek gevestig.

Bewys-URL-veiligheid (normatief). Wanneer evidence_url teenwoordig is, MOET valideerders (komponeer-kant, draad-kant, Worker-rand) afdwing:

  1. Slegs HTTPS. Die string MOET met die greep-volgorde https:// begin. Enige ander skema — http, ftp, javascript, data, file, ens. — word verwerp.
  2. Lengte-plafon. ≤ 2,048 grepe (praktiese URL-grens van blaaier).
  3. NFC + vyandige-kodepunt-toets. Dieselfde reël as title en reflection — bidi-oorheers- / nul-wydte- / merker-blok- / BOM- / C0- / C1-kodepunte word verwerp. Definisie stem ooreen met die Rust crate::handle::contains_hostile_text_codepoint en die TS workers/api/src/utils/unicode.ts::isHostileCodepoint (hou in pas).
  4. Geen wit-spasie, geen ASCII-beheerkarakters nie. Wit-spasie / DEL / sub-0x20 grepe enige plek in die URL word verwerp — sluit die \n/\t-inspuiting-vektor af wat die bidi-reël nie dek nie.
  5. Nie-leë gasheer-segment. Alles tussen https:// en die eerste /, ?, of # MOET nie-leeg wees.

Geen bediener-kant gaan haal nie. Die Worker MOET NIE die URL volmag, gaan haal, of voorskou nie. Die protokol stoor 'n string; lewering gebeur kyker-kant met rel="nofollow noopener noreferrer" target="_blank" en 'n sigbare gasheer vertoon langs die skakel-teks.

Refleksie. Opsionele skepper-geskrewe refleksie-teks ("wat het verander, wat het jy geleer"). Dieselfde NFC + vyandige-kodepunt-validasie as title. Leeg / slegs wit-spasie-inset val saam tot afwesig by konstruksie-tyd.

Skema-weergawe. v1 ondersteun slegs verdict_version = 0x01. Toekomstige skema-hersienings stoot hierdie greep op en land saam met 'n nuwe protokol-weergawe per §12.


7. Verseël-protokol

Die volledige verseël-volgorde. Elke stap is normatief.

 1. User composes plaintext and metadata in ComposeQub.
 2. Validate:
    a. body is non-empty.
    b. body size ≤ max for content_type and user tier (see §6).
    c. unlock_at is in the future.
    d. unlock_at ≤ created_at + 10 years.
    e. content_type is a known, supported value.
    f. visibility is 0x00 (private) or 0x01 (public).
 3. Compute body_hash = SHA3-256(body).
 4. Set created_at = current Unix seconds UTC.
 5. Select drand chain. Load chain_genesis_time and chain_period_seconds, and
    compute drand_round = floor((unlock_at - chain_genesis_time) / chain_period_seconds) + 1
    (§4.3). (Computed here, before qub_id, because drand_round is bound into the
    qub_id preimage—§4.1.)
 6. Compute qub_id (see §4.1), folding in drand_round from step 5.
 7. Construct QubEnvelope with all fields.
 8. Serialise QubEnvelope using canonical CBOR → bytes B.
    Assert: serialised output matches canonical profile (§3).
 9. Compute C = tlock_encrypt(B, drand_round, drand_chain_public_key).
10. Construct SealedQub with tlock_ciphertext = C and matching qub_id, version,
    visibility, unlock_at, drand_chain_id, and drand_round.
11. Serialise SealedQub using canonical CBOR → SealedQubCbor.
12. Select the delivery shape from visibility:
    a. Private (0x00): generate K = 32 random bytes and N = 12 random bytes
       using a CSPRNG. Compute W = wrap_sealed_qub(SealedQubCbor,
       qub_id=qub_id, key=K, nonce=N) per §13. Upload payload = W.
    b. Public (0x01): upload payload = bare SealedQubCbor; do not generate K.
13. Display seal-time disclosure. User confirms.
14. Validate upload eligibility via the qub upload service (bot-detection, entitlement, rate limits).
15. Submit the selected upload payload to the qub upload service. For a private
    browser seal, the service is byte-blind to the inner SealedQubCbor and never
    receives K. The Builder `/api/v1/seal` route is an explicit exception: it
    receives plaintext and caller-supplied K in memory, then persists neither.
16. Receive arweave_tx_id from the service. For private delivery, construct
    `<origin>/c/<arweave_tx_id>#<base64url(K)>` (or the equivalent short-code
    path). For public delivery, omit the fragment. Browsers do not transmit URL
    fragments to servers, so K from the browser-seal path is not observed by
    qub.social or any storage gateway.

Bergingsetiket laag (buite-band). Die qub-oplaaidiens heg 'n opsetlik klein stel bergingstransaksie-etikette saam met die geselekteerde oplaaivrag. Content-Type=application/octet-stream word normatief vereis. Die verwysing-diens heg addisioneel drie opsionele etikette aan wanneer die skepper kies om hulle te wys: Intent (toelaatlys-gevalideerde komposisie-intensie — presies een van announcement, thesis, prediction, letter, secret, commitment, proof, of stelsel-uitgestraalde verdict), Author (skepper se §9.3 publieke-sleutel vingerafdruk as 64-karakter kleinletter hex), en Parent-Tx-Id (ouer-qub se bergingstransaksie-ID vir antwoord-kettings, 43-karakter base64url).

Die Author-etiket is per-qub kies-in: die verwysing-skepper toepassing heg dit slegs aan wanneer die gebruiker openbare-toeskrywing eksplisiet by seël-tyd aktiveer. Wanneer die skakelaar af is — die verstek — word geen Author-etiket geskryf nie en die qub is nie toegeskryf op die ketting nie: niks in permanente berging koppel die oplaai aan 'n skepper se handvatsel, e-pos, of ander qubs nie. Wanneer die skakelaar aan is, los die Author-vingerafdruk op na die skepper se gekose @handvatsel via die §9.5 bevestigings-ketting. Antwoord-ketting verhoudings en Intent is nie-identifiserend. Vir private aflewering enkripteer die buitenste omhulling (§13) die herkenbare binneste SealedQub-artefak, dus is die oes van gestoorde omhulsels en die verkryging van openbare drand-handtekeninge steeds onvoldoende om die liggaam sonder K te herwin; bergingsetikette bly doelbewus openbare metadata.

Die verwysing-diens heg opsetlik NIE App-Name-, App-Version-, of Type-etikette aan nie: enige sodanige enkel-waarde filter sou die hele qub-korpus na 'n GraphQL-navraag terugstuur, wat onversoenbaar is met die omhulling se liggaam-alleen vertroulikheid-omvang.

'n Konformerende verifieerder MOET NIE op enige bergingsetiket vir §11 derde-party verifikasie staatmaak nie; die liggaam-hash / qub_id / handtekening verbind slegs tot die binnenste CBOR, nooit tot die etiket-stel nie.


8. Ontsluit-protokol

Die volledige ontsluit-volgorde. Elke stap is normatief.

 1. Viewer opens delivery URL. Extract arweave_tx_id from the path and retain
    the optional URL fragment. Do not assume a missing fragment is an error:
    public/bare delivery intentionally has no K.
 2. Check denylist. If tx_id is denylisted → display block message. Stop.
 3. Fetch the stored bytes (with multi-gateway fallback).
 3a. Resolve the delivery shape structurally:
    a. If the bytes parse as OuterWrapper, require a well-formed 32-byte K in
       the URL fragment, require wrapper version 0x01, and unwrap per §13.
       Any missing/malformed K or AEAD failure is a terminal error.
    b. Otherwise require the bytes to parse as bare SealedQubCbor; no K is
       required. If neither shape parses, report an integrity error.
 4. Parse SealedQubCbor → SealedQub.
 5. Validate: SealedQub.version is known (0x01), visibility is known, and the
    delivery shape matches it (wrapped = 0x00 private; bare = 0x01 public).
    Reject any mismatch or unknown value.
 6. If current time < SealedQub.unlock_at → display countdown. Poll or wait.
 6a. Round-binding check. Recompute expected_round from
    SealedQub.unlock_at per §4.3. Reject unless SealedQub.drand_round ==
    expected_round OR SealedQub.drand_round == expected_round - 1 (the
    legacy pre-release mapping—see §4.3), AND the round baked into the tlock
    ciphertext stanza (read via the age/tlock header, no signature required)
    == SealedQub.drand_round exactly. The stanza round is the one that
    actually gates decryption; without this check a malicious creator could
    bind the ciphertext to an already-past round while displaying a future
    countdown, so anyone reading the stored bytes could decrypt before
    unlock_at. Implementations with no chain identity (test mocks) skip this
    check.
 7. Once current time ≥ SealedQub.unlock_at:
    a. Fetch drand round signature for SealedQub.drand_round from drand network.
    b. Compute B = tlock_decrypt(SealedQub.tlock_ciphertext, round_signature).
 8. Parse B → QubEnvelope.
 9. Validate QubEnvelope.version is known.
10. Verify: SHA3-256(QubEnvelope.body) == QubEnvelope.body_hash.
    Fail → integrity error.
11. Verify: QubEnvelope.qub_id == SealedQub.qub_id.
    Fail → integrity error.
12. Verify: QubEnvelope.unlock_at == SealedQub.unlock_at.
    Fail → integrity error.
12a. Verify: QubEnvelope.outcome_at == SealedQub.outcome_at (both absent, or
    both present and equal). Fail → integrity error.
12b. Content re-derivation. Recompute qub_id per §4.1 from the decrypted
    fields — (QubEnvelope.version, content_type, created_at, unlock_at,
    outcome_at, SealedQub.drand_round, QubEnvelope.body_hash,
    title_hash(SealedQub.title)) — and verify it equals SealedQub.qub_id.
    Fail → integrity error. The pairwise checks in steps 10-12a only prove
    the two layers agree with EACH OTHER; a forger who rewrites a bound
    field consistently on both surfaces (a pre-reveal title swap, or a
    post-round body swap with a recomputed body_hash re-encrypted to the
    same round under the same qub_id) passes them all. Only re-deriving
    the identity from content closes this.
13. Verify: QubEnvelope.content_type is known and renderable.
    Known values: 0x01 (text), 0x03 (pact), 0x04 (verdict).
    Unknown → display error.
14. If QubEnvelope.sig_alg != 0x00 → verify author signature (see §9.4).
15. If cosigner_pubkey or cosigner_signature present → verify cosigner (see §9.7).
16. Render content using the appropriate renderer (see §10 for text and §6 for pact/verdict).
17. Construct RevealedQub for display.

9. Outeurskap-ondertekening

9.1 Rasionaal

Elke qub word in permanente berging gestoor. Outeurskap-handtekeninge moet onbedrieglik bly vir onbepaalde tyd, en daarom gebruik v1.0 die post-kwantum ML-DSA-65 skema (FIPS 204) eerder as 'n klassieke skema waarvan die sekuriteit mag verswak binne die qub se permanente leeftyd.

9.2 Algoritme-register

sig_alg Skema Sleutelgrootte Handtekeninggrootte Status
0x00 Geen handtekening (ongeteken) — — Aktief
0x01 ML-DSA-65 (FIPS 204) 1 952 grepe 3 309 grepe Aktief
0x02 Ed25519 32 grepe 64 grepe Gereserveerde konstante; nie in protokol-v1 ondersteun nie

Protokol-v1-kykers MOET elke waarde buite {0x00, 0x01} verwerp, insluitend die gereserveerde 0x02-waarde. Reservering voorkom toevallige hergebruik; dit is nie aktivering nie. Aktivering vereis die beheerde verandering wat in §15 beskryf word.

9.3 Ondertekende preimage-konstruksie

Twee preimage-weergawes het bestaan. Alle handtekeninge MOET V2 gebruik, en verifieerders MOET slegs V2 aanvaar. Die verouderde V1-preimage (hieronder gedokumenteer vir historiese verwysing) is as 'n slegs-verifikasie terugval tydens die V2-migrasie aanvaar; daardie terugval is uit diens gestel en 'n slegs-V1-handtekening word nou verwerp.

V2 (huidig — geproduseer deur alle nuwe outeur-ondertekening, en deur albei handtekeninge van die verbond se verhoog- / mede-ondertekenvloei):

sig_input = SHA3-256(
    "QUB_AUTHOR_SIG_V2"  ||    // domain separator (17 bytes)
    version              ||    // u8 (1 byte)
    qub_id               ||    // [u8; 32] (32 bytes)
    body_hash            ||    // [u8; 32] (32 bytes)
    unlock_at            ||    // i64 big-endian (8 bytes)
    0x00                 ||    // u8 (1 byte): MUST be 0x00 in v1.x
    sender_label_hash    ||    // [u8; 32]: SHA3-256(NFC(sender_label)),
                               //   or 32 zero bytes when absent
    reply_to_or_zero           // [u8; 32]: parent qub_id, or 32 zero
                               //   bytes when absent
)

// Total preimage: 155 bytes → 32-byte hash

signature = Sign(author_secret_key, sig_input)

sender_label_hash volg dieselfde afwesig-sentinel-konvensie as title_hash (§4.2.1): 32 nul-grepe is nie 'n geldige SHA3-256-uitset nie, dus kan "afwesig" nooit met 'n teenwoordige etiket bots nie. Alle velde is vaste-breedte, dus is die preimage ondubbelsinnig sonder lengte-voorvoegsels.

V1 (verouderd — UIT DIENS GESTEL; nie meer geproduseer nie en nie meer aanvaar op verifikasie nie):

sig_input = SHA3-256(
    "QUB_AUTHOR_SIG_V1"  ||    // domain separator (17 bytes)
    version              ||    // u8 (1 byte)
    qub_id               ||    // [u8; 32] (32 bytes)
    body_hash            ||    // [u8; 32] (32 bytes)
    unlock_at            ||    // i64 big-endian (8 bytes)
    0x00                       // u8 (1 byte): MUST be 0x00 in v1.0
)

// Total preimage: 91 bytes → 32-byte hash

Die V1-preimage het sender_label en reply_to weggelaat. Dit is as 'n slegs-verifikasie terugval tydens die migrasie na V2 aanvaar; daardie terugval is sedertdien uit diens gestel — verifieerders MOET slegs die V2-preimage aanvaar. Die definisie word hier behou vir historiese verwysing en om die domein-skeier hieronder te verduidelik. 'n Handtekening wat slegs teen V1 verifieer MOET as 'n verifikasie-mislukking hanteer word.

Domein-skeiers: "QUB_AUTHOR_SIG_V1" / "QUB_AUTHOR_SIG_V2" is elk 17 ASCII-grepe ([0x51, 0x55, 0x42, 0x5F, 0x41, 0x55, 0x54, 0x48, 0x4F, 0x52, 0x5F, 0x53, 0x49, 0x47, 0x5F, 0x56, 0x31/0x32]). Geen vulling. Die verskillende skeier skei die twee konstruksies domein-gewys, dus kan 'n handtekening oor die een preimage nooit as die ander verifieer nie.

org_id_present-greep: die greep ná unlock_at MOET 0x00 wees. Die verwysing-implementasie ontbloot dit as die konstante ORG_ID_PRESENT_INDIVIDUAL = 0x00 in crates/qub-core/src/signing.rs; kykers wat sig_input vir verifikasie herbou MOET dieselfde greep uitstuur.

Handtekening-omvang — wat gedek word en wat nie. Die V2-sig_input verbind direk tot version, qub_id, body_hash, unlock_at, sender_label, en reply_to (plus die vaste domein-skeier en org_id_present-greep). qub_id word self afgelei van version, content_type, created_at, unlock_at, outcome_at, drand_round, en body_hash via die §4.1 preimage, dus enige verandering aan daardie velde lewer 'n verskillende qub_id en maak die handtekening transitief ongeldig. Die geverifieerde oppervlak is dus:

Veld Geverifieer deur handtekening Hoe
version ✓ Direkte inset na sig_input
qub_id ✓ Direkte inset
body_hash ✓ Direkte inset
unlock_at ✓ Direkte inset
sender_label ✓ Direkte inset via sender_label_hash (V2-preimage — die enigste aanvaarde vorm)
reply_to ✓ Direkte inset via reply_to_or_zero (V2-preimage — die enigste aanvaarde vorm)
content_type ✓ Transitief, via qub_id-preimage
created_at ✓ Transitief, via qub_id-preimage
outcome_at ✓ Transitief, via qub_id-preimage
drand_round ✓ Transitief, via qub_id-preimage
body ✓ Transitief, via body_hash = SHA3-256(body)
author_pubkey — (implisiet) Sleutel wat die handtekening geverifieer het is per definisie die outeur
cosigner_pubkey / cosigner_signature — Onafhanklik onderteken oor dieselfde sig_input (sien §9.7)
drand_chain_id, tlock_ciphertext, visibility — Buitenste SealedQub-velde, nie binne die koevert nie — gedek deur hul eie strukturele invariante (rondte / ketting konsekwentheid) maar nie deur die outeur-handtekening nie. (drand_round word nou transitief gebind via die qub_id-preimage — sien hierbo.)

Waarom V2 die enigste aanvaarde preimage is.

Implementasies wat sender_label of reply_to aan eind-gebruikers vertoon MOET die geverifieerde identiteit (publieke-sleutel vingerafdruk, bevestiging) as die primêre identiteit-sein vertoon, nie die etiket nie.

9.4 Verifikasie-prosedure

1. Read sig_alg from QubEnvelope.
2. If sig_alg == 0x00 → unsigned. No verification. Display "unsigned qub."
3. If sig_alg is unknown → reject. Display "unrecognised signature scheme."
4. Extract author_signature and author_pubkey. If either is absent → integrity error.
5. Reconstruct sig_input using fields from QubEnvelope (V2 formula, §9.3).
6. Verify(author_pubkey, sig_input, author_signature). The V2 preimage is the
   only accepted form — the legacy V1 fallback is retired (§9.3), so a
   signature that does not verify against V2 fails, full stop.
7. If verification succeeds → display "signed by [key fingerprint]."
8. If verification fails → display "signature verification failed."

Handtekening-verifikasie is die mees duur bewerking (veral ML-DSA-65). Dit BEHOORT uitgevoer te word nadat alle goedkoper kontroles (hash, qub_id, unlock_at) geslaag het.

9.5 Identiteit-bevestigings

Identiteit-bevestigings — die kartering van author_pubkey na mens-herkenbare identiteit-eise soos 'n qub-handvatsel, e-posadres, sosiale handvatsel, of passleutel-geloofsbrief — is 'n kyker-kant progressiewe verbetering en word nie vereis vir handtekening-verifikasie nie. Kykers wat bevestigings na 'n vertoon-identiteit oplos MOET die voorrang toepas:

handle > email > social > fingerprint

Die vingerafdruk-terugval is die kleinletter-hex van SHA3-256(author_pubkey); dit is altyd beskikbaar vir enige onderteken-qub. Kykers MAG dit afkort vir vertoning — die verwysing-kyker lewer qub: gevolg deur die eerste en laaste vier grepe (qub:<8 hex>…<8 hex>).

'n Konformerende verifieerder kan elke kontrole in §9.4 voltooi sonder om die qub-API te kontak, sonder enige netwerk buite permanente berging en drand, en sonder enige bediener-kant opsoek. Bevestiging-oplossing is 'n aparte beste-poging stap wat slegs uitgevoer word nadat handtekening-verifikasie geslaag het.

9.6 Grootte-impak

Ed25519 ML-DSA-65
Handtekening 64 grepe 3 309 grepe
Publieke sleutel 32 grepe 1 952 grepe
Totaal per qub 96 grepe 5 261 grepe
Bergingskoste delta (teen ~$5/MB) ~$0.0005 ~$0.026

Vir 'n teks-qub van 500–2 000 grepe, verdriedubbel ML-DSA-65 ongeveer die gestoorde grootte. Die absolute koste is weglaatbaar.

9.7 Mede-ondertekenaar-verifikasie (verbond bilaterale ooreenkomste)

Vir bilaterale ooreenkomste (content_type = 0x03) bewys 'n tweede handtekening-laag dat albei partye tot dieselfde voorwaardes ingestem het.

Koevert-velde:

Beide velde MOET saam teenwoordig wees of albei afwesig. As presies een teenwoordig is, MOET kykers 'n integriteitsfout rapporteer.

Verifikasie-prosedure:

1. If cosigner_pubkey absent and cosigner_signature absent → no cosigner. Done.
2. If exactly one is present → integrity error.
3. Verify cosigner_pubkey != author_pubkey (prevent self-cosigning).
   Fail → display "cosigner pubkey must differ from author."
4. Reconstruct sig_input using the same formula as §9.3 (V2 only — the
   legacy V1 fallback is retired; all pact clients produce V2 signatures).
5. Verify(cosigner_pubkey, sig_input, cosigner_signature).
6. Success → display "co-signed by [cosigner fingerprint]."
7. Failure → display "co-signature verification failed."

Eienskappe:

E-pos-bindings hek (operasioneel). Wanneer 'n verhoogde verbond 'n Party B e-pos-kontak dra (§6.1), MOET die qub-oplaaidiens die mede-ondertekening-versoek weier tensy 'n kort-leef e-pos-verifikasie merker bestaan wat ooreenstem met beide die verhoog-id en die genormaliseerde-e-pos hash van daardie kontak. Die merker word deur /api/v1/auth/verify geskryf wanneer die magiese-skakel teken 'n staging_id dra en die geverifieerde adres ooreenstem met SHA-256(normalise_email(party_b.contact)) — waar normalise_email(addr) die plaaslike-deel kas behoue hou en slegs die domein-deel na kleinletter omskakel (per RFC 5321 §2.3.11), en SHA-256 hier is die NIST FIPS 180-4 hash (onderskeie van die SHA3-256 wat in §4-afleidings gebruik word) — en verloop 900 sekondes (15 minute) ná uitgifte. Dit is 'n operasionele anti-personifikasie hek, NIE deel van die op-ketting qub-bewys nie — 'n derde-party verifieerder wat §11 herspeel benodig slegs permanente berging en drand, sonder enige bediener-kant opsoek. Die merker bestaan slegs bediener-kant en is nooit deel van die onderteken-liggaam nie.

Grootte-impak (ML-DSA-65 outeur + mede-ondertekenaar):

Komponent Grootte
Outeur-handtekening 3 309 grepe
Outeur publieke sleutel 1 952 grepe
Mede-ondertekenaar-handtekening 3 309 grepe
Mede-ondertekenaar publieke sleutel 1 952 grepe
Totale kripto-bo-koste 10 522 grepe
Bergingskoste delta ~$0.05

10. Markdown-lewering en saniteer

Hierdie afdeling is sekuriteit-krities. Die kyker lewer teks-qubs (content_type = 0x01) met 'n beperkte Markdown-subversameling.

10.1 Toegelate elemente

10.2 Verbode elemente

Element Hantering
Rou HTML (<div>, <script>, ens.) Heeltemal verwyder. Geen HTML gaan deur.
Beelde (![alt](url)) Verwyder. Beeld-sintaks word uit uitset verwyder.
Skakels ([text](url)) URL gelewer as sigbare plat teks. Nie outomaties geskakel nie. Nie klikbaar sonder eksplisiete gebruikersaksie nie.
Gevaarlike URL-skemas javascript:, data:, vbscript:, file: — verwyder.
Iframes, ingebed, objekte Verwyder.
HTML-entiteite Gedekodeer na vertoon-karakters slegs as veilig.

10.3 Implementasie

Implementasies MOET 'n streng toelaatlys-parser gebruik, nie 'n bloklys nie. Die aanbevole benadering:

  1. Ontleed Markdown met pulldown-cmark (of ekwivalent).
  2. Loop deur die AST en laat val enige nood wat nie in die toelaatlys is nie (§10.1).
  3. Vir skakel-nodes: stuur die URL as sigbare teks uit, nie as 'n klikbare <a>-element nie.
  4. Skakel die gefiltreerde AST om na 'n getipte tussen-verteenwoordiging (bv., 'n MarkdownNode-enum met slegs veilige variante). Rou HTML is struktureel onverteenwoordigbaar in hierdie IR.
  5. Lewer van die getipte IR na die teiken-aansig laag (bv., reaktiewe aansig-komponente, DOM-nodes). Geen HTML-string aaneenskakeling of innerHTML op enige punt nie.

Bloklys-benaderings is brokkelig omdat nuwe Markdown-uitbreidings of parser-eienaardighede ongefiltreerde elemente kan invoer. Die getipte-AST benadering maak XSS struktureel onmoontlik — daar is geen variant wat arbitrêre HTML kan dra nie.

10.4 Grootte- en struktuur-limiete


11. Derde-party verifikasie

Enige derde party wat die gestoorde grepe besit (en K vir 'n private/omhulde qub), kan die kriptografiese artefak sonder qub se samewerking verifieer. 'n Onafhanklik getydstempelde bestaan-aanspraak vereis boonop óf geverifieerde permanenteberging-insluiting per qub óf 'n geverifieerde §16-deursigtigheidslog-bewys.

1. Obtain the stored bytes. For a private delivery, also obtain K from the
   delivery-link fragment.
2. Resolve the delivery shape (§8 step 3a): unwrap OuterWrapper with K, or
   accept bare SealedQubCbor. Require shape/visibility agreement.
3. Parse SealedQubCbor → SealedQub; validate protocol version, visibility,
   content type, chain identity, and structural bounds.
4. Recompute expected_round from unlock_at (§4.3); require the stored round
   (allowing the documented legacy minus-one case) and ciphertext-stanza round
   to agree exactly as §8 step 6a specifies.
5. Obtain the drand signature for SealedQub.drand_round and verify its BLS
   signature against the pinned chain public key.
6. tlock_decrypt(tlock_ciphertext, round_signature) → QubEnvelope CBOR bytes.
7. Parse → QubEnvelope.
8. Verify SHA3-256(body) == body_hash.
9. Verify envelope/sealed equality for qub_id, unlock_at, and outcome_at.
10. Recompute qub_id from the decrypted fields, sealed drand_round, and title;
    require it to equal the carried qub_id (§4.1).
11. If sig_alg != 0x00, verify the V2 author signature (§9.4). If cosigner
    fields are present, verify their pairing, key separation, and signature
    (§9.7).
12. For an existence-time claim, independently verify either:
    a. the permanent-storage transaction's data-to-id binding, owner, block
       inclusion, and block timestamp; or
    b. a §16 inclusion proof through its pinned anchor and anchor block time.
13. Report each verified claim separately; do not collapse an absent storage
    or anchor proof into a successful timing verdict.

Wat verifikasie bewys:

Bewysinvoer Wat dit vasstel
Geldige bundel / verseëlde artefak + drand-handtekening Die herwonne liggaam stem met body_hash ooreen; die metadata wat in qub_id gebind is, is ongeskonde; die syferteks is aan die verklaarde drand-rondte gebind; en daardie rondte het verstryk. Dit stel nie vas wanneer die syferteks geskep is nie.
Geldige V2-outeur-/mede-ondertekenaarhandtekening Die houer(s) van die ooreenstemmende geheime sleutel(s) het die ondertekende oppervlak in §9.3 gewaarmerk.
Onafhanklik geverifieerde bergingstransaksie per qub Die presiese gestoorde syferteks het nie later as sy bloktydstempel bestaan nie.
Geldige geankerde deursigtigheidslog-bewys Die blaartipe-spesifieke aanspraak in §16.11, insluitend 'n boonste grens vir verbintenistyd vanaf die ankerblok.

Wat verifikasie NIE bewys nie:

Nie-bewys Hoekom
Outeurskap Die sender_label is dekoratief. Sonder sig_alg ≥ 0x01 kon enigiemand hierdie inhoud verseël het.
Bedoeling Die artefak bewys grepe en kriptografiese verwantskappe, nie wat die skepper subjektief bedoel het nie.
Voorafbestaande verbintenis uit .qub alleen 'n Skepper kan 'n geldige bundel saamstel nadat die gebinde rondte verstryk het. Die ingebedde drand-handtekening bewys dat die rondte verstryk het, nie dat die syferteks voor dit bestaan het nie.
Presiese tyd van die verseëlknoppie 'n Bergings- of ankerbloktydstempel is 'n onafhanklik verifieerbare boonste grens en kan agter die gebruiker se plaaslike aksie bly. sealed_at-/received_at-stellings is nie bewyskragtig nie.

Die geïmplementeerde deursigtigheidslog (§16) brei verifikasie oor qubs uit met peuteraanduidende ordening en 'n vertrouenslose boonste grens vir verbintenistyd (die ankerbloktyd), afgebaken volgens blaartipe (§16.11). Dit voeg nie outeurskap of bedoeling by nie; vir die verstek-greepblinde-oplaaipad bewys dit nie self body_hash of drand_round nie—dit kom steeds uit die artefakkontroles.


12. Weergawe- en vrystellingsbeheer

Dokumentvrystellings, die binneste draadprotokol en die buitenste omhulsel is afsonderlike weergawe-ruimtes. 'n Dokument-alleen-verduideliking verander dus nie stilweg grepe nie, en 'n toekomstige draadmigrasie kan nie as 'n redaksionele hersiening voorgedoen word nie.

12.1 Dokumentvrystellingsweergawe

Hierdie spesifikasie gebruik semantiese dokumentvrystellings (MAJOR.MINOR.PATCH) en 'n onveranderlike Git-merker genaamd protocol-v<release>.

Vrystellingstatus is een van Konsep (nog nie normatief nie), Huidig (die enigste aanbevole implementeringsteiken) of Vervang (vir historiese verifikasie behou). Die ongeversioneerde /protokol-roete vertoon die Huidige vrystelling; die vrystellingsmerker bewaar die presiese bron en elke lokaal wat daarmee gepubliseer is. 'n Status- of vrystellingsnommerverandering vereis dat hierdie tabel en die vrystellingsgeskiedenis in dieselfde hersiene verandering opgedateer word.

Dokumentvrystelling Effektiewe datum Status Draadprotokol Omhulsel Bron
1.0.0 2026-09-23 Huidig 0x01 0x01 protocol-v1.0.0

12.2 Protokolweergawe

Die version-veld (u8) in beide SealedQub en QubEnvelope identifiseer die hoof protokol-weergawe.

12.3 Protokolweergawegeskiedenis

Weergawe Waarde Beskrywing
v1 0x01 Private/omhulde en openbare/kaal aflewering; teks- (0x01), verbond- (0x03) en uitspraakliggame (0x04); ML-DSA-65 V2-outeur-/mede-ondertekening; drand-quicknet-tlock; SHA3-256.

12.4 Voorwaartse versoenbaarheid

'n v1-kyker wat 'n QubEnvelope met onbekende CBOR-kaart-sleutels teëkom (sleutels nie in die §3.2 kanonieke orde nie) MOET dit met 'n dekodeer-fout verwerp (§3.1). Voorwaartse versoenbaarheid berus op die version-veld, nie op sleutel-toleransie nie: toekomstige toevoegings — selfs geringe metadata — verskyn onder 'n nuwe version-waarde, wat 'n v1-kyker met 'n duidelike "nuwer protokol"-fout verwerp eerder as om inhoud waartoe die handtekeninge verbind stilweg te laat val.

'n v1-kyker wat sig_alg = 0x01 (ML-DSA-65) teëkom maar ML-DSA-65 verifikasie-ondersteuning kortkom BEHOORT die qub-inhoud met 'n "handtekening teenwoordig maar nie verifieerbaar nie" kennisgewing te vertoon, nie die qub geheel te verwerp nie. Die verwysing-implementasie verwerp vandag elke sig_alg-waarde anders as 0x00 en 0x01 omdat die v1-register geen ander geldige algoritme bevat nie — streng verwerping en sag-misluk is waarnemingsgewys identies tot 'n derde algoritme geregistreer word. Die sag-misluk gedrag hierbo word dra-belastend sodra §9.2 'n nuwe inskrywing toelaat, en die verwysing-kyker sal op daardie punt opgedateer word om sag te misluk.

12.5 Buitenste omhulselweergawe

Die OuterWrapper wat in §13 beskryf word dra sy eie version-greep, onafhanklik van SealedQub.version en QubEnvelope.version. Die twee weergawe-ruimtes ontwikkel afsonderlik: 'n toekomstige post-kwantum-veilige simmetriese vervanging stamp die omhulling-greep sonder om die binne-protokol-weergawe aan te raak, en 'n toekomstige protokol-laag toevoeging (bv., 'n nuwe koevert-veld) stamp die binne-weergawe sonder om die omhulling-greep aan te raak.

OUTER_WRAPPER_VERSION_* Waarde Algoritme Status
OUTER_WRAPPER_VERSION_1 0x01 AES-256-GCM met 12-greep nonce, 16-greep verifikasie-etiket, AAD gebind aan qub_id Aktief vir private aflewering
— 0x02–0xFF Gereserveer Toekoms

Kykers MOET onbekende omhulling-weergawes met 'n duidelike fout verwerp. Die protokol hou die omhulling-weergawe-ruimte opsetlik nou tot 'n konkrete migrasie-dryver verskyn (bv., NIST-leiding wat 'n ander AEAD bevoordeel); 'n 0x02-gleuf sal in dieselfde hersiening toegeken word wat die algoritme inlei.


13. Buitenste enkripsie-omhulling

13.1 Rasionaal

Die protokol-lae (QubEnvelope → tlock → SealedQub) maak 'n verseëlde qub tyd-geslote: die liggaam is onleesbaar totdat unlock_at en die drand-rondte handtekening gepubliseer is. Ná ontsluiting is die rondte-handtekening egter openbaar en die kanonieke CBOR-vorm van SealedQub is herkenbaar, sodat 'n oeser wat permanente-berging-transaksies geïndekseer het die hele qub-korpus grootmaat sou kon dekripteer.

Vir private aflewering sluit die buitenste enkripsie-omhulsel daardie kanaal deur 'n bykomende simmetriese AEAD-laag tussen die kanonieke SealedQubCbor en die gestoorde grepe te plaas. In die blaaier-verseëlpad leef die 256-bis-sleutel K slegs in die URL-fragment van die aflewerings-URL en op gebruikerstoestelle; blaaiers stuur nie URL-fragmente na bedieners nie, dus is qub.social, elke bergingspoort en elke CDN voor een van hulle blind vir K. 'n Private qub se gestoorde voorstelling is daarom ondeursigtige syferteks waarvan die skoonteks nie sonder die URL wat die skepper gekies het om te deel, herwin kan word nie. Openbare aflewering laat hierdie laag doelbewus weg (§13.8).

Netto effek:

13.2 Lae

plaintext body                       ← QubEnvelope.body (§2.2)
  ↓ canonical CBOR (§3)
envelope CBOR
  ↓ tlock encrypt to drand round (§7 step 10)
tlock_ciphertext (inside SealedQub) (§2.3)
  ↓ canonical CBOR (§3)
SealedQubCbor bytes                  ← inner wire artifact
  ├─ public (visibility=0x01) ───────────────▶ stored directly (§13.8)
  └─ private (visibility=0x00)
       ↓ AES-256-GCM(K, nonce, AAD=qub_id) (§7 step 12, this section)
     OuterWrapper CBOR bytes         ← stored private payload

Verseël en ontsluit by die protokol-laag (§7, §8) is onveranderd onder die omhulling-grens; die omhulling heg aan by die oproep-plek van seal() en ontkoppel by die oproep-plek van unlock().

13.3 OuterWrapper-datastruktuur

struct OuterWrapper {
    version:    u8,           // 0x01, see §12.5
    qub_id:     [u8; 32],     // copied from inner SealedQub; AEAD AAD
    nonce:      [u8; 12],     // 96-bit AEAD nonce
    ciphertext: Vec<u8>,      // AES-256-GCM(K, nonce, SealedQubCbor, AAD=qub_id) || 16-byte tag
}

Veld-invariante.

CBOR-enkodering. Kanonieke CBOR per §3, met dieselfde sleutel-orde reël (gesorteer volgens gekodeerde greep-lengte stygend, dan leksikografies). Die vier sleutels is:

Sleutel Gekodeerde grepe Orde
nonce 6 1
qub_id 7 2
version 8 3
ciphertext 11 4

Die eerste greep van die OuterWrapper CBOR is dus die definitiewe-lengte kaart-kop vir 'n 4-inskrywing kaart (0xA4).

13.4 AAD-binding aan qub_id

Die omhulling bind qub_id as AEAD bykomende geverifieerde data. Dit is die dra-belastende strukturele verdediging teen drie klasse aanvalle:

Aanval Verdediging
Skuif ciphertext onder 'n verskillende qub_id-veld in die omhulling AAD-wanverhouding → AEAD-verifikasie misluk
Meng die URL-fragment van qub A met die gestoorde grepe van qub B Verkeerde sleutel (en onafhanklik gebinde AAD) → AEAD-verifikasie misluk
Peuter met die qub_id-veld van die omhulling ná oplaai AAD-wanverhouding → AEAD-verifikasie misluk

Om qub_id in die omhulling-plaintext te dra verswak nie enumerasie-immuniteit op betekenisvolle wyse nie — qub_id is self 'n SHA3-256 hash van die §4.1 preimage met geen herwinbare preimage van die spysvertering nie, en 'n enumereerder wat reeds die omhulling-grepe geoes het leer niks van die sigbare qub_id wat hulle nie reeds van die bestaan van die oplaai self kon aflei nie.

13.5 Omhulling- en uitpak-algoritmes

wrap_sealed_qub(SealedQubCbor S, qub_id Q, key K, nonce N):
    require K.len() == 32 and N.len() == 12 and Q.len() == 32
    I := canonical_cbor_decode(S) as SealedQub
    require I.qub_id == Q               // reject mismatched caller AAD
    C := AES_256_GCM_encrypt(key=K, nonce=N, msg=S, aad=Q)
    // C includes the 16-byte authentication tag at the end
    return canonical_cbor_encode(OuterWrapper{
        version:    0x01,
        qub_id:     Q,
        nonce:      N,
        ciphertext: C,
    })

unwrap_sealed_qub(OuterWrapper bytes W, key K):
    require K.len() == 32
    O := canonical_cbor_decode(W) as OuterWrapper
    require O.version == 0x01           // §12.5
    P := AES_256_GCM_decrypt(
            key=K, nonce=O.nonce, ciphertext=O.ciphertext, aad=O.qub_id
         )
    // any AEAD failure → DECRYPT_FAILED, indistinguishable to caller
    S := canonical_cbor_decode(P) as SealedQub
    require S.qub_id == O.qub_id       // explicit inner/outer cross-check
    return P                            // P is the validated SealedQubCbor

Mislukking-modus inval. Verkeerde K, verkeerde nonce, AAD-wanverhouding, en gepeuterde ciphertext lewer almal dieselfde DECRYPT_FAILED fout. Dit is 'n opsetlike AEAD-eienskap: om die mislukking-modus te onderskei sou 'n sykanaal skep wat 'n afstand-aanvaller kon ondersoek deur misvormde omhullings te stuur en die antwoord te tydsbereken. Verwysing-implementasies MOET alle AEAD-mislukkings tot 'n enkele fout-vorm laat inval.

13.6 Sleutelmateriaal en verspreiding

Die omhullingssleutel K is 'n 256-bis eenvormige willekeurige waarde wat per-qub deur 'n CSPRNG gegenereer word. Die verwysing-implementasies bron dit uit:

Verspreiding: K MOET URL-veilig base64 (RFC 4648 §5, geen vulling) gekodeer word en aan die afleweringskakel as die fragment-komponent aangeheg word:

delivery_url = <origin>/c/<arweave_tx_id>#<base64url(K)>

Die fragment word nooit deur 'n konformerende blaaier na enige bediener gestuur nie. Herwinning-kanale (bediener-kant geskiedenis-indeks, kies-in e-pos outo-stuur) wat die volle afleweringskakel — insluitend die fragment — buite die gebruiker se toestel volhou is 'n eksplisiete handelsverkeer teen die verstek kripto-versnipper postuur en MOET op eksplisiete gebruiker-toestemming gehek wees.

Fragmentverlies. As 'n gebruiker die URL-fragment verloor en geen herwinningskanaal het nie, is die qub onleesbaar. Dit is die dragende kompromis van die ontwerp en MOET by verseëltyd aan die gebruiker bekend gemaak word. Die verwysingstoepassing versterk die verseëltyd-bekendmaking met uitdruklike “stoor hierdie URL”-kopie en 'n geverifieerde-e-pos-herwinningskanaal vir gebruikers wat kies om deel te neem.

13.7 Buite omvang vir hierdie afdeling

13.8 Openbare qubs (omhulling-weglating)

Die buitenste omhulling is opsioneel by die aflewering-laag. 'n Skepper kan 'n qub as openbaar verseël, in welke geval die kanonieke SealedQubCbor direk die bergingspyplyn binnegaan, sonder 'n OuterWrapper-laag en sonder sleutel K:

SealedQubCbor bytes  ──(public)──▶  stored as-is
SealedQubCbor bytes  ──(private)─▶  AES-256-GCM(K, …) ▶ OuterWrapper ▶ stored

'n Openbare qub is tyd-geslote maar nie skakel-gehek nie: dit bly onleesbaar totdat sy drand-rondte publiseer (die tlock-laag is onveranderd), maar ná ontsluiting kan enigeen wat die bergingstransaksie-ID het dit dekripteer — geen URL-fragment word vereis nie, omdat daar geen K is nie. Dit is die opsetlike kompromis vir oppervlakke wat die bediener moet dryf: onthul-kennisgewing-e-posse, fragmentlose oEmbed-/outomatiese inbeddingskakels en ryker ná-onthul SEO benodig almal 'n skakel wat werk sonder 'n geheim wat die bediener nooit hou nie (§13.6). 'n Private qub kan steeds die uitdruklike <qub-embed src="full_delivery_url">-vorm gebruik wanneer die uitgewer sy volledige fragmentbevattende bevoegdheid verskaf.

Gevolge waarvoor 'n produsent rekening MOET hou:

Privaat (omhul) bly die verstek; openbaar is 'n eksplisiete per-qub skepper-keuse.


14. Toets-vektore

14.1 qub_id-afleiding

Input:
  version      = 0x01
  content_type = 0x01
  created_at   = 1735689600 (2025-01-01 00:00:00 UTC)
  unlock_at    = 1736294400 (2025-01-08 00:00:00 UTC)
  outcome_at   = absent
  drand_round  = 4695446  (= floor((1736294400 - 1595431050) / 30) + 1, §4.3 mapping, drand mainnet params §14.2)
  body         = "Hello, future."  (UTF-8, 14 bytes)
  title        = absent

Intermediate:
  body_hash  = SHA3-256("Hello, future.")
             = 76ab8b3f843c6ed4f2d0fd75b9f457b4
               ad49dd4450f9c22723ae430e3af3211d
  title_hash = [0u8; 32]   (title absent — §4.2.1 sentinel)

Domain separator (10 bytes):
  [0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00]

Preimage (108 bytes—current protocol v1):
  domain_separator    ||  // 10 bytes
  0x01                ||  // version
  0x01                ||  // content_type
  0x0000000067748580  ||  // created_at as i64 big-endian (1735689600)
  0x00000000677DC000  ||  // unlock_at as i64 big-endian (1736294400)
  0x0000000000000000  ||  // outcome_at_or_zero (outcome_at absent)
  0x000000000047A596  ||  // drand_round as u64 big-endian (4695446)
  body_hash           ||  // 32 bytes
  title_hash              // 32 bytes (all-zeros sentinel; title absent)

Expected output:
  qub_id = SHA3-256(preimage)
         = 4a84e3dfaec32954949c30073f8e6506
           fd3204c1bb97f9162b81c7587afe412e

Implementasies MOET identiese body_hash- en qub_id-waardes vir hierdie inset lewer. Hierdie toetsvektor BEHOORT die eerste eenheidstoets te wees wat geskryf word. Die kanonieke waardes hierbo is deur die verwysingsimplementasie bereken en MOET greep-vir-greep ooreenstem. Historiese prototipe-uitlegte voor bekendstelling (geen lewende qubs het van die eerste twee afgehang nie) het 92 grepe voor outcome_at (3d9fc2390eab043d38a1669ed3b71be76f9eefe872b9569ab1aaa027b88392b0) en 100 grepe ná die toevoeging van outcome_at_or_zero (b0d032898ad629795150fdcb3f84e518f59ed05b7a2a82bc24ebdb87f52144ed) gebruik. Die huidige 108-greep-uitleg het daarna drand_round en die QUB_ID_V2-domeinskeier bygevoeg. 'n Vroeë 108-greep-vektor het die verouderde ceil-rondtekartering (drand_round = 4695445) gebruik en 3a9fcb31b750d985c262fada6d4f777fd6a28be831d941d85c131f5a4bbaf8a4 opgelewer—steeds 'n geldige qub_id vir daardie rondteinvoer, terwyl die voorbeeld hierbo die huidige-rondte-kartering in §4.3 volg.

14.2 Ontsluitings-rondte kartering

Input:
  unlock_at           = 1735689600
  chain_genesis_time  = 1595431050
  chain_period_seconds = 30

Calculation:
  (1735689600 - 1595431050) / 30 = 4675285.0
  floor(4675285.0) + 1 = 4675286

drand_round = 4675286

Rondte 4675286 word by 1595431050 + (4675286 - 1) * 30 = 1735689600 gepubliseer—presies by unlock_at, nooit daarvoor nie. (Die verouderde voorvrystelling-ceil-kartering het 4675285 opgelewer, gepubliseer by 1735689570—30 sekondes vroeg; verifieerders aanvaar daardie verouderde rondte volgens §4.3.)

14.3 Kanonieke CBOR rondreis

Implementasies MOET verifieer dat serialize(parse(serialize(qub))) == serialize(qub) vir alle geldige insette. Dit is 'n eienskap-toets, nie 'n enkele vektor nie.

14.4 PactTerms CBOR (content_type 0x03)

Input:
  pact_version = 1
  title        = "Scooter deposit"
  terms        = [
    { key: "Item",    value: "Honda Metropolitan scooter" },
    { key: "Price",   value: "$100" },
    { key: "Deposit", value: "$10" }
  ]
  party_a      = { label: "Alice" }
  party_b      = { label: "Bob", contact: "bob@example.com" }
  notes        = absent

Canonical CBOR key order (PactTerms):
  "notes"(6) < "terms"(6) < "title"(6) < "party_a"(8) < "party_b"(8) < "pact_version"(13)

Canonical CBOR key order (PactTerm):
  "key"(4) < "value"(6)

Canonical CBOR key order (PartyIdentifier):
  "label"(6) < "contact"(8)

Die kanonieke CBOR-grepe en SHA3-256 body_hash word deur die verwysing-implementasie bereken. Implementasies MOET greep-identiese CBOR vir hierdie inset lewer.

Implementasies MOET ook verifieer dat serialize(parse(serialize(pact))) == serialize(pact) vir alle geldige PactTerms-insette (eienskap-toets).

14.5 Buitenste omhulling kruis-taal vektore

Die buitenste omhulling (§13) het 'n aparte kanonieke vasstelling by crates/qub-core/tests/vectors/wrapper_v1.json. Elke geval pen 'n (key, nonce, qub_id, sealed_cbor)-tupel vas as ondeurskynende hex-insette en beweer 'n spesifieke expected_wrapper_hex-uitset. Albei verwysing-implementasies verbruik dieselfde JSON-lêer:

Die vasstelling pen tans drie laevlak-omhullingsgevalle vas. Hulle toets deterministiese OuterWrapper-enkodering en AEAD-interoperabiliteit onafhanklik van die §13.8-afleweringsvorm-invariant; in die besonder maak die historiese naam basic-text-public en sy binneste visibility = 0x01 nie die gevolglike omhulde grepe 'n voldoenende openbare aflewering nie. 'n Vervaardiger MOET steeds openbare binneste grepe kaal berg en slegs private (0x00) binneste grepe omhul.

Geval Dekking
basic-text-public Historiese laevlak-fixture-naam. Kleinste realistiese SealedQub-vorm, sonder opsionele velde; toets slegs omhullingsgrepe en is nie 'n voldoenende §13.8-bergingsaflewering nie.
with-recipient-pubkey SealedQub met recipient_pubkey gestel (gereserveerde toekomstige pad). Oefen 'n ander binneste CBOR-sleutelstel; die afsonderlike fixture-inhoud daarvan lewer onafhanklik 'n ander qub_id (die recipient_pubkey self is nie in die §4.1-preimage nie).
longer-body ~4 KiB liggaam — oefen multi-greep CBOR lengte-voorvoegsels binne beide die binne-koevert en die buitenste ciphertext.

Implementasies MOET greep-identiese expected_wrapper_hex vir die opgeneemde insette lewer. Om die vasstelling te herskep vereis QUB_REGEN_VECTORS=1 cargo test -p qub-core --test wrapper_vectors en is gereserveer vir opsetlike formaat-veranderings.


15. Kripto-profiel-bestuur (toekoms)

Hierdie afdeling is informatief vir v1 en word normatief die eerste keer dat 'n tweede algoritme enige van qub se kriptografiese primitiewe binnegaan.

15.1 Huidige postuur

Protokol v1 bind presies een algoritme per primitief:

Verifieerders hardkodeer tans sleutel- en handtekeninglengtes per aktiewe primitief. Die sig_alg- en omhulselweergawagrepe is uitdruklike keurders, maar v1 voer geen inband-onderhandeling uit nie en laat slegs die aktiewe waardes hierbo toe.

15.2 Beoogde vorm

Wanneer 'n tweede algoritme die protokol binnegaan, sal die verifieerder gekonfigureer word vir 'n benoemde CryptoProfile (bv., ExqubV1) wat die presiese stel toegelate waardes per primitief lys — sig_algs, drand-kettings, omhulling-weergawes, inhoudstipes. Die profiel is by verifikasie-tyd vasgestel, nooit in-baand onderhandel nie. Enige waarde buite die aktiewe profiel word verwerp.

Dit waarborg dat die toevoeging van ML-DSA-87 of die aktivering van Ed25519 nie retrospektief bestaande verifieerder-konfigurasies kan verswak nie: 'n v1-verifieerder bly 'n v1-verifieerder selfs nadat 'n v2-profiel gepubliseer is.

15.3 Snellervoorwaardes

Bevorder §15 na normatiewe status wanneer enige van die volgende voorgestel word:

Tot dan is §15 'n plekhouer wat die migrasie-vorm vasstel sodat toekomstige PR's teen 'n bekende teiken land eerder as om die onderhandelings-oppervlak van vooraf te herbedink.


16. Deursigtigheidslogboek en Duursaamheidsvlakke (Geïmplementeer — hersiening voltooi)

Status. Hierdie afdeling is geïmplementeer (W5/UP-B1, Fases 1–8), met die vervaardiger en trust-root omvang hier aangedui. Die draadformate, hashing en verifieerderpaaie is lewendig: die kern Merkle + kanonieke-CBOR-tipes (qub-core), die TypeScript-spieël + ANS-104-bundelaar (workers/api/src/crypto/), die enkel-skrywer LogDO + koördinaat-sleutel R2-knoopstoor, die /upload log-byvoegpoging, die daaglikse anker + bundel-drain krome, die GET /api/v1/qub/:tx_id/proof (insluiting) en GET /api/v1/log/consistency (RFC 9162) bewyseindpunte, die getipeerde insluitingsbewys wat in die .qub-bundel gedra word (§17.5), die inheemse ANS-104 ankerverifieerder (tools/qub-verify), en die dubbele self-gepubliseerde-koppe haak (§16.6). 'n Suksesvolle /upload is altyd R2-duursaam, maar word slegs log-gedek wanneer LOG_DO gekonfigureer is en die inlyn-byvoeging slaag; eers dan dra sy reaksie log_seq, receipt en anchor_status. As RECEIPT_SK afwesig of ongeldig is, is daardie kwitansie se sig_b64url leeg en bied geen nie-weerlegging nie. Die huidige /seal- en paktpublikasiepaaie skeduleer individuele Arweave-transaksies, maar voeg nie 'n logbladsy by nie. Geen kode voer tans die /upload kommentaar se voorgestelde latere rekonsiliasie uit na 'n byvoegingsfout nie. Die W5 eksterne hersiening is voltooi: §16.15 registreer ontwerpbesluite en lanseringsbeperkings, maar daardie beperkings verbreed nie die vervaardigerdekking wat pas genoem is nie. Drie trust-/ontplooiingsitems bly gegated: (a) die toegewyde ankerbeursie (ANCHOR_JWK; LogProfile.anchor_owner steeds die [0xAB; 32] plekhouer); (b) die kwitansie-ondertekeningsleutel en ooreenstemmende openbare-sleutel pin (RECEIPT_SK is opsioneel en LogProfile.receipt_pubkey tans leeg); en (c) die self-gepubliseerde-kop GitHub-bewaarplek + token (§16.6). Totdat die anker-/profielpenne voorsien is, rapporteer 'n selfstandige verifieerder die bewysstatus eerlik eerder as om 'n volledig geankerde, vasgemaakte verifikasie te beweer. Die ontwerp is streng additief en daar is geen verandering aan die SealedQub / QubEnvelope draadformaat nie.

16.1 Rasionaliteit en Duursaamheidsvlakke

Die huidige publikasiepaaie ontkoppel bevestiging van Arweave-bevestiging: hulle lei 'n individuele transaksie af en teken dit, behou die artefak en presiese indieningstoestand in R2, en plaas dan asinchronies. Die deursigtigheidslogboek voeg 'n onafhanklik geankerde ordeningslaag by vir die substel van algemene /upload versoeke waarvan die LogDO byvoeging slaag:

Tier Naam Waarborg Wanneer
T1 R2-eerste sinchroniese bevestiging Duursaamheidsvloer — verseëlde grepe en presiese publikasietoestand word na duursame berging geskryf voordat sukses terugbesorg word. Geïmplementeer oor huidige publikasie-paaie.
T2 Gebundelde deursigtigheid-log insluiting Alleen-aanvoeg, aantoonbaar gewysigde verbintenis + totale ordening sodra ingesluit en veranker. Huidige produsent: suksesvolle LogDO byvoegings van /upload; reaksie dra die kwitansietuple. Nie universeel nie.
T3 Per-qub Arweave permanentheid 'n Afsonderlike Arweave transaksie vir die qub. Tans voorberei vir elke aanvaarde publikasie en gepos asynchroon; die presiese onderteken transaksie bly in die dreinbare uitboks totdat dit afgelewer is.

Die lae beskryf verskillende bewyse- en duursaamheidseienskappe, nie die huidige kommersiële plan nie. Die huidige kode skeduleer steeds 'n individuele Arweave transaksie vir elke aanvaarde publikasie; dit stel nie T3 bloot slegs as 'n betaalde opswaai nie. API-sleutel/rekeneenheid kwotum plafonne bly aparte toepassingsbeheer.

Duursaamheid eerlikheid. Die T1-skryf is sinchronies, so 'n suksesvolle reaksie vestig toepassingsvlakduursaamheid sonder om vir 'n Arweave-poort te wag. Dit vestig nie self 'n onafhanklike tydstempel nie. 'n Bevestigde individuele transaksie verskaf sy blok-tyduid. Vir 'n reaksie wat die volledige T2-kwitansietuple dra, kan die volgende bevestigde veranker die logbewys soos hieronder beskryf voorsien. As die tuple afwesig is, mag geen oppervlak aandui dat hierdie qub reeds in die deursigtigheid-log is nie. Veranker- en publikasie-latensie het geen protokolvlak numeriese SLA nie.

16.2 LogLeaf-struktuur (twee toegewyde vorms)

‘n Loginskrywing is ‘n LogLeaf, gekodeer as handgeskrewe kanonieke CBOR onder die §3.1-profiel (definiete-lengte, geen etikette, geen desimale getalle, kortste-vorm heelalle, NFC-tekst, opsionele velde weggelaat wanneer afwesig, sleutels georden volgens gekodeerde-bytelengte stygend en daarna byt-vir-byt). Die §3.1 parse → re-encode → compare kanonieke beskerming word toegepas op die kodeerpad voor hasing (nie net op dekodeer nie), sodat twee implementasies nie oor die bladsy-bytes kan verskil weens ‘n heelgetal-breedte of sleutel-orde verskil nie. Alle heelalle is u8 / u64 / i64; alle digest’e is 32-byt stingels (bstr[32]). ‘n Gestoor Arweave-transaksie-ID is ‘n rou 32-byt SHA-256 digest wat as bstr[32] gedra word, nooit as ‘n base64url teksstring nie (ooreenstem met §3.3).

Die blaarsnit het twee vorms wat gekies word deur ‘n kind-byt, omdat die algemene oplaai-pad byte-onverskillig is: POST /api/v1/upload behandel doelbewus beide aanvaarde vragvorms as deursigtig en ontvang qub_id en unlock_at slegs as onbetroubare kliëntverklarings. Op die standaard privaat pad, body_hash, drand_round, created_at, en drand_chain_version is verder weggesteek binne die §13 buite-lus, waarvan die sleutel die Werker nooit hou nie. Die typesisteem definieer ook ‘n bevestigde vorm vir ‘n vervaardiger wat self body_hash / drand_round aflei. Die huidige /seal-roete het daardie waardes maar roep nie LogDO aan nie, sodat produksie tans slegs geasserdeerde (0x02) blaarsnitte van suksesvolle algemene-oplaai-aanhegtings uitbring. Die splitsing hou elke toegewyde waarde eerlik sonder om voor te gee dat die bevestigde vervaardiger gekoppel is:

Sleutel Enc. len Tipe Teenwoordigheid Betekenis
seq 4 u64 vereis Globale 0-gebaseerde blaarindeks; die posisie waartoe die insluitingsbewys verbind is.
kind 5 u8 vereis 0x01 bevestigde-bekwame (gedefinieer, tans nie uitgestraal nie) of 0x02 geaktiveer (kliëntseël / greep-blinde oplaai).
ref 4 bstr[32] vereis Bladverwysings-ID. Bevestig → rou qub_id. Bevestig → die blinde id SHA3-256(qub_id ‖ log_blind_secret) (§16.2.1).
chash 6 bstr[32] vereis Inhoudsadres SHA3-256(stored_bytes) — die een inhoudsband wat die Werker altyd eerlik kan bereken, op albei paaie.
unlock_at 10 i64 vereis Gekopieer (bevestig) of bevestig (bevestig); gevalideer > 0 voordat dit die blaar binnegaan.
received_at 12 i64 vereis Werker-muurklok by R2-akk. Nie-bewyslik (operator-bevestig; §16.6). Teenwoordig vir selfbeskrywing, nooit 'n bewys nie. Gevalideerde > 0.
body_hash 10 bstr[32] kind=0x01 slegs op 0x02 weggelaat — die Werker het dit nie ingevolge §13 nie.
drand_round 12 u64 kind=0x01 slegs op 0x02 weggelaat.

'n kind=0x02 blaar doen doelbewus nie body_hash of drand_round nie: dit bevestig die verbintenis en ordening van 'n ondeursigtige kodeteks by inhoudadres-chash, en beweer qub_id en unlock_at — nie sy platteks of ronde nie. Die platteks/ronde bene vir 'n beweerde qub kom van die bestaande §11 .qub-bundelverifikasie, nie van die log nie (§16.11). drand_chain_version is nie in die blaar nie (dit is binne die omhulsel op die standaardpad); kettinggranulariteit leef op die anker (§16.7). Enkodeer-dissipline: verwerp 'n alles-nul ref of chash, en verwerp nie-positiewe unlock_at / received_at, wat die outcome_at > 0 sentinelwag in cbor.rs weerspieël.

16.2.1 Privaat-qub blindering

Die log mag nie die enumerasie-orakel word wat die §13 buitenste omhulsel voorkom nie (§13.1). Vir 'n privaat (toegedraaide) kwb verbind die asserted blaar die blinde identifiseerder SHA3-256(qub_id ‖ log_blind_secret), waar log_blind_secret 'n bediener-gehoude geheim is, en laat body_hash uit. 'n Derde party kan nie so 'n blaar aan 'n spesifieke qub_id koppel nie; die qub se houer, wat die aflewerings-URL het en dus qub_id, kan die blind herbereken om hul eie insluiting te bevestig. 'n Publieke qub (reeds opnoembaar, reeds met die Visibility: public Arweave-etiket volgens §13.8) verbind die rou qub_id. Dit is die een plek waar selfstandige verifieerbaarheid doelbewus toegee aan 'n draende privaatheidsinvariant; die afsonderlike band vir private qubs is chash (§16.9).

log_blind_secret bewaring (opgelos — §16.15 Q4). Die blinde beskerm blaar-ontkoppelbaarheid, nie platteksvertroulikheid nie (die §13-omhulsel hou dit onafhanklik). Op 'n log_blind_secret kompromie, vir enige qub_id wat die teenstander reeds hou of kan herbou (elke qub waarvan dit bondel/URL het, plus enige lae-entropie of openbare qub_id) bereken dit die blaar ref in een hash en koppel dit — dit is direkte koppeling van 'n bekende populasie, nie 'n brute-force oor 'n onbekende ruimte nie. Klassifiseer log_blind_secret as 'n korrelasie-/Sybil-graad geheim in dieselfde bewaringsvlak as ander bedienergeheime, en roteer slegs vorentoe ('n rotasie maak toekomstige blare weer blind; dit kan nie retroaktief reeds geankerde blare ontkoppel nie).

16.3 Blaar- en Node-hashing

RFC 6962 §2.1 domeingeskeide hashing met SHA-256 vervang deur SHA3-256:

leaf_hash      = SHA3-256(0x00 || canonical_cbor(LogLeaf))
node_hash(l,r) = SHA3-256(0x01 || l || r)
empty tree     = SHA3-256("")          // defined but never anchored

Domein-voorvoegselsgrepe 0x02 (inskrywingsketting, §16.4) en 0x03 (STH-hash, §16.6) is gereserveer en van hierdie geskei. Hulle is enkelgrepe en kan dus nie bots met die bestaande 10-greep ASCII-domeinskeiers (QUB_ID_V2, ens.) nie. Die boom is die RFC 6962 links-vol ongebalanseerde boom (elke binnekant verdeel by die grootste mag van twee streng minder as die subboom-blaartelling), wat insluitings- en konsekwentheidsbewyse toelaat om een ouditpad-algoritme te deel. Die verwysingspesifikasie dra eksplisiete links/regs-afleiding pseudokode en pen 'n nie-mag-van-twee (5-blaar) toetsvektor vas, sodat die regterkant-promosiegeval — wat 'n 4-blaarvektor verberg — benut word.

16.4 Hash-ketting (intern)

Die LogDO handhaaf 'n interne inskrywingsketting slegs vir botsingskonsistensie. Dit word nooit gepubliseer nie en nooit verifieer-gerig nie:

entry_chain[seq] = SHA3-256(0x02 || entry_chain[seq-1] || leaf_hash[seq])
entry_chain[-1]  = SHA3-256("QUB_TLOG_GENESIS_V1")

Die gepubliseerde byvoegings-slegs gesag is die kumulatiewe Merkle-wortel + sy anker (§16.5–16.6), nooit die rou volgorde waarin die operateur toevallig blare bedien nie: die ketting herbereken vir enige bevel wat bedien word, so slegs die geankerde wortelpenne is die kanonieke posisie.

16.5 Kumulatiewe Merkle-boom en bondelvorming

Daar is een voortdurend groeiende RFC 6962-boom oor al die blare in seq volgorde — nie geïsoleerde per-bondel-bome nie. ('n Dra-blaar-ketting per-bondel-konstruksie is verwerp: dit is nie 'n ware voorfiksverhouding nie, dus is sy "konsekwentheidsbewyse" onbetroubaar.) Die kumulatiewe boom gee egte RFC 9162-konsekwentheidsbewyse en laat 'n enkele onlangse anker toe om insluiting vir enige ouer kwb te bewys.

Die LogDO Duursame Objek is die enkele skrywer (blockConcurrencyWhile, spieëlend QuotaDO / EntitlementDO) — om by 'n gedeelde log te voeg is lees-wysig-skryf op gedeelde toestand en MOET dus deur 'n DO, nooit KV gaan nie. Dit kas die boom se regterkantgrens (O(log n) hashes), so die sluiting van 'n bondel is O(batch). 'n bondel is die stel blare wat aanmekaar geanker is; sy geïmplementeerde snellers is 'n tree_size vooruitgang van minstens LOG_BATCH_MAX_LEAVES (verstek 4096), ouderdom wat die ankerkadens bereik, of 'n eksplisiete administratiewe/cron-kragsluiting. root_i is die kumulatiewe Merkle Boom Hash oor blare 0 .. tree_size_i.

16.6 Gemerkte Boomkop via Arweave Anker

Die Arweave-ankertransaksie is die Gesigneerde Boomkop en vervang 'n operateurhandtekening vir die boomkop self: die daaglikse anker benodig geen qub-sleutel nie omdat die Arweave-tx-owner die handtekening is. Die slootstelling hou stand — die onveranderlike substraat, nie 'n qub-gehoude geheim nie, dra die las vir die geankerde wortel.

Die logontwerp vra vir een warm suksesvolle-byvoeg ontvangssleutel (§16.10), vasgespeld in LogProfile en kruisonderteken deur anchor_owner. Die huidige implementering het daardie vertrouenswortel-voorsiening nog nie voltooi nie: RECEIPT_SK opsioneel is, lewer 'n afwesige/ongeldige sleutel sig_b64url: "", en die saamgestelde LogProfile.receipt_pubkey is leeg. So 'n kwitansie kan die bygevoegde blaar beskryf, maar is nie 'n nie-weerlegbare ondertekende kwitansie nie. Die sterker ontwerp-eis geld slegs nadat 'n verifieerder-vrystelling die ooreenstemmende publieke sleutel vaspen en die anker-eienaar dit kruisonderteken. 'n Publikasie-reaksie sonder die volledige kwitansie-tuple maak geen log-aanvaardings-eis nie; een met 'n leë handtekening maak 'n byvoeg-posisie-eis maar geen handtekening-verifikasie-eis nie.

Die SignedTreeHead is kanonieke CBOR (sleutels volgens gekodeerde lengte): size:u64, root:bstr[32], batch:u64, prev:bstr[32] (vorige sth_hash; genesis = 32 nul grepe), log_id:bstr[32], first_seq:u64, anchored_at:i64. Sy hash is sth_hash = SHA3-256(0x03 || canonical_cbor(SignedTreeHead)).

Vasgepinde vertrouenswortel. log_id = SHA3-256("QUB_TLOG_V1" || anchor_owner_address). 'n Konformer verifiseerder MOET anchor_tx.owner == LogProfile.anchor_owner vereis, waar anchor_owner (en die ontvangs-sleutel openbare sleutel) in qub_core ingesluit is as die LogProfile — langs die quicknet-konstantes wat reeds in DrandTimelockProvider::quicknet() is — en saam met die verifiseerder-binneprogram versprei word. Die verifiseerder MOET ook die Arweave-tx data → tx_id binding plaaslik verifieer eerder as om 'n gateway /raw/ reaksie te vertrou. Dit sluit die rower-beursie-tweerigting-gat: "geanker op Arweave" is betekenisloos totdat die verifiseerder vasstel watter beursie.

Rotasie is 'n §15 bestuur uitbreiding, nie 'n hergebruik nie (opgelos — §16.15 V3). §15.2 se profieloppervlak som tans slegs sig_algs / drand-kettings / inwikkelersweergawes / inhoudstipes op, en §15.3 se afluisterlys sluit geen hiervan in nie — LogProfile / anchor_owner is nog nie in §15 se oppervlak nie. Rotasiebestuur moet dus gebou word: §15.3 word (hieronder) uitgebrei om die LogProfile-afluisteraar by te voeg, en 'n rotasie is 'n ondertekende LogProfile-verhoging wat in 'n verifiseerder-opdatering gestuur word. 'n Beplande rotasie dra 'n uitgaande → inkomende kruis-ondertekening; 'n kompromie-gedrewe rotasie kan nie (die uitgaande sleutel is presies daardie tyd onbetroubaar/onbeskikbaar nie) en val terug op die §15-beheerde verhoging, met die vorige-anker-greppuntkontrole (hieronder) wat skade in die tussentyd beperk.

Tweerigting-venster (eerste-klas vertrouensparameter). 'n Blad is slegs weerligbestand sodra sy dekkende anker Arweave-bevestig is. Die venster is received_at → anchor confirmation (kadens + Arweave-finaliteit, sonder protokol-latensiep Garan tie). Voor vertrouenswortelverskaffing, voorsien die huidige implementering qub se operasionele integriteit plus enige onondertekende toevoegmetadata wat teenwoordig is; dit verskaf nie die beplande nie-weerlegging waarborg nie. Drie aanspreeklikheid-artefakte definieer die voltooide ontwerp (die getuie-model is §16.15 V2 se oplossing):

  1. Seëlontvangs (voorsieningsafhanklik) — die SCT-analoog word teruggegee wanneer 'n oplaaier se logboekbyvoeging slaag (§16.10). Dit word nie weerlêbaar nie eers wanneer sig_b64url nie-leeg is nie en die ooreenstemmende publieke sleutel/anker-eienaar verhouding in die verifieerder vasgepen is. Die tans leë produksieprofielpen kan nie daardie uitspraak ondersteun nie. Hierdie beheer geld nie vir 'n weggelate kwitansie-tuple of 'n ongetekende kwitansie nie.
  2. Gepubliseerde moniteringsmetodologie + vorige kettingstap — die anker prev ketting word kop→genesis geloop; 'n vurk (twee ankers by een size met verskillende root, of 'n gebreekte prev) is publiseerbare bewys van wangedrag. Dubbelsinnigheidsopsporing is 'n verklaarde operasionele verbintenis, nie 'n stille aanname nie.
  3. Dubbele self-gepubliseerde koppe — elke nuwe kop {sth_hash, tree_size} word geplaas op 'n toegewyde qub-besit openbare, slegs byvoegings-GitHub-bewaarplek (die draende, manipuleerbare selfpublikasie-been), met 'n sosiale plasing slegs as beste poging bevestiging. 'n Mislukte plasing MOET-bladsy (nie misluk stil nie). Geïmplementeer (Fase 8) as die publishHead haak op die anker-cron (workers/api/src/utils/heads-publish.ts): 'n PUT tot die inhouds-API sonder 'n sha is slegs byvoeg ('n 422 beteken die kop is reeds gepubliseer, nooit 'n oorskryf nie); opt-in / ontplooi-gated op PUBLISH_HEAD_GITHUB_{TOKEN,OWNER,REPO} en inert totdat die bewaarplek voorsien is. 'n Moeilike GitHub-fout blaai via die health_alert-kanaal en stoot 'n duursame mislukkingsmetriek (m:tlog_publish_head_fail); die Arweave-anker self rol nooit terug op 'n publiseerfout nie. "Nie fail silent" word gewaarborg deur daardie duursame maatstaf — wat operasies MOET dashboard-waarskuwing op — selfs al kan die beste-poging e-posbladsy nie afgelewer word nie. Twee eerlike beperkings volg uit "anchor-on-advance" (die cron publiseer slegs wanneer die grootte toeneem): 'n tydelike GitHub-fout laat 'n gaping in die gepubliseerde-kop-volgorde vir daardie grootte — begrens, nie stil nie (it-bladsye), en omdat elke kop 'n superset-boom verbind, oorbrug 'n §16.9-konsekwentheidsbewys die gaping; krities is dat daardie konsekwentheidsbewys bereken word uit die gesaghebbende Arweave-ankerboom, nie van die GitHub-oppervlak nie, sodat 'n GitHub-gaping nooit verifieerbaarheid verswak nie. 'n Inhaal-opvulling wat gapings in gepubliseerde koppe vul, is 'n uitgestelde verbetering.

Eerlikheidsgebonde (bindende beperking). Omdat qub albei beplande plasingsoppervlaktes beheer, is dit self-gepubliseer, nie onafhanklik getuienis nie. Geen produk, bemarking of wettige oppervlak mag beweer die log is "onafhanklik gewit" nie. Nadat die kwitansie/profiel/hoofhekke voorsien is, is die toegelate eis dat dubbelsinnigheid opspoorbaar is en 'n suksesvol ondertekende byvoeging 'n nie-weerlegbare kwitansie laat. Voor dan is daardie eis nie beskikbaar nie. 'n Ware onafhanklike derdeparty-getuie word uitgestel na 'n toekomstige §15-bestuursverhoging.

received_at word deur die operateur bevestig en geen eis mag daarop steun nie — dit word nooit as bewys of as geskilbevestiging op enige produk / wettige / API / bewys-renderingsoppervlak vertoon nie. Die Arweave-ankerblok tyd-T is die enigste betroubare tydstempel ('n boonste grens op "aangeteken deur"). Enige monitering-sanity-kontrole op received_at MOET vergelyk word met T, nie met die operateur-beheerde anchored_at sth-veld nie; so 'n kontrole is slegs 'n beskerming teen 'n eerlike operateur se klokfout, nie 'n aanspreeklikheidsbeheer teen 'n kwaadwillige operateur nie (§16.15 Q5).

16.7 Anker Transaksieformaat en Ritme

Die AnchorBundle is die kanonieke-CBOR Arweave-transaksieliggaam, geskryf via die §16.8-bundeler: ver:u8, sth:bstr (kanonieke SignedTreeHead grepe), prev_anchor:bstr (vorige anker-tx id rou grepe; weggelaat by genesis), chain_hash:tstr (die DRAND-ketting in werking — quicknet), en die batch se blaar-CBOR-stroom in seq volgorde sodat die anker selfstandig is: 'n monitor herlei root van die liggaam af sonder nul qub-afhanklikheid. (As die blaarstroom groot word by hoë volume, mag 'n toekomstige hersiening slegs 'n blaarreeks deur verwysing toewys; opgemerk, nie in v1 aangeneem nie.)

Arweave-etikette is doelbewus opsombaar — die logboek is bedoel om gevind te word, anders as private qubs: App-Name: qub-tlog, Anchor-Format: 1, Log-Id: <hex>, Batch: <n>, Tree-Size: <n>, Root: <hex>, Prev-Anchor: <tx>, Content-Type: application/cbor. Etikette is onbetroubare leidrade; die CBOR-liggaam is die enigste gesag.

Cadence: daily by default, revisited with volume (the size trigger auto-shortens the effective cadence under load). The current producer does not implement a paid-seal force-anchor hook. The anchor wallet is dedicated and low-velocity, separate from the upload wallet — it MUST be its own JWK (a distinct key, not a logical role on the upload wallet) so an upload-wallet compromise cannot forge anchors — with a hard per-day anchor-transaction budget. The custody posture is stated plainly: a narrow-scope hot key with a tight circuit breaker and low balance, not "cold" — a wallet that auto-signs daily cannot be cold, and the spec does not pretend otherwise.

16.8 ANS-104 Bundler

An in-house ANS-104 DataItem encoder and deep-hash signer, roughly 300 lines, Web Crypto only, zero npm dependencies (both Turbo SDKs fail the npm ci --ignore-scripts supply-chain gate). DataItem byte layout:

signatureType (2, LE) || raw_signature || owner || target(flag+0|32) || anchor(flag+0|32) || num_tags(8, LE) || tags_len(8, LE) || avro_tags || data

Signing is Arweave deepHash — a recursive SHA-384 digest (Arweave's wire requirement, crypto.subtle.digest("SHA-384")) over ["dataitem", "1", sig_type, owner, target, anchor, encoded_tags, data] — then RSA-PSS over the deep hash with the wallet JWK via crypto.subtle; id = base64url(SHA-256(signature)). The SHA-384 here is quarantined as an Arweave-wire-only primitive, never a qub trust primitive (§15 records the fence; qub trust hashing is SHA3-256 throughout).

The ANS-104 code path serves the deferred fallback/drain machinery and writes AnchorBundle DataItems. The ordinary publication path first creates an exact signed Arweave transaction and keeps its JSON in a durable outbox; direct posting is a latency optimisation, and the drain path retries the same transaction before applying its bundler fallback. Signature scheme (resolved — §16.15 Q8): v1 signs with RSA-PSS (signature type 1) reusing the existing Arweave wallet JWK mechanism (zero new long-lived key custody, serving the "one fewer secret" thesis); Ed25519 is deferred to the §15 PQ-migration path.

The hand-rolled deep hash is the highest-risk, lowest-natural-coverage code in W5, so its gating is non-negotiable (§16.15 Q8):

  1. Die kruis-taal toestel tlog_v1.json (Rust + TS, die §14.5 wrapper_v1.json patroon) dek diep-hash, DataItem grepe + id, blaar-hashe, 'n 5-blaar wortel + ouditpad, 'n STH hash, 'n insluitingbewys, en 'n konsekwentheidsbewys — in beide die teken- en verifieer-rigtings (die verifieer-rigting is belangrik omdat §16.6 se plaaslike tx → tx_id kontrole die diep-hash in elke selfstandige verifiseerder trek, nie net die skrywer nie).
  2. 'n Eenmalige interop heen-en-weer deur 'n verwysings-ANS-104 bundelaar, gebruik as statiese toetsdata slegs — nooit 'n npm-runtime afhanklikheid nie (die Web-Crypto-alleen / geen-install-skripte houding bly van krag).
  3. Die diep-hash + RSA-PSS pad moet heen-en-weer gaan deur dieselfde crypto.subtle primitiewe produksie gebruik, sodat die interne enkodeerder byte-kompatibel is.
  4. 'n Voortdurende post-bundel aanvaarding monitor bevestig dat elke anker / terugval DataItem inderdaad Arweave-aanvaarding bereik, met 'n alarm + stroombaan-breker — omdat die diep-hash ook die Arweave-onbeskikbaarheid-terugval-ry dien, sodat 'n stil regresie daardie ry met netwerk-verwërpte items sou vul tydens dieselfde uitval waarvan dit bedoel is om te dek.

16.9 Insluiting- en Konsekwentheidsbewyse

Albei is RFC 9162, SHA3-256, bedien as kanonieke CBOR.

InclusionProof — GET /api/v1/qub/:tx_id/proof: ver:u8, leaf:bstr (die presiese blaar-CBOR — die verifiseerder bereken leaf_hash self en vertrou nooit 'n voorsien hash nie), index:u64, size:u64, audit:[bstr[32]], root:bstr[32], anchor:{ txid:bstr[32], batch:u64, sth:bstr[32], log_id:bstr[32], block_height?:u64, anchored_at?:i64 }.

ConsistencyProof — GET /api/v1/log/consistency?first=<size_a>&second=<size_b>: ver:u8, first_size:u64, second_size:u64, first_root:bstr[32], second_root:bstr[32], nodes:[bstr[32]], first_anchor, second_anchor. 'n Enkele ondubbelsinnige sleutellys, vasgemaak deur toetsvektor.

Selfstandige verifikasie (geen qub-bediener nie, brei op §11 uit):

1.  Parse .qub bundle → SealedQub; recompute qub_id (§4.1).
2.  Read leaf.kind.
3a. kind=0x01 (attested):
      assert leaf.ref == qub_id
      assert leaf.body_hash   == SHA3-256(body)
      assert leaf.drand_round == unlock_round(unlock_at)
3b. kind=0x02 (asserted):
      assert leaf.ref == SHA3-256(qub_id || blind)   // holder supplies blind
      OR treat ref as opaque and bind via leaf.chash == SHA3-256(stored_bytes)
4.  Recompute leaf_hash = SHA3-256(0x00 || leaf); fold `audit` per RFC 6962
    using index/size; require derived root == proof.root.
5.  Fetch anchor.txid from any gateway; verify the tx data → tx_id binding
    (do not trust a gateway /raw/ response); REQUIRE anchor_tx.owner ==
    LogProfile.anchor_owner.
6.  Parse AnchorBundle; require committed root == proof.root and size ==
    proof.size; read the Arweave block time T.
7.  Emit the claim scoped by leaf.kind (§16.11).

Bewys-bediener berging MOET koördinaat-sleutel hê (opgelos — §16.15 Q7, blokkeringsvoorwaarde). Koue-blaar bewysgenerering is korrektheidsneutraal slegs as die R2-ouditmateriaal 'n volhoubare Merkle-node stoor is wat deur absolute boomkoördinaat (level, index) gesleutel is — nie per batch-node deltas nie. Met 'n koördinaat-sleutel stoor is enige (leaf i, size N) ouditpad 'n stel van O(log N) direkte R2 GET's met geen herberekening oor bondelgrense nie; met 'n bondel-sleutel stoor is dit nie, wat die stoor-uitleg gaping is wat hierdie resolusie sluit. Die blaarliggame is eweneens inhoudsadresseerbaar deur seq. 'n W5-toetsvektor MOET 'n genesis-era koue blaar teen 'n baie later wortel bewys deur slegs R2 + Arweave met die LogDO-berging uitgevee te gebruik, so die herwinningsveiligheids-eis in §16.13 word ondersteun eerder as bevestig. Die O(log N) opeenvolgende R2 GETs behoort slegs op die asinkrone bewys-eindpunt — nooit op die seël-warm pad (§16.10) of 'n per-tick cron nie.

16.10 R2-Eerste Ack-Bestelling

Die geïmplementeerde POST /api/v1/upload-volgorde is:

  1. Voorste helfte hekke (verkenning, validering, idempotensie-shard-sleutel) — onveranderd.
  2. Skep, merk en teken die presiese individuele Arweave-transaksie. Dit lei tx_id plaaslik af, alhoewel transaksieskepping dalk belonings-/ankermetadata van 'n poort kan haal. 'n Voorbereidingsmislukking laat steeds die versoek misluk voor bevestiging.
  3. Sinchronies skryf die gekose artefak by qub-cache/<tx_id> en handhaaf die stabiele skeppingsoperasie/uitboksrekords. Hierdie is die duursaamheids- en herprobeervloer; mislukkings voor nedersetting gee 503 terug.
  4. Wanneer LOG_DO gekonfigureer is, probeer sinchronies LogDO.append(leaf). Die enkele skrywer ken seq toe, brei die inskrywingsketting uit, en werk die grens by. Die byvoeg-RPC doen net dit; bondelsluiting loop van die pad af op die alarm. 'n Byvoegingsvervoer-/toepassingsfout is tans fail-sag: die reaksie kan steeds slaag sonder log_seq, receipt of anchor_status. Ten spyte van 'n implementeringskommentaar word geen outomatiese latere logrekonsiliasie vandag bedraad nie.
  5. Gee die bevestiging terug. Sluit { log_seq, anchor_status: "pending", receipt } slegs in wanneer die byvoeging die volledige suksesvolle tuple teruggegee het. receipt.sig_b64url is leeg wanneer die ontvangs-ondertekenaar nie beskikbaar is nie; kliënte MAG NIE daardie waarde onderteken of nie-weerlêbaar noem nie. Die afwesigheid van die tuple beteken slegs duursame publikasie, nie deursigtigheidslog-aanvaarding nie.
  6. Gebruik een uitgestelde taak om die presiese ondertekende transaksie te plaas. Sukses verwyder die uitboks; mislukking laat dit oor vir die begrensde dreineringskrom en mag nie die reeds erkende tx_id verander nie. Voorlopige metadata en ander beste-poging sykar word ook uitgestel.

Latensiegrens. Die versoekpad sluit voorste helfte gesag-/kwotawerk, transaksievoorbereiding/-ondertekening, duursame R2-skryfwerk, en (wanneer gekonfigureer) die LogDO poging in. < 300 ms verskyn in die ontwerphersiening as 'n operasionele teiken, nie 'n protokolwaarborg nie; die huidige transaksievoorbereidingsstap kan 'n gateway-metadataversoek uitvoer. Latensie-alarms en lanseringshekke is operasionele beheer, nie bewyse beskikbaar vir 'n verifieerder nie.

16.11 Trustmodel — die presiese bewering, beperk volgens blaarsoort

Vir kind=0x01 (bevestig): *"Hierdie inhoud — liggaamspassing body_hash, geïdentifiseer deur qub_id — is in qub se byvoeg-alleen log by posisie seq vasgelê en het nie later as Arweave-bloktyd T bestaan nie; dit was kriptografies onleesbaar tot DRAND-ronde R = unlock_round(unlock_at)." * Dit is die volle {tlock round binding + Merkle inclusion + anchored root} drievoudige.

Vir kind=0x02 (geaktiveer, die verstek): *"'n Ondeursigtige syferteks met inhoudsadres-chash, wat qub_id en unlock_at beweer, is by die byvoeg-alleen log by posisie seq toegewy en het nie later as Arweave-bloktyd T bestaan nie." * Die ronde en liggaamsbene word voorsien deur die bestaande §11 .qub-bundelverifikasie (qub_core::unlock), nie deur die log; wat die log byvoeg oor 'n kaal per-qub-transaksie is manipulasie-duidelike volgorde, 'n vertrouelose bo-grens verbintenistyd, en dubbelsinnigheidsweerstand.

Beide eise sluit uit, volgens §11: outeurskap sonder sig_alg ≥ 0x01, bedoeling, en subanker-granulariteitstyd. Geen van hulle laat enige eis op received_at steun nie.

Eisplafon (bindende lanseringsbeperking — opgelos §16.15 Q1). Vir 'n beweerde (kind=0x02) blaar is die bogenoemde omvattende eis die plafon op wat enige produk, bemarking, terme of bewysrenderingsoppervlak mag beweer. Geen oppervlak mag aandui of impliseer dat die logboek die inhoud of die ontsluitingsronde van 'n byte-blinde oplaai bewys nie — die log bewys ordening + 'n betroubare boongrens-verbintenistyd van 'n ondeursigtige syferteks. Inhoud- en rondtebewys kom uitsluitlik van die bestaande §11 .qub-bundelverifikasie, wat log-onafhanklik is. 'n Publikasie sonder 'n suksesvolle byvoeging/ontvangs het glad nie 'n log-eis nie.

16.12 Weergawebeheer en W3-koördinering

Daar is geen SealedQub draad-bump en dus geen protokol-weergawe-bump (§12.2): die log is 'n sidecar wat aan bestaande velde en grepe verbind, dus kom dit nie in die §12.3 protokol-weergawe-geskiedenis in nie. W3 se opsionele drand_chain_version bly onaangeraak en bly die enigste opsionele SealedQub-veld. Die log stel in plaas daarvan sy eie onafhanklike weergaweruimtes in — LOG_VERSION_1, ANCHOR_FORMAT_1, InclusionProof.ver — wat die §12.5 wrapper-weergawe-onafhanklikheid weerspieël (die wrapper dra 'n weergawebyte onafhanklik van die protokolweergawe, en die log-weergawes volg dieselfde skeiding).

Bewyslewering word standaard gehaal, met 'n opsionele saamry. 'n Bewys kan nie tydens seëltyd bestaan nie (die anker is nog nie geskryf nie), so die seëltyd-.qub bundel bly bewysvry. W7 se verifieerder haal GET …/proof een keer, of rekonstrueer die bewys in heeltemal vanlyn modus vanaf die openbare AnchorBundle via 'n Arweave-navraag op Log-Id. Die .qub-bundel (W7) reserveer 'n opsionele inclusion_proof-lid — afwesig by seël, gevul deur 'n na-anker heruitvoer vir koue argivering — volgens dieselfde "opsioneel, weggelaat, byvoegend"-patroon as W3 se drand_chain_version.

16.13 Bewaring

Retensievensters vir die LogDO-oop stert, die R2-bewys-dienende substraat, die anker-stroombaanbreker-tellers, en die bundel-terugvalry word in docs/DATA-RETENTION.md gespesifiseer. Beginsel: die log se warm per-invoer stoorplek (LogDO) is herwinbaar na die anker; sy ouditmateriaal — die koördinaat-sleutel (level, index) Merkle-node stoor + die seq-geadresseerde blaarliggame (§16.9) + die Arweave-ankers — is permanent. Om 'n koue blaar van die DO terug te neem, maak nooit 'n uitgereikte bewys ongeldig nie, want 'n bewys los op teen daardie permanente R2-knoopstoor en die Arweave-anker, nie die DO nie (en die §16.9 uitgevee-DO-toetsvektor bewys dit).

16.14 Toetsvektore

W5 lewer die kruis-taal fixture tlog_v1.json (§16.8) plus gewerkende vektore: 'n kind=0x01 en 'n kind=0x02 blaar → leaf_hash; die 5-blaar kumulatiewe wortel; een insluitingsbewys; een konsekwentheidsbewys; een AnchorBundle; en een DataItem-id. Hierdie leef langs die §14.5 buite-omhulselvektore en word deur beide die Rust (qub-core) en TypeScript (Worker) implementasies geoefen.

16.15 Hersieningsbesluite (W5 — opgelos)

Die W5 eksterne hersiening ('n teenstrydige ontwerppas + eienaarsgoedkeuring) is voltooi. Elke besluit hieronder word vasgestel en weerspieël in die §16-teks hierbo; die bindende lanseringsbeperkings word aan die einde herformuleer. Implementering kan onder hulle voortgaan.

  1. Standaardpad (kind=0x02) blaareerlikheid — OPGELOS. Stuur die twee-blaar-soort splitsing soos gespesifiseer: kind=0x02 verbind nie body_hash of drand_round nie. Geen *_body_hash veld op die byte-blinde pad nie (dit sou die mees leesbare vals "geverifieerde" sein vir integrators wees en is 'n gerief wat §11 reeds van die bundel voorsien). Moet nie bediener-seël vereis vir log-bevestigde qubs nie (wat platteks deur die Werker sou dwing en die kripto-versnipperingsgracht sou vernietig). Enige selfbeskryfende kortsluiting behoort in die .qub bundel / bewys-omhulsel as 'n verifieerder-herberekende veld, nooit 'n blaarveld nie. Eienaar-bevestigde eisplafon: §16.11.
  2. Dubbelsinnigheid / weglatingsverantwoordelikheid — ONTWERP OPGELOS, VOORSIENING ONVOLLEDIG. Die ontwerp vereis dat die seël-ontvangssleutel in LogProfile vasgepen en deur anchor_owner gekruisteken word, plus moniteringsmetodologie, vorige kettingstap en dubbele selfgepubliseerde koppe. Die saamgestelde profiel en ontplooiingshake is steeds plekhouers/opsioneel soos uiteengesit in §16.6, so die sterker opspoorbaar + ontvangste eis is nie aktueel totdat daardie hekke sluit nie. Dit mag nooit as onafhanklik gewit bemark word nie. 'n Ware derdeparty-getuie word uitgestel tot 'n §15-bestuursverhoging.
  3. Vasgepen anker-eienaar vertrouwortel + rotasie — OPGELOS. Neem die LogProfile-pen aan (§16.6); die verifieerder kontroleer anchor_tx.owner == anchor_owner en verifieer die tx-data → tx_id binding plaaslik. Rotasiebestuur is 'n §15 uitbreiding tot bou (§15.3 sneller bygevoeg), nie 'n hergebruik nie; beplande rotasies kruisteken kruis, kompromie-gedrewe rotasies val terug na die §15-stoot met die vurkkontrole wat skade begrens.
  4. Private-qub blaarblinding — OPGELOS. Hou blinding vir private qubs (ref = SHA3-256(qub_id ‖ log_blind_secret)), rou qub_id vir publieke qubs (reeds §16.2.1), chash as die selfstandige tie. log_blind_secret is 'n korrelasie/Sybil-graad geheim, slegs vorentoe roteer (§16.2.1).
  5. received_at — OPGELOS. Hou dit in die blad, toegewyd maar uitdruklik nie-bewys; nooit as bewys of betwiste bevestiging op enige oppervlak verskyn nie. Enige monitor sanity-kontrole vergelyk met die Arweave-bloktyd T, nie die operateur-beheerde anchored_at nie (§16.6).
  6. Gelaagde bewysbare tydsberekening — ONTWERPRESOLUSIE, NIE HUIDIGE ROETERING NIE. Die hersiene ontwerp ken ankerblok-tydsberekening toe aan die bondelvlak en presiese-uur bewys aan betaalde T3, sonder numeriese SLA vir eersgenoemde. Die huidige roetes het nie daardie kommersiële onderskeid bedraad nie: hulle skeduleer 'n individuele transaksie vir elke aanvaarde publikasie, en logdekking bly voorwaardelik soos in §16.1/§16.10 aangedui. Produkkopie moet die implementering beskryf, nie hierdie toekomstige vlakverdeling nie.
  7. Opgehoopte boom oor Werkers — BESLUIT. Enkele opgehoopte RFC 9162 boom + grens-gebergde enkele-skrywer LogDO (gerieflike kopruimte teenoor die ~1k skrywings/sek DO plafon; stel Merkle-van-skerf-wortels skeiding uit tot daar naby). Die koördinaat-sleutel (level, index) R2 node winkel + uitgeveegde-DO koue-blaar toetsvektor is geïmplementeer (§16.9). < 300 ms bly 'n ontwerp/operasionele teiken, nie 'n protokolbeloofde nie (§16.10).
  8. ANS-104 handtekening skema + diep-hash — BESLUIT. RSA-PSS (sig tipe 1, hergebruik van die toegewyde anker-beurs JWK); Ed25519 uitgestel na die §15 PQ pad. Die hand-gedraaide SHA-384 diep-hash is gebind aan die beide-rigtings kruis-impl fixture, 'n staties-alleen verwysing-bundelaar interop toets, die gedeelde-crypto.subtle rondte-reis, en die post-bundel Arweave-aanvaarding monitor (§16.8).

Bindende bekendstellingsbeperkings (oorgedra in implementering + produk-/regshersiening):


17. Draagbare verifikasiebundel (.qub)

Status. Hierdie afdeling is geïmplementeer (W7 / UP-C2): qub_core::export produseer en ontleed die bundel, en tools/qub-verify is 'n openbare, selfstandige CLI wat een vanlyn verifieer. §11 en §16.9 verwys reeds na “die .qub-bundel” as die eenheid wat 'n alleenstaande verifieerder gebruik; hierdie afdeling spesifiseer die grepe en verifikasiedeurloop. Dit is streng addisioneel—die bundel verpak bestaande §11-insette en verander geen draadformaat in permanente berging nie.

17.1 Doel

§11 stel vas dat enige derde party 'n qub se kriptografiese artefak sonder qub se samewerking kan verifieer. Die .qub-bundel maak daardie verifikasie draagbaar en vanlyn: dit verpak die verseëlde CBOR en die drand-rondtehandtekening wat dit ontsluit in een selfstandige artefak, sodat 'n ontvanger inhoudintegriteit, rondtebinding en enige outeurskaphandtekeninge met geen netwerkoproep hoegenaamd nie kan verifieer (geen bergingsophaling, geen regstreekse drand-versoek, geen qub-API). 'n Bundel alleen bewys nie wanneer sy syferteks geskep is nie; 'n onafhanklik geverifieerde bergingstransaksie of geankerde logbewys verskaf daardie afsonderlike bestaanstyd-aanspraak (§11, §17.5).

17.2 Bundelformaat

'n QubBundle is handgeskrewe kanonieke CBOR onder die §3.1-profiel (bepaalde lengte, geen etikette, geen swewende-kommagetalle, kortste-vorm-heelgetalle, NFC-teks, opsionele velde weggelaat wanneer afwesig, sleutels georden volgens stygende geënkodeerde greeplengte en daarna greepsgewys). Die drie 15-karakter-sleutels orden d < i < s. 'n Rou .qub-lêer is presies hierdie grepe; vir URL- of kopieer-en-plak-vervoer is dieselfde grepe base64url (sonder vulling).

Sleutel Geënk. lengte Tipe Teenwoordigheid Betekenis
version 8 u8 vereis Bundelformaatweergawe (0x01).
sealed_at 10 i64 opsioneel Skepper-beweerde verseëltyd (Unix-sekondes); selfbeskrywend, nie-bewyskragtig.
drand_round 12 u64 vereis Die rondte waaraan die qub gesluit is. 'n Projeksie van die ingebedde verseëlde qub.
arweave_tx_id 14 tstr vereis Die transaksie-id waaronder die verseëlde grepe gestoor is (herkomswyser).
drand_chain_id 15 tstr vereis Die drand-ketting (heks). 'n Projeksie van die ingebedde verseëlde qub.
drand_signature 16 bstr vereis Die drand-bakenhandtekening vir drand_round—die waarde wat die syferteks ontsluit.
inclusion_proof 16 bstr opsioneel Die §16-deursigtigheidslog se Merkle-insluitingsbewys (§17.5).
sealed_qub_cbor 16 bstr vereis Die binneste SealedQubCbor-grepe (ná §13-uitpak), dit wil sê die §11-verifikasie-invoer.

drand_round en drand_chain_id is geriefsprojeksies van sealed_qub_cbor, gedra sodat gereedskap hulle kan lees sonder om die binneste CBOR te ontleed. Hulle word by konstruksie afgelei en by dekodering weer gekontroleer teen die ontlede verseëlde qub; 'n bundel waarvan 'n boonstevlakveld met sy lading verskil, word verwerp. Enkodeerderdissipline weerspieël die res van die draadformaat: verwerp 'n leë drand_signature of arweave_tx_id, en begrens elke veranderlikelengteveld.

17.3 Wat die ingebedde drand-handtekening bewys

Die bundel dra die drand-handtekening eerder as om van die verifieerder te vereis om dit te gaan haal. Tydslot-ontsleuteling (tlock oor die drand-ketting, §8) kan slegs met die egte bakenhandtekening vir die gebinde rondte slaag—'n waarde wat die ketting eers publiseer wanneer daardie rondte verstryk, en wat 'n geldige BLS-handtekening onder die ketting se openbare sleutel is. 'n Vervalste of verkeerde handtekening druip BLS-verifikasie of IBE-/AEAD-ontsleuteling. 'n Bundel wat ontsleutel, bewys dus: die syferteks is aan rondte R gebind, en rondte R het verstryk. Die verifieerder pen die ketting (DrandTimelockProvider::quicknet()) vas en pas die §11-rondtebindingskontrole toe, dus kan 'n bundel nie 'n rondte beweer waaraan sy syferteks nie gebind is nie.

Dit is 'n bewys van 'n vrystellingsvoorwaarde, nie 'n skeppingstydstempel nie. Nadat rondte R verstryk het, kan enigiemand 'n nuwe syferteks vir R skep en sy reeds openbare handtekening verpak. Daarom MOET die bundel alleen NIE beskryf word as bewys dat die syferteks of inhoud voor R, voor unlock_at of voor enige gebeurtenis bestaan het nie.

17.4 Vanlyn verifikasiedeurloop

qub-verify <file.qub> voer die standaard-§11-prosedure volledig uit die bundel uit en dryf qub_core::unlock::unlock met 'n vasgepende DrandTimelockProvider:

1. Parse the .qub bytes → QubBundle (canonical-CBOR guard; bound every field;
   re-check drand_round / drand_chain_id against the embedded sealed qub).
2. BLS-verify bundle.drand_signature for the pinned chain and round, then
   tlock_decrypt(sealed.tlock_ciphertext, bundle.drand_signature) → QubEnvelope.
3. Verify SHA3-256(body) == body_hash               (§11 step 8).
4. Verify QubEnvelope.qub_id   == SealedQub.qub_id   (§11 step 9).
5. Verify QubEnvelope.unlock_at == SealedQub.unlock_at (§11 step 10).
6. Verify ciphertext round == unlock_round(unlock_at) and the chain binding.
7. If sig_alg != 0x00: verify author_signature (and any cosigner; §9.4).
8. Report integrity, round-elapsed/round-binding, authorship, and cosigner
   verdicts separately, plus the recovered body. Do not report a commitment
   timestamp unless step 9 succeeds.
9. Optional existence-time leg: verify an included §16 proof through its pinned
   anchor, or independently verify the referenced storage transaction. Report
   its block time as an upper bound on ciphertext existence.

Die CLI sluit af met 0 (geverifieer), 1 (verifikasie het misluk—steeds gesluit, liggaam-hash-wanpassing, gebreekte rondte-/kettingbinding of 'n handtekening wat nie verifieer nie) of 2 (gebruik / misvormde bundel). 'n --json-verslag dra dieselfde uitsprake vir outomatisering. Omdat die bundel selfstandig is, is die verifieerderkrat (qub-core) en die CLI (qub-verify) die enigste sagteware wat 'n derde party benodig; albei is openbaar en hergebruik die protokol se bestaande verifikasiepad—geen pasgemaakte kriptografie nie.

17.5 Verwantskap met die deursigtigheidslog

inclusion_proof is 'n opsionele gleuf vir die §16-Merkle-insluitingsbewys. Bundel-alleen-verifikasie (§17.4) is volledig vir integriteit, rondtebinding / rondte verstryk en opsionele outeurskap, maar het doelbewus geen onafhanklik getydstempelde bestaansaanspraak nie. 'n Gevulde, volledig anker-geverifieerde inclusion_proof voeg die blaartipe-spesifieke verbintenis en boonstegrenstyd uit §16.11 by sonder om die bundelformaatweergawe te verander. 'n Afwesige bewys beteken slegs “geen bewys ingesluit nie”—nie “ongeldig” nie en nie noodwendig “nie geanker nie” nie.

In die verwysingsimplementasie is die gleuf nou getipeer: qub_core::export::QubBundle::inclusion_proof_typed() lewer 'n Option<InclusionProof> wat die volledige §16.9-struktuur (blaar, ouditpad, geankerde wortel en AnchorRef) deur dieselfde ondeursigtige CBOR-veld dra—geen bundelformaatweergawe-verhoging nie. Die alleenstaande qub-verify-CLI gebruik dit via sy --anchor-been en—totdat die ankerbeursie voorsien is (§16, Status)—rapporteer 'n gevulde bewys met plekhouereienaar as slegs insluiting eerder as volledig anker-geverifieer.