qub Protokollspesifikasjon

qub er en protokoll for kryptografiske temporale forpliktelser: et system for å forsegle ord til en framtidig dato og bevise, når den datoen kommer, nøyaktig hva som ble sagt og når.

Tre primitiver får det til å fungere. drand er en desentralisert tilfeldighetsbeacon — avsløringsdatoen håndheves av fysikk, ikke av noen parts velvilje. Permanent offentlig lagring er et manipulasjonssikkert offentlig lager — ingen part kan redigere eller slette en qub når den først er forseglet. ML-DSA-65 er en post-kvante digital signatur — hver qub er knyttet til et nøkkelpar der den hemmelige delen aldri forlater forfatterens enhet.

Sammen utgjør disse primitivene en uttalelse som er tidslåst, manipulasjonssikker og tilskrivbar — en kvittering hvis verdi vokser i takt med verdens evne til å fabrikere fortiden.

Resten av dette dokumentet er den normative spesifikasjonen som kreves for interoperable implementasjoner.


qub-protokollspesifikasjon

Felt Verdi
Versjon 1.0 (protokollversjon 0x01, ytre omslagsversjon 0x01)
Dato 2026-05-01
Status Utkast
Gjennomgått til 2026-05-01

Dette dokumentet er den normative protokollspesifikasjonen for qub-systemet for tidsbestemte forpliktelser. Det definerer datastrukturer, serialiseringsregler, utledningsformler og verifikasjonsprosedyrer som kreves for interoperable implementasjoner.

Omfang: protokolllaget er bevisst språknøytralt — qub-kroppen er ugjennomsiktig klartekst / markdown / pakt-byte, og språkbevisst gjengivelse er leserens ansvar (qub.social-webapp, <qub-embed>-iframe, MCP-klienter, osv.).


1. Notasjon og konvensjoner

Notasjon Betydning
u8, u64, i64 Usignerte/signerte heltall med spesifisert bitbredde
[u8; N] Bytearray med fast lengde på N byte
Vec<u8> Bytearray med variabel lengde
Option<T> Verdi av type T, eller fraværende
String UTF-8-tekststreng, NFC-normalisert
`
SHA3-256(x) NIST SHA3-256-hash av bytestrengen x (FIPS 202)
ceil(x) Takfunksjon: minste heltall ≥ x
CBOR Concise Binary Object Representation (RFC 8949)
big-endian Mest signifikante byte først

Alle heltall i preimage-konstruksjoner kodes som big-endian bytearray med fast bredde (i64 → 8 byte, u8 → 1 byte) med mindre annet er spesifisert.

Alle tidsstempler er Unix-sekunder i UTC.


2. Datastrukturer

2.1 ComposeQub (skaperens tilstand i minnet)

Ikke serialisert til CBOR. Ikke skrevet til permanent lagring. Lokal for skaperens app.

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,              // 0x01 = public (only value in MVP)
    content_type:   u8,              // 0x01 = text (only value in MVP)
    plaintext:      Vec<u8>,         // UTF-8 qub body
    sender_label:   Option<String>,  // Decorative display name; not authenticated
    status:         DraftStatus,     // Composing | Sealed | Uploaded | Failed
}

2.2 QubEnvelope (dekryptert nyttelast)

Serialisert med kanonisk CBOR (§3). Kryptert inne i SealedQub. Dette er strukturen som beviser innholdets integritet etter dekryptering.

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>,     // V1.1 — when reality renders judgment (verdict-uplift-plan §3.1)
    sender_label:        Option<String>,  // Decorative; not authenticated in MVP
    reply_to:            Option<[u8; 32]>,// Parent qub_id for reply chains; not in qub_id preimage; not signed (see §9.3)
    body:                Vec<u8>,         // Content payload (UTF-8 for text, CBOR for pact)
    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
}

Grunntilfelle (usignert tekst-qub): version = 0x01, content_type = 0x01, sig_alg = 0x00, alle Option-felt fraværende.

Andre v1-konfigurasjoner: content_type = 0x03 (pakt-kropp, se §6.1); sig_alg = 0x01 (ML-DSA-65) med author_signature og author_pubkey til stede (se §9.3); cosigner_pubkey og cosigner_signature til stede sammen for medundertegnede pakter (se §9.7); reply_to satt til den overordnede qub-ens qub_id for svarkjede-qubs (se §9.3 for konsekvensene for signaturomfanget).

2.3 SealedQub (kanonisk wire-format)

Serialisert med kanonisk CBOR (§3). Skrevet til permanent lagring. Dette er on-chain-artefakten.

SealedQub {
    version:           u8,              // Protocol major version (0x01 for v1)
    qub_id:            [u8; 32],        // Same as QubEnvelope.qub_id
    visibility:        u8,              // 0x01 = public; v1 viewers reject other values
    unlock_at:         i64,             // Unix seconds UTC
    outcome_at:        Option<i64>,     // V1.1 — 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
    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 control characters.
}

2.4 RevealedQub (leserens applikasjonstilstand)

Ikke serialisert til CBOR. Lokal for leserens app. Konstruert etter vellykket dekryptering og verifikasjon.

RevealedQub {
    qub_id:              [u8; 32],
    arweave_tx_id:       String,
    visibility:          u8,
    content_type:        u8,
    created_at:          i64,
    unlock_at:           i64,
    outcome_at:          Option<i64>,       // V1.1 — videreført fra QubEnvelope.outcome_at / SealedQub.outcome_at; driver dom-vente-blokken på avsløringssiden (verdict-uplift-plan §5.1)
    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. Kanonisk CBOR-profil

All serialisering av SealedQub og QubEnvelope MUST samsvare med denne profilen. To implementasjoner gitt samme logiske struktur MUST produsere identiske byte.

3.1 Kodingsregler

Regel Spesifikasjon
Standard RFC 8949 §4.2.1 (Core Deterministic Encoding Requirements)
Sortering av nøkler i map Sortert etter kodet bytelengde først (kortere før lengre), deretter leksikografisk (byte-for-byte for kodinger med samme lengde)
Heltallskoding Korteste form: 0–23 i innledende byte; 24–255 i 2 byte; 256–65535 i 3 byte; osv.
Lengdekoding Kun definitive lengder. Ingen ubestemt-lengde arrayer, maps, byte-strenger eller tekststrenger (additional info = 31 er forbudt).
Tags Ingen CBOR-tags (major type 6 er forbudt).
Flyttall Ingen flyttall (major type 7-verdier 0xF9–0xFB er forbudt).
Tekststrenger UTF-8-kodet, NFC-normalisert (Unicode Normalization Form C).
Byte-strenger Rå byte. Ingen base64-koding på CBOR-laget.
Dupliserte nøkler Avvis med feil. Parsere MUST NOT stille godta dupliserte map-nøkler.
Ukjente nøkler Avvis med feil. Parsere MUST NOT tolerere map-nøkler utenfor typens kanoniske nøkkelsett — to distinkte kanoniske byte-strenger må aldri dekode til samme verdi (encode(decode(x)) == x), og for signerte nyttelaster ville en ekstra nøkkel vært skjult innhold som begge signaturer forplikter seg til. Skjemaevolusjon går gjennom version, aldri gjennom ekstra nøkler.
Enkle verdier Kun true (0xF5), false (0xF4) og null (0xF6) er tillatt.
Valgfrie felt Fraværende valgfrie felt utelates helt fra CBOR-mapen (ikke kodet som null). Til stede-felter inkluderes i sortert nøkkelrekkefølge.

3.2 Verifiserte kanoniske nøkkelrekkefølger

Disse nøkkelrekkefølgene er normative. Implementasjoner MUST sende ut nøkler i nøyaktig denne rekkefølgen. Debug-asserter SHOULD verifisere rekkefølgen i ikke-release-bygg.

QubEnvelope (versjon 0x01, usignert, alle valgfrie felt fraværende):

"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 (V1.1 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)

Utledning av QubEnvelope-nøkkelrekkefølge: hver nøkkel er en CBOR-tekststreng. Kodet lengde = 1 byte header + strenglengde (for strenger under 24 byte). Sorter etter total kodet lengde først, deretter leksikografisk for nøkler med samme lengde.

SealedQub (versjon 0x01, offentlig, ingen mottaker):

"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 (V1.1 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)

PactTerms (pakt-kropp, 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 (rad i terms-arrayet):

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

PartyIdentifier (party_a / party_b map):

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

3.3 Byte-kodingsreferanse

Type CBOR-koding Eksempel
SHA3-256-hash (32 byte) 0x58 0x20 + 32 byte body_hash, qub_id
Tidsstempler (i64) Major type 0 (positiv) eller 1 (negativ), korteste koding Unix-sekunder
Versjon (u8, verdi 1) 0x01 (én byte)
Content type (u8, verdi 1) 0x01 (én byte)
sig_alg (u8, verdi 0) 0x00 (én byte)
ML-DSA-65-signatur (3 309 byte) 0x59 0x0C 0xED + 3 309 byte author_signature, cosigner_signature
ML-DSA-65 offentlig nøkkel (1 952 byte) 0x59 0x07 0xA0 + 1 952 byte author_pubkey, cosigner_pubkey

4. Normative utledninger

4.1 qub_id

qub_id identifiserer unikt en qub og binder QubEnvelope til SealedQub. Den utledes deterministisk fra envelope-innholdet.

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

Koding av domeneseparator: Strengen "QUB_ID_V2" er 9 ASCII-byte. Én enkelt 0x00-padding-byte legges til for å nå 10 byte for justering. Implementasjoner MUST bruke nøyaktig disse 10 byte: [0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00].

Koding av outcome_at: V1.1 utvidet preimagen fra 92 til 100 byte for å folde det valgfrie outcome_at-feltet inn i bindingen. Fraværende outcome_at kodes som 8 nullbyte; protokollvalidatorene avviser outcome_at <= 0 overalt, slik at denne sentinelen ikke kan kollidere med en legitim verdi. Se §3.2 (wire-format) og den interne tasks/verdict-uplift-plan.md for verdiktmekanikken som motiverer dette feltet.

Koding av drand_round: V1.2 utvidet preimagen fra 100 til 108 byte for å folde drand_round (mål-drand-runden, §4.3) inn i bindingen, og hevet domeneseparatoren til QUB_ID_V2. Dette binder tidslås-runden inn i qub-identiteten: en gateway kan ikke binde ciphertexten på nytt til en annen (f.eks. allerede passert) runde enn den viste unlock_at antyder. Opplåsingsprosedyren (§8) verifiserer i tillegg at runden som er bakt inn i tlock-ciphertext-stanzaen samsvarer med unlock_round(unlock_at), slik at den viste opplåsingstiden beviselig er runden som styrer dekryptering.

Egenskaper:

4.2 body_hash

body_hash = SHA3-256(body)

Der body er den rå Vec<u8>-innholdsnyttelasten. For tekst-qubs er dette den UTF-8-kodede qub-kroppen.

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

Der title er den valgfrie klartekst-tittelen som vises på leserens nedtelling før avsløring (se §3.2). NFC-normalisering kjøres ved hash-tid slik at digesten er stabil på tvers av visuelt ekvivalente kodepunkt-sekvenser. Sentinel-verdien med kun nuller er reservert for det fraværende tilfellet; en tom streng avvises ved den kanoniske CBOR-grensen som en ikke-kanonisk koding av «fraværende» (den kanoniske kodingen utelater feltet helt).

4.3 Mapping til opplåsingsrunde

drand_round = ceil((unlock_at - chain_genesis_time) / chain_period_seconds)
Parameter Kilde Eksempel
unlock_at Brukervalgt Unix-sekunder UTC 1735689600 (2025-01-01 00:00:00 UTC)
chain_genesis_time drand chain info (genesis_time) 1595431050
chain_period_seconds drand chain info (period) 30

ceil()-operasjonen velger den første drand-runden hvis avsløringstid er ≥ unlock_at. Dette sikrer at qub-en ikke blir mulig å dekryptere før den valgte opplåsingstiden.

Grensetilfelle: hvis (unlock_at - chain_genesis_time) er nøyaktig delelig med chain_period_seconds, er resultatet nøyaktig den runden — qub-en låses opp presist ved den rundens avsløringstid.

Validering: unlock_at MUST være i framtiden ved forseglingstidspunkt. unlock_at MUST NOT være mer enn 10 år fra created_at (for å begrense risiko knyttet til drand-avhengighet med lang horisont; brukergrensesnittet SHOULD advare for opplåsingsdatoer utover 2 år).


5. Wire-format-newtypes

Wire-format-newtypes gir compile-time sikkerhet mot å forveksle CBOR-byte med JSON, rå klartekst eller andre byte-kodinger.

Type Inneholder Produsert av Konsumert av
SealedQubCbor Kanonisk CBOR av SealedQub serialize_sealed_qub() Opplasting til permanent lagring, leserens henting
QubEnvelopeCbor Kanonisk CBOR av QubEnvelope serialize_qub_envelope() tlock-krypteringsinngang, tlock-dekrypteringsutgang

5.1 Konstruksjonsregler

// 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 Validering ved konstruksjon

from_encoded() SHOULD validere at inndataen starter med en gyldig CBOR map-header. Full strukturell validering skjer ved parse-tid, ikke ved konstruksjonstid, for å unngå dobbel parsing.


6. Content type-register

Verdi Type Maks kroppsstørrelse Merknader
0x00 Reservert (ugyldig) MUST NOT brukes
0x01 Rentekst (UTF-8, begrenset Markdown) 50 KB betalt / 10 KB gratis Se §10 for gjengivelsesregler. Splittet gratis/betalt håndheves av opplastingstjenesten; det harde taket på protokoll-laget er 50 KB.
0x02 Reservert (fremtidig) Tildelt for en fremtidig content type; ikke gyldig i v1. Lesere MUST avvise per regelen nedenfor.
0x03 Pakt (bilateral avtale, CBOR-kropp) 100 KB Kroppen er kanonisk CBOR PactTerms (§6.1). Medundertegner-signering per §9.7.
0x04 Dom (skaperens selvbedømmelse, CBOR-kropp) 8 KB Kroppen er kanonisk CBOR VerdictBody (§6.2). Sendes ut kun av system-side verdict-intensjonen. Moder-relasjonen ligger på Arweave-taggen Parent-Tx-Id, ikke i kroppen. Se verdict-uplift-plan §3.4.

Lesere MUST avvise ukjente content types med en tydelig brukersynlig feil. Lesere MUST NOT forsøke å gjengi ukjente typer som tekst.

6.1 Pakt-kropp (content_type = 0x03)

En pakt-kropp er den kanoniske CBOR-kodingen av en PactTerms-verdi:

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), value: String (≤ 2,000) }   // NFC on both sides
PartyIdentifier{ label: String (≤ 100), contact: Option<String (≤ 320)> }

Kanoniske CBOR-nøkkelrekkefølger for alle tre mapene er gitt i §3.2. Total serialisert pakt-CBOR MUST NOT overstige 100 KB (samsvarer med §6).

Skjemadiskriminator. Den første raden i terms for en structured/v1-pakt MUST være { key: "pact_schema", value: "structured/v1" }. Rader uten denne markøren er «egendefinerte» pakter og får ingen strukturert validering eller skjemabevisst gjengivelse.

Frosne bekreftelses-slotter. structured/v1-pakter har nøyaktig fire bekreftelsesrader under disse nøklene:

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

value for hver er én av åtte frosne engelske strenger valgt av paret (role, kind), der role ∈ { seller, buyer, provider, client } og kind ∈ { standard, capacity }. Selve strengene er normative protokolldata — begge parters ML-DSA-65-signaturer forplikter seg til de eksakte bytene via body_hash. De er IKKE lokaliserte; den signerte kroppen er språknøytral. Enhver formuleringsendring krever en ny skjemaversjon (structured/v2).

De åtte strengene, deres oppslag (acknowledgement_for(role, kind)), og begrunnelsen for hver er fastsatt av referanseimplementasjonen. Samsvarende implementasjoner MUST sende ut byte-identiske bekreftelsesverdier; gylne-fixture SHA3-256 body-hash-tester som dekker alle fire rollekombinasjoner fanger opp eventuell drift.

Visningsrekkefølge for leseren. Bekreftelsesstrengene inneholder fraser som «described above», som forutsetter at beskrivelses-/omfangsradene gjengis foran bekreftelsene. Lesere MUST gjengi terms-arrayet i CBOR-rekkefølge; omorganisering bryter prosa-semantikken.

Motpartskontakt. Når Part Bs contact er en gyldig e-postadresse, sender qub-opplastingstjenesten automatisk en gjennomgangs-/medsignaturinvitasjon på e-post ved iscenesettelsestidspunkt og binder den eventuelle medsignaturen til verifisering av samme adresse (§9.7). Pakter der Part Bs kontakt mangler kan fortsatt medundertegnes, men kun via en out-of-band-kanal — tjenesten avviser medsignaturforespørsler som ikke kan produsere en matchende 15-minutters e-postverifiseringsmarkør.

6.2 Dom-kropp (content_type = 0x04)

En dom-kropp er den kanoniske CBOR-kodingen av en VerdictBody-verdi:

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; «hva endret seg, hva lærte du»
    evidence_url:    Option<String>,      // ≤ 2,048 bytes; bare HTTPS; nøkkelen er fraværende når den utelates
}

Kanonisk CBOR-nøkkelrekkefølge:

"outcome"          (8 encoded bytes)
"reflection"       (11 encoded bytes)  ← bare hvis til stede
"evidence_url"     (13 encoded bytes)  ← bare hvis til stede
"verdict_version"  (16 encoded bytes)

Total serialisert dom-CBOR MUST NOT overstige 8 KB (samsvarer med registerraden ovenfor).

Utfalls-enum. Wire-byten er intensjonsnøytral; de fire bøttene Right / Partial / Wrong / Unfalsifiable dekker utfallsrommet for enhver dom-bærende intensjon. Per-intensjons-etiketter («Traff blink» / «Holdt» / «Levert» / «Bekreftet» for Right, osv.) er en leser-side gjengivelses-anliggende som løses opp mot moder-qub-ens intensjon — wire forblir språk- og intensjonsnøytral. Verdier utenfor 1..=4 MUST avvises ved dekoding.

Moder-kobling. En dom-qub bærer IKKE moder-referansen i kroppen. Moder-qub-ens Arweave-transaksjons-ID sendes ut som lagrings-taggen Parent-Tx-Id ved opplastingstidspunktet (§7 lagrings-tagg-laget). Dette holder kroppen som et selvstendig signert utsagn om selvvurdering; revisjonskjeden («rett om hva?») etableres via Arweave-tagg-oppslag.

Beviselenke-sikkerhet (normativt). Når evidence_url er til stede, MUST validatorer (kompose-side, wire-side, Worker-edge) håndheve:

  1. Bare HTTPS. Strengen MUST starte med bytesekvensen https://. Enhver annen skjema — http, ftp, javascript, data, file, osv. — avvises.
  2. Lengdetak. ≤ 2 048 byte (praktisk grense for nettlesere-URL).
  3. NFC + sjekk på fiendtlige kodepunkter. Samme regel som title og reflection — bidi-override / nullbredde / tag-blokk / BOM / C0 / C1 kodepunkter avvises. Definisjonen samsvarer med Rust crate::handle::contains_hostile_text_codepoint og TS workers/api/src/utils/unicode.ts::isHostileCodepoint (hold i lås).
  4. Ingen mellomrom, ingen ASCII-kontrolltegn. Mellomrom / DEL / sub-0x20-byte hvor som helst i URL-en avvises — lukker injeksjonsvektoren \n/\t som bidi-regelen ikke dekker.
  5. Ikke-tomt vertssegment. Alt mellom https:// og første /, ? eller # MUST være ikke-tomt.

Ingen server-side henting. Worker MUST NOT proxyere, hente eller forhåndsvise URL-en. Protokollen lagrer en streng; gjengivelsen skjer leser-side med rel="nofollow noopener noreferrer" target="_blank" og en synlig vert vist ved siden av lenketeksten.

Refleksjon. Valgfri skaper-skrevet refleksjonstekst («hva endret seg, hva lærte du»). Samme NFC + sjekk på fiendtlige kodepunkter som title. Tom / bare-mellomrom-inndata kollapser til fraværende ved konstruksjon.

Skjemaversjon. v1 støtter kun verdict_version = 0x01. Framtidige skjemarevisjoner bumper denne byten og lander sammen med en ny protokollversjon per §12.


7. Forseglingsprotokoll

Den komplette forseglingssekvensen. Hvert trinn er normativt.

 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.
 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 = ceil((unlock_at - chain_genesis_time) / chain_period_seconds).
    (Computed here, before qub_id, because drand_round is bound into the qub_id
    preimage — §4.1, V1.2.)
 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,
    unlock_at, drand_chain_id, drand_round.
12. Serialise SealedQub using canonical CBOR → SealedQubCbor.
12a. Generate K = 32 random bytes (CSPRNG) and N = 12 random bytes (CSPRNG).
     Compute W = wrap_sealed_qub(SealedQubCbor, qub_id=qub_id, key=K, nonce=N)
     per §13. The bytes uploaded to permanent storage are the OuterWrapper CBOR W,
     never the bare SealedQubCbor. K leaves the device only as the URL
     fragment in step 16.
13. Display seal-time disclosure. User confirms.
14. Validate upload eligibility via the qub upload service (bot-detection, entitlement, rate limits).
15. Submit W (the OuterWrapper bytes) to the qub upload service; the service
    signs and uploads to permanent storage. The service is byte-blind to the inner
    SealedQubCbor and never receives K.
16. Receive arweave_tx_id from the service. Construct delivery URL as
    `<origin>/c/<arweave_tx_id>#<base64url(K)>` (or `<origin>/s/<short_code>#<base64url(K)>`
    when a short code is allocated). Browsers do not transmit URL fragments
    to servers, so K is never observed by qub.social or any storage gateway.

Lagringstaggslag (out-of-band). qub-opplastingstjenesten knytter et bevisst lite sett med lagringstransaksjonstagger sammen med den innpakkede nyttelasten. Content-Type=application/octet-stream er normativt påkrevet. Referansetjenesten knytter i tillegg tre valgfrie tagger når skaperen velger å eksponere dem: Intent (allowlist-validert kompositt-intensjon — f.eks. quote, reply, commitment), Author (skaperens §9.3-pubkey-fingeravtrykk som 64-tegns små heksadesimal), og Parent-Tx-Id (overordnet qubs lagringstransaksjons-ID for svarkjeder, 43-tegns base64url).

Author-taggen er opt-in per qub: referanse-skaper-appen knytter den kun til når brukeren eksplisitt aktiverer offentlig tilskrivelse ved forseglingstidspunkt. Når veksleren er av — som er standard — skrives ingen Author-tagg og qub-en er utilskrevet på kjeden: ingenting i permanent lagring lenker opplastingen til en skapers handle, e-post eller andre qubs. Når veksleren er på, løser Author-fingeravtrykket til skaperens valgte @handle via §9.5-attesteringskjeden. Forhold i svarkjeder og Intent er ikke-identifiserende. Det ytre omslaget (§13) beskytter den indre kroppen mot ciphertext-korrelasjon — det hindrer en høster i å gjenkjenne og masse-dekryptere qub-formede opplastinger etter at deres drand-runde publiseres.

Referansetjenesten knytter bevisst IKKE App-Name, App-Version eller Type-tagger: ethvert slikt enkeltverdifilter ville returnert hele qub-korpuset til et GraphQL-spørring, noe som er inkonsistent med omslagets body-only-konfidensialitetsomfang.

En samsvarende verifikator MUST NOT avhenge av noen lagringstagg for §11 tredjeparts-verifikasjon; body-hashen / qub_id / signaturen forplikter seg kun til den indre CBOR, aldri til taggsettet.


8. Opplåsningsprotokoll

Den komplette opplåsingssekvensen. Hvert trinn er normativt.

 1. Viewer opens delivery URL. Extract arweave_tx_id from path AND
    K = base64url_decode(fragment) from the URL fragment. If the fragment
    is absent or malformed → display "this URL is missing its decryption
    key" and stop; the viewer MUST NOT contact the storage gateway
    without K, since fetching wrapped bytes the viewer cannot decrypt
    serves no purpose and only leaks the access attempt.
 2. Check denylist. If tx_id is denylisted → display block message. Stop.
 3. Fetch OuterWrapper bytes from permanent storage (with multi-gateway fallback).
 3a. Unwrap: parse the bytes as OuterWrapper (§13), verify the wrapper
    `version` byte is `0x01`, and compute SealedQubCbor =
    unwrap_sealed_qub(OuterWrapper, key=K). Any AEAD authentication
    failure (wrong K, tampered ciphertext, swapped qub_id-as-AAD,
    swapped nonce) → display "this URL's decryption key does not match
    the stored qub" and stop. Authentication failures are
    indistinguishable to the viewer per §13.5.
 4. Parse SealedQubCbor → SealedQub.
 5. Validate: SealedQub.version is known (0x01). Reject unknown versions.
 6. If current time < SealedQub.unlock_at → display countdown. Poll or wait.
 6a. Round-binding check (V1.2). Recompute expected_round =
    ceil((SealedQub.unlock_at - chain_genesis_time) / chain_period_seconds).
    Reject unless SealedQub.drand_round == expected_round AND the round baked
    into the tlock ciphertext stanza (read via the age/tlock header, no signature
    required) == expected_round. 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.
13. Verify: QubEnvelope.content_type is known and renderable.
    Known values: 0x01 (text), 0x03 (pact). 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 appropriate renderer (see §10 for text, §6 for pact).
17. Construct RevealedQub for display.

9. Forfattersignering

9.1 Begrunnelse

Alle qub-er lagres i permanent lagring. Forfattersignaturer må forbli uforfalskbare på ubestemt tid, og det er derfor v1.0 bruker det post-kvantale ML-DSA-65-skjemaet (FIPS 204) framfor et klassisk skjema hvis sikkerhet kan svekkes innenfor qub-ens permanente levetid.

9.2 Algoritmeregister

sig_alg Skjema Nøkkelstørrelse Signaturstørrelse
0x00 Ingen signatur (usignert)
0x01 ML-DSA-65 (FIPS 204) 1 952 byte 3 309 byte

Lesere MUST avvise ukjente sig_alg-verdier.

9.3 Konstruksjon av signert preimage

Det har eksistert to preimage-versjoner. Alle signaturer MUST bruke V2, og verifikatorer MUST akseptere kun V2. Det eldre V1-preimaget (dokumentert nedenfor for historisk referanse) ble akseptert som et rent verifikasjonstilbakefall under V2-migreringen; det tilbakefallet er nå avviklet, og en signatur som kun er V1 avvises nå.

V2 (gjeldende — produsert av all ny forfattersignering, og av begge signaturene i paktens iscenesettelses-/medsigneringsflyt):

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 følger samme fraværssentinel-konvensjon som title_hash (§4.2.1): 32 nullbyte er ikke en gyldig SHA3-256-utdata, så «fraværende» kan aldri kollidere med en tilstedeværende etikett. Alle felt har fast bredde, så preimaget er entydig uten lengdeprefikser.

V1 (eldre — AVVIKLET; ikke lenger produsert og ikke lenger akseptert ved verifikasjon):

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

V1-preimaget utelot sender_label og reply_to. Det ble akseptert som et rent verifikasjonstilbakefall under migreringen til V2; det tilbakefallet er siden avviklet — verifikatorer MUST akseptere kun V2-preimaget. Definisjonen beholdes her for historisk referanse og for å forklare domeneseparatoren nedenfor. En signatur som kun verifiserer mot V1 MUST behandles som en verifikasjonsfeil.

Domeneseparatorer: "QUB_AUTHOR_SIG_V1" / "QUB_AUTHOR_SIG_V2" er 17 ASCII-byte hver ([0x51, 0x55, 0x42, 0x5F, 0x41, 0x55, 0x54, 0x48, 0x4F, 0x52, 0x5F, 0x53, 0x49, 0x47, 0x5F, 0x56, 0x31/0x32]). Ingen padding. Den ulike separatoren domeneseparerer de to konstruksjonene, så en signatur over det ene preimaget kan aldri verifisere som det andre.

org_id_present-byte: byten som følger unlock_at MUST være 0x00. Referanseimplementasjonen eksponerer dette som konstanten ORG_ID_PRESENT_INDIVIDUAL = 0x00 i crates/qub-core/src/signing.rs; lesere som rekonstruerer sig_input for verifikasjon MUST sende ut samme byte.

Signaturomfang — hva som er og ikke er dekket. V2-sig_input forplikter seg direkte til version, qub_id, body_hash, unlock_at, sender_label og reply_to (i tillegg til den faste domeneseparatoren og org_id_present-byten). qub_id er selv utledet fra version, content_type, created_at, unlock_at, outcome_at, drand_round og body_hash via §4.1-preimagen, så enhver endring av disse feltene gir en annen qub_id og invaliderer signaturen transitivt. Den autentiserte flaten er derfor:

Felt Autentisert av signatur Hvordan
version Direkte input til sig_input
qub_id Direkte input
body_hash Direkte input
unlock_at Direkte input
sender_label Direkte input via sender_label_hash (V2-preimage — den eneste aksepterte formen)
reply_to Direkte input via reply_to_or_zero (V2-preimage — den eneste aksepterte formen)
content_type Transitivt, via qub_id-preimage
created_at Transitivt, via qub_id-preimage
outcome_at Transitivt, via qub_id-preimage
drand_round Transitivt, via qub_id-preimage (V1.2)
body Transitivt, via body_hash = SHA3-256(body)
author_pubkey — (implisitt) Nøkkelen som verifiserte signaturen er forfatteren, per definisjon
cosigner_pubkey / cosigner_signature Uavhengig signert over samme sig_input (se §9.7)
drand_chain_id, tlock_ciphertext, visibility Ytre SealedQub-felt, ikke inne i envelopen — dekket av sine egne strukturelle invarianter (runde-/kjedekonsistens) men ikke av forfattersignaturen. (drand_round er nå bundet transitivt via qub_id-preimagen — se over.)

Hvorfor V2 er det eneste aksepterte preimaget.

Implementasjoner som viser sender_label eller reply_to til sluttbrukere MUST vise den autentiserte identiteten (pubkey-fingeravtrykk, attestering) som det primære identitetssignalet, ikke etiketten.

9.4 Verifikasjonsprosedyre

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."

Signaturverifikasjon er den dyreste operasjonen (spesielt ML-DSA-65). Den SHOULD utføres etter at alle billigere sjekker (hash, qub_id, unlock_at) har passert.

9.5 Identitetsattesteringer

Identitetsattesteringer — kartleggingen av author_pubkey til menneskelig gjenkjennelige identitetspåstander som et qub-handle, e-postadresse, sosial handle eller passkey-legitimasjon — er en leserside-progressiv forbedring og er ikke nødvendige for signaturverifikasjon. Lesere som løser attesteringer til en visningsidentitet MUST anvende presedensen:

handle > email > social > fingerprint

Fingeravtrykk-tilbakefallet er små heksadesimal av SHA3-256(author_pubkey); det er alltid tilgjengelig for enhver signert qub. Lesere MAY forkorte det for visning — referanseleseren gjengir qub: etterfulgt av de første og siste fire bytene (qub:<8 hex>…<8 hex>).

En samsvarende verifikator kan fullføre hver sjekk i §9.4 uten å kontakte qub-API-et, uten noe nettverk utover permanent lagring og drand, og uten noe oppslag på serversiden. Attesteringsoppløsning er et separat best-effort-trinn utført kun etter at signaturverifikasjon har lyktes.

9.6 Størrelsesinnvirkning

Ed25519 ML-DSA-65
Signatur 64 byte 3 309 byte
Offentlig nøkkel 32 byte 1 952 byte
Totalt per qub 96 byte 5 261 byte
Lagringskostnadsdelta (ved ~$5/MB) ~$0,0005 ~$0,026

For en tekst-qub på 500–2 000 byte tredobler ML-DSA-65 omtrent den lagrede størrelsen. Den absolutte kostnaden er ubetydelig.

9.7 Medsignaturverifikasjon (bilaterale paktavtaler)

For bilaterale avtaler (content_type = 0x03) beviser et andre signaturlag at begge parter samtykket til de samme betingelsene.

Envelope-felt:

Begge felt MUST være til stede sammen eller begge fraværende. Hvis nøyaktig ett er til stede, MUST lesere rapportere en integritetsfeil.

Verifikasjonsprosedyre:

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."

Egenskaper:

E-postbindingsport (operasjonell). Når en iscenesatt pakt har en Part B e-postkontakt (§6.1), MUST qub-opplastingstjenesten avvise medsignaturforespørselen med mindre en kortvarig e-postverifiseringsmarkør eksisterer som matcher både iscenesettelses-ID-en og den normaliserte e-posthashen til den kontakten. Markøren skrives av /api/v1/auth/verify når magic-link-tokenet bærer en staging_id og den verifiserte adressen samsvarer med SHA-256(normalise_email(party_b.contact)) — der normalise_email(addr) bevarer storingbruken i lokaldelen og gjør kun domenedelen til små bokstaver (per RFC 5321 §2.3.11), og SHA-256 her er NIST FIPS 180-4-hashen (forskjellig fra SHA3-256 brukt i §4-utledninger) — og utløper 900 sekunder (15 minutter) etter utstedelse. Dette er en operasjonell anti-imitasjonsport, IKKE en del av on-chain-qub-beviset — en tredjepartsverifikator som spiller av §11 trenger kun permanent lagring og drand, uten noe oppslag på serversiden. Markøren eksisterer kun på serversiden og er aldri en del av den signerte kroppen.

Størrelsesinnvirkning (ML-DSA-65 forfatter + medundertegner):

Komponent Størrelse
Forfattersignatur 3 309 byte
Forfatterens offentlige nøkkel 1 952 byte
Medundertegnerens signatur 3 309 byte
Medundertegnerens offentlige nøkkel 1 952 byte
Total krypto-overhead 10 522 byte
Lagringskostnadsdelta ~$0,05

10. Markdown-gjengivelse og -sanitisering

Denne seksjonen er sikkerhetskritisk. Leseren gjengir tekst-qubs (content_type = 0x01) ved å bruke et begrenset Markdown-delsett.

10.1 Tillatte elementer

10.2 Forbudte elementer

Element Håndtering
Rå HTML (<div>, <script>, osv.) Fjernes helt. Ingen HTML slipper gjennom.
Bilder (![alt](url)) Fjernes. Bildesyntaks fjernes fra utdataen.
Lenker ([text](url)) URL gjengis som synlig rentekst. Ikke autolenket. Ikke klikkbar uten eksplisitt brukerhandling.
Farlige URL-skjemaer javascript:, data:, vbscript:, file: — fjernes.
Iframes, embeds, objects Fjernes.
HTML-entiteter Dekodes til visningstegn kun hvis trygge.

10.3 Implementasjon

Implementasjoner MUST bruke en streng allowlist-parser, ikke en blocklist. Den anbefalte tilnærmingen:

  1. Parse Markdown med pulldown-cmark (eller tilsvarende).
  2. Gå gjennom AST-en og dropp enhver node som ikke er på allowlisten (§10.1).
  3. For lenkenoder: send ut URL-en som synlig tekst, ikke som et klikkbart <a>-element.
  4. Konverter den filtrerte AST-en til en typet mellomrepresentasjon (f.eks. en MarkdownNode-enum med kun trygge varianter). Rå HTML er strukturelt urepresenterbar i denne IR-en.
  5. Gjengi fra den typede IR-en til mål-visningslaget (f.eks. reaktive visningskomponenter, DOM-noder). Ingen HTML-strengkonkatenering eller innerHTML på noe tidspunkt.

Blocklist-tilnærminger er sårbare fordi nye Markdown-utvidelser eller parser-særegenheter kan introdusere ufiltrerte elementer. Den typede AST-tilnærmingen gjør XSS strukturelt umulig — det finnes ingen variant som kan bære vilkårlig HTML.

10.4 Størrelses- og strukturgrenser


11. Tredjepartsverifikasjon

Enhver tredjepart kan verifisere en offentlig qub uten samarbeid fra qub. Verifikasjonsprosedyren:

1. Obtain arweave_tx_id (from delivery URL or direct knowledge).
2. Fetch SealedQubCbor from any storage gateway.
3. Confirm storage block inclusion (block height, block timestamp).
4. Parse SealedQubCbor → SealedQub.
5. Fetch drand round signature for SealedQub.drand_round.
6. tlock_decrypt(tlock_ciphertext, round_signature) → QubEnvelope CBOR bytes.
7. Parse → QubEnvelope.
8. Verify SHA3-256(body) == body_hash.
9. Verify QubEnvelope.qub_id == SealedQub.qub_id.
10. Verify QubEnvelope.unlock_at == SealedQub.unlock_at.
11. If sig_alg != 0x00: verify author_signature (see §9.4).
12. All checks pass → qub is verified.

Hva verifikasjonen beviser:

Bevis Hva det fastslår
Forpliktelse Cipherteksten eksisterte ved lagringsblokkens tidsstempel.
Integritet Klartekstkroppen samsvarer med den forpliktede hashen og har ikke blitt endret.
Tidspunkt Innholdet var uleselig fram til drand-runden, som tilsvarer den valgte opplåsingstiden (forutsatt tlock- og drand-sikkerhetsantagelser).

Hva verifikasjonen IKKE beviser:

Ikke-bevis Hvorfor
Forfatterskap sender_label er dekorativ. Uten sig_alg0x01 kunne hvem som helst ha forseglet dette innholdet.
Intensjon qub-en beviser innhold og tidspunkt, ikke hva skaperen subjektivt mente.
Tidspunkt før hendelse Lagringsblokk-inkludering kan ligge minutter etter faktisk opplasting. Forpliktelsestidsstempelet er blokk-tiden, ikke øyeblikket brukeren trykket «forsegle».

12. Versjonering

12.1 Protokollversjon

version-feltet (u8) i både SealedQub og QubEnvelope identifiserer hovedprotokollversjonen.

12.2 Versjonshistorikk

Versjon Verdi Beskrivelse
v1 0x01 Offentlige tekst-qubs (content_type 0x01), bilaterale paktavtaler (0x03, structured/v1-skjema, ML-DSA-65 forfatter + medundertegner), tlock, SHA3-256

12.3 Fremtidskompatibilitet

En v1-leser som støter på en QubEnvelope med ukjente CBOR-map-nøkler (nøkler som ikke er i §3.2 kanoniske rekkefølge) MUST avvise den med en dekodingsfeil (§3.1). Fremtidskompatibilitet hviler på version-feltet, ikke på nøkkeltoleranse: fremtidige tilføyelser — selv mindre metadata — leveres under en ny version-verdi, som en v1-leser avviser med en tydelig «nyere protokoll»-feil i stedet for stille å forkaste innhold som signaturene forplikter seg til.

En v1-leser som støter på sig_alg = 0x01 (ML-DSA-65) men mangler ML-DSA-65-verifikasjonsstøtte SHOULD vise qub-innholdet med et varsel om «signatur til stede, men ikke verifiserbar», ikke avvise qub-en helt. Referanseimplementasjonen avviser i dag enhver sig_alg-verdi annet enn 0x00 og 0x01 fordi v1-registeret ikke inneholder noen annen gyldig algoritme — streng avvisning og soft-fail er observasjonelt identiske inntil en tredje algoritme er registrert. Soft-fail-atferden ovenfor blir bærende når §9.2 tar opp en ny oppføring, og referanseleseren vil bli oppdatert til soft-fail på det tidspunktet.

12.4 Ytre omslagsversjon

OuterWrapper beskrevet i §13 bærer sin egen version-byte, uavhengig av SealedQub.version og QubEnvelope.version. De to versjonsrommene utvikler seg separat: en framtidig post-kvantesikker symmetrisk erstatning hever omslagsbyten uten å berøre den indre protokollversjonen, og en framtidig protokoll-lag-tilføyelse (f.eks. et nytt envelope-felt) hever den indre versjonen uten å berøre omslagsbyten.

OUTER_WRAPPER_VERSION_* Verdi Algoritme Status
OUTER_WRAPPER_VERSION_1 0x01 AES-256-GCM med 12-byte nonce, 16-byte autentiseringstagg, AAD bundet til qub_id v1-standard
0x020xFF Reservert Framtidig

Lesere MUST avvise ukjente omslagsversjoner med en tydelig feil. Protokollen holder bevisst omslagsversjonsrommet smalt inntil en konkret migrasjonsdriver dukker opp (f.eks. NIST-veiledning som favoriserer en annen AEAD); et 0x02-slot vil bli tildelt i samme revisjon som introduserer algoritmen.


13. Ytre krypteringsomslag

13.1 Begrunnelse

Protokoll-lagene (QubEnvelope → tlock → SealedQub) gjør en forseglet qub tidslåst: kroppen er uleselig fram til unlock_at og drand-rundesignaturen er publisert. Etter opplåsing er imidlertid rundesignaturen offentlig og den kanoniske CBOR-formen til SealedQub gjenkjennelig, så en høster som indekserte transaksjoner i permanent lagring kunne masse-dekryptere hele qub-korpuset.

Det ytre krypteringsomslaget lukker den kanalen ved å plassere et ekstra symmetrisk AEAD-lag mellom den kanoniske SealedQubCbor og bytene som lastes opp til permanent lagring. Den 256-biters nøkkelen K lever kun i URL-fragmentet til leveringslenken og på brukerenheter; nettlesere overfører ikke URL-fragmenter til servere, så qub.social, hver lagringsgateway, og hvert CDN foran dem er observasjonelt blinde for K. Hver qub i permanent lagring er derfor en ugjennomsiktig ciphertext hvis klartekst ikke kan gjenopprettes uten URL-en skaperen valgte å dele.

Netto effekt:

13.2 Lagdeling

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
  ↓ AES-256-GCM(K, nonce, AAD=qub_id) (§7 step 12a, this section)
OuterWrapper CBOR bytes              ← uploaded to permanent storage (§7 step 15)

Forsegling og opplåsing på protokoll-laget (§7, §8) er uendret under omslagsgrensen; omslaget kobles til ved kallstedet for seal() og kobles fra ved kallstedet for unlock().

13.3 OuterWrapper-datastruktur

struct OuterWrapper {
    version:    u8,           // 0x01, see §12.4
    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
}

Felt-invarianter.

CBOR-koding. Kanonisk CBOR per §3, med samme nøkkelsorteringsregel (sortert etter kodet bytelengde stigende, deretter leksikografisk). De fire nøklene er:

Nøkkel Kodede byte Rekkefølge
nonce 6 1
qub_id 7 2
version 8 3
ciphertext 11 4

Den første byten til OuterWrapper-CBOR-en er derfor map-headeren med definitiv lengde for en 4-elements map (0xA4).

13.4 AAD-binding til qub_id

Omslaget binder qub_id som AEAD additional authenticated data. Dette er det bærende strukturelle forsvaret mot tre klasser av angrep:

Angrep Forsvar
Flytt ciphertext under et annet qub_id-felt i omslaget AAD-uoverensstemmelse → AEAD-autentisering mislykkes
Bland URL-fragmentet til qub A med byte-ene i permanent lagring til qub B AAD-uoverensstemmelse → AEAD-autentisering mislykkes
Tukle med qub_id-feltet til omslaget etter opplasting AAD-uoverensstemmelse → AEAD-autentisering mislykkes

Å bære qub_id i omslagets klartekst svekker ikke enumereringsimmunitet på en meningsfull måte — qub_id er selv en SHA3-256-hash av §4.1-preimagen uten noen gjenopprettbar preimage fra digesten, og en enumerator som allerede har høstet omslagets byte lærer ingenting fra det synlige qub_id som de ikke kunne utlede fra eksistensen av opplastingen selv.

13.5 Innpaknings- og utpakkingsalgoritmer

wrap_sealed_qub(SealedQubCbor S, qub_id Q, key K, nonce N):
    require K.len() == 32 and N.len() == 12 and Q.len() == 32
    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.4
    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
    return P                            // P is the inner SealedQubCbor

Kollaps av feilmoduser. Feil K, feil nonce, AAD-uoverensstemmelse og tuklet ciphertext gir alle samme DECRYPT_FAILED-feil. Dette er en bevisst AEAD-egenskap: å skille feilmodusen ville skape en sidekanal en fjern angriper kunne sondere ved å sende defekte omslag og time responsen. Referanseimplementasjoner MUST kollapse alle AEAD-feil til én enkelt feilform.

13.6 Nøkkelmateriale og distribusjon

Innpakkingsnøkkelen K er en 256-biters uniform tilfeldig verdi generert per-qub av en CSPRNG. Referanseimplementasjonene henter den fra:

Distribusjon: K MUST kodes som URL-sikker base64 (RFC 4648 §5, ingen padding) og legges til leveringslenken som fragmentkomponenten:

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

Fragmentet overføres aldri til noen server av en samsvarende nettleser. Gjenopprettingskanaler (server-side historikk-indeks, opt-in e-post autosending) som beholder den fullstendige leveringslenken — inkludert fragmentet — utover brukerens enhet, er en eksplisitt avveining mot standard krypto-makulerings-holdningen og MUST betinges av eksplisitt brukersamtykke.

Tap av fragment. Hvis en bruker mister URL-fragmentet og ikke har noen gjenopprettingskanal, er qub-en uleselig. Dette er den bærende avveiningen i designet og MUST opplyses til brukeren ved forseglingstidspunkt. MVP-en styrker forseglings-tidspunkt-opplysningen med eksplisitt «lagre denne URL-en»-tekst og en verifisert-e-post-gjenopprettingskanal for brukere som velger inn.

13.7 Utenfor omfang for denne seksjonen

13.8 Offentlige qubs (utelatelse av omslag)

Det ytre omslaget er valgfritt på leveringslaget. En skaper kan forsegle en qub som offentlig, og i så fall skrives den kanoniske SealedQubCbor til permanent lagring direkte, uten noe OuterWrapper-lag og uten noen nøkkel K:

SealedQubCbor bytes  ──(public)──▶  uploaded to permanent storage as-is
SealedQubCbor bytes  ──(private)─▶  AES-256-GCM(K, …) ▶ OuterWrapper ▶ uploaded

En offentlig qub er tidslåst, men ikke lenkekontrollert: den forblir uleselig fram til drand-runden publiseres (tlock-laget er uendret), men etter opplåsing kan hvem som helst med arweave_tx_id dekryptere den — det kreves ikke noe URL-fragment, fordi det ikke finnes noen K. Dette er den bevisste avveiningen for flater som serveren må drive: e-poster med avsløringsvarsel, tredjeparts-innbygginger og rikere SEO etter avsløring trenger alle en lenke som virker uten en hemmelighet serveren aldri holder (§13.6).

Konsekvenser en produsent MUST ta høyde for:

Privat (innpakket) forblir standarden; offentlig er et eksplisitt per-qub-valg fra skaperen.


14. Testvektorer

14.1 qub_id-utledning

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  = 4695445  (= (1736294400 - 1595431050) / 30, 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 — V1.2):
  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)
  0x000000000047A595 ||  // drand_round as u64 big-endian (4695445)
  body_hash          ||  // 32 bytes
  title_hash             // 32 bytes (all-zeros sentinel; title absent)

Expected output:
  qub_id = SHA3-256(preimage)
         = 3a9fcb31b750d985c262fada6d4f777f
           d6a28be831d941d85c131f5a4bbaf8a4

Implementasjoner MUST produsere identiske body_hash- og qub_id-verdier for denne inndataen. Denne testvektoren SHOULD være den første enhetstesten skrevet. De kanoniske verdiene over ble beregnet av referanseimplementasjonen og MUST stemme bit-for-bit. Historiske preimage-oppsett (før lansering — ingen aktive qubs var avhengige av disse): den 92-byte V1.0-qub_id var 3d9fc2390eab043d38a1669ed3b71be76f9eefe872b9569ab1aaa027b88392b0; den 100-byte V1.1-qub_id (etter folding av outcome_at_or_zero) var b0d032898ad629795150fdcb3f84e518f59ed05b7a2a82bc24ebdb87f52144ed. V1.2 folder drand_round inn og hever domeneseparatoren til QUB_ID_V2.

14.2 Mapping til opplåsingsrunde

Input:
  unlock_at           = 1735689600
  chain_genesis_time  = 1595431050
  chain_period_seconds = 30

Calculation:
  (1735689600 - 1595431050) / 30 = 4675285.0
  ceil(4675285.0) = 4675285

drand_round = 4675285

14.3 Kanonisk CBOR rundtur

Implementasjoner MUST verifisere at serialize(parse(serialize(qub))) == serialize(qub) for alle gyldige innputt. Dette er en egenskapstest, ikke en enkelt vektor.

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)

De kanoniske CBOR-byte-ene og SHA3-256 body_hash beregnes av referanseimplementasjonen. Implementasjoner MUST produsere byte-identisk CBOR for denne inndataen.

Implementasjoner MUST også verifisere at serialize(parse(serialize(pact))) == serialize(pact) for alle gyldige PactTerms-innputt (egenskapstest).

14.5 Tverr-språk-vektorer for ytre omslag

Det ytre omslaget (§13) har en separat kanonisk fixture på crates/qub-core/tests/vectors/wrapper_v1.json. Hvert tilfelle fester et (key, nonce, qub_id, sealed_cbor)-tuppel som ugjennomsiktige hex-innputt og hevder en spesifikk expected_wrapper_hex-utdata. Begge referanseimplementasjonene konsumerer den samme JSON-filen:

Fixturen fester for øyeblikket tre tilfeller:

Tilfelle Dekning
basic-text-public Minste realistiske SealedQub-form; ingen valgfrie felt. Etablerer den kanoniske omslagsformen for en v1.0-typisk qub.
with-recipient-pubkey SealedQub med recipient_pubkey satt (Fase 2-sti). Annet indre CBOR-nøkkelsett, annen qub_id.
longer-body ~4 KiB kropp — utøver multi-byte CBOR-lengdeprefikser inne i både den indre envelopen og den ytre cipherteksten.

Implementasjoner MUST produsere byte-identisk expected_wrapper_hex for de registrerte innputtene. Regenerering av fixturen krever QUB_REGEN_VECTORS=1 cargo test -p qub-core --test wrapper_vectors og er reservert for bevisste formatendringer.


15. Krypto-profil-styring (framtidig)

Denne seksjonen er informativ for v1 og blir normativ første gang en andre algoritme entrer noen av qubs kryptografiske primitiver.

15.1 Nåværende holdning

Protokoll v1 binder nøyaktig én algoritme per primitiv:

Verifikatorer hardkoder for øyeblikket nøkkel- og signaturlengder per primitiv. Ingen smidighetsflate er eksponert av wire-formatet.

15.2 Tiltenkt form

Når en andre algoritme entrer protokollen, vil verifikatoren bli konfigurert for en navngitt CryptoProfile (f.eks. ExqubV1) som lister opp det nøyaktige settet med tillatte verdier per primitiv — sig_algs, drand-kjeder, omslagsversjoner, content types. Profilen er fast ved verifiseringstidspunkt, aldri forhandlet in-band. Enhver verdi utenfor den aktive profilen avvises.

Dette garanterer at å legge til ML-DSA-87 eller aktivere Ed25519 ikke kan retroaktivt svekke eksisterende verifikator-konfigurasjoner: en v1-verifikator forblir en v1-verifikator selv etter at en v2-profil er publisert.

15.3 Utløsningsbetingelser

Forfremme §15 til normativ status når noe av følgende foreslås:

Inntil da er §15 en plassholder som fester migrasjonsformen slik at framtidige PR-er lander mot et kjent mål framfor å re-prosessere forhandlingsflaten fra bunnen av.


16. Transparensloggen og holdbarhetsnivåer (Design — gjennomgang fullført)

Status. Denne seksjonen er en designspesifikasjon. Wire-formatene, hashingen og tillitsmodellen nedenfor er normative for implementasjonen, men ingen transparenslogg-kode har blitt levert ennå. Den eksterne W5-gjennomgangen er fullført: §16.15 nedtegner de avklarte beslutningene og de bindende lanseringsbetingelsene som kom ut av den. Implementasjon kan fortsette under disse betingelsene. §16 forblir framoverskuende i samme forstand som §15 — den fester målet slik at implementasjonen lander mot et ferdig avklart design framfor å re-utlede tillitsmodellen i kodegjennomgang. Den er strengt additiv — hver eksisterende qub beholder sin individuelle Arweave-transaksjon, og det er ingen endring i SealedQub / QubEnvelope-wire-formatet.

16.1 Begrunnelse og holdbarhetsnivåer

I dag hviler en qubs holdbarhet og dens temporale forpliktelse begge på en enkelt Arweave-transaksjon per qub (§11). Det kobler forseglingslatens til Arweave-finalitet, gjør per-qub-opplastingen til et produktkostnadstak (ARWEAVE_DAILY_CEILING), og gir ingen tukle-bevisbar rekkefølge på tvers av qub-er. Transparensloggen legger til to lag under og rundt det enkelte nivået:

Nivå Navn Garanti Når
T1 R2-først synkron bekreftelse Holdbarhetsgulv — forseglede byte skrives til holdbar lagring før forseglingen returnerer (< 300 ms p95). Hver qub, synkront (§16.10).
T2 Batchet transparenslogg-inkludering Universell append-only, tukle-bevisbar forpliktelse + total rekkefølge, forankret til Arweave. Hver qub, utsatt + batchet (§16.5–16.7).
T3 Per-qub Arweave-permanens En individuell Arweave-transaksjon for qub-en. Betalt oppsalg, og Arweave-utilgjengelighets-reserveløsningen (§16.8).

T2 gjør per-qub-Arweave til et valg (T3) framfor den eneste holdbarhetsveien. ARWEAVE_DAILY_CEILING pensjoneres som et produkttak og degraderes til en strømbryter på den dedikerte ankerlommeboken alene (§16.7); brukerforseglinger avvises aldri for å overskride det.

Holdbarhetsærlighet (avklart — §16.15 Q6). Holdbarheten regredierer ikke: T1 R2-skrivingen er synkron og skriv-én-gang, så en gratis-nivå-qub som ikke kjøpte T3 er fullt holdbar i det øyeblikket forseglingen returnerer. Det som blir grovere, er den bevisbare øvre-grense-forpliktelsestiden: for en gratis qub blir den ankerets blokktid framfor en blokktid for en per-qub-transaksjon. Ved lavt volum — den realistiske tidlig-lansering- og lavtrafikk-tilstanden — er den fulle daglige kadensen det typiske gulvet, ikke et sjeldent grensetilfelle. Produktrammingen er derfor en øvre grense med ingen forpliktet numerisk latens — «forseglet og holdbar nå; et uavhengig offentlig tidsstempel legges til ved neste logg-anker (typisk daglig)» — og eksakt-time-forpliktelsesbevis er en betalt T3-egenskap, opplyst på nivåsammenligningsflaten og i betingelsene (§16.11, §16.15 Q6). Enhver tidsgrense er kun en intern SLO, aldri en markedsført SLA.

16.2 LogLeaf-struktur (to forpliktede former)

En loggoppføring er en LogLeaf, kodet som håndskrevet kanonisk CBOR under §3.1-profilen (definitiv lengde, ingen tagger, ingen flyttall, korteste-form heltall, NFC-tekst, valgfrie felt utelatt når de mangler, nøkler ordnet etter kodet bytelengde stigende deretter bytevis). Den kanoniske §3.1-vakten parse → re-encode → compare anvendes på kodingsveien før hashing (ikke bare ved dekoding), slik at to implementasjoner ikke kan være uenige om leaf-bytene gjennom en forskjell i heltallsbredde eller nøkkelrekkefølge. Alle heltall er u8 / u64 / i64; alle digester er 32-byte byte-strenger (bstr[32]). En lagret Arweave-transaksjons-id er en rå 32-byte SHA-256-digest båret som bstr[32], aldri en base64url-tekststreng (samsvarer med §3.3).

Leaf-en har to former valgt av en kind-byte, fordi på standard-opplastingsveien er Worker-en byte-blind: POST /api/v1/upload mottar bare qub_id og unlock_at som ikke-tiltrodde klientpåstanderbody_hash, drand_round, created_at og drand_chain_version er alle forseglet inne i det ytre §13-omslaget, hvis nøkkel Worker-en aldri holder. Bare server-seal-veien (POST /api/v1/seal) utleder body_hash / drand_round fra klartekst. En enkelt leaf-form som bærer body_hash + drand_round ville derfor forplikte verdier operatøren aldri verifiserte for flertallet av reelle qub-er. Splittet holder hver forpliktet verdi ærlig:

Nøkkel Kod. len Type Tilstedeværelse Betydning
seq 4 u64 påkrevd Global 0-basert leaf-indeks; posisjonen inkluderingsbeviset forplikter seg til.
kind 5 u8 påkrevd 0x01 attestert (server-seal) eller 0x02 påstått (klient-seal / byte-blind opplasting).
ref 4 bstr[32] påkrevd Leaf-referanse-id. Attestert → rå qub_id. Påstått → den blindede id-en SHA3-256(qub_id ‖ log_blind_secret) (§16.2.1).
chash 6 bstr[32] påkrevd Innholdsadresse SHA3-256(stored_bytes) — den ene innholdsbindingen Worker-en alltid kan beregne ærlig, på begge veier.
unlock_at 10 i64 påkrevd Kopiert (attestert) eller påstått (påstått); validert > 0 før den entrer leaf-en.
received_at 12 i64 påkrevd Worker-veggklokke ved R2-bekreftelse. Ikke-bevisbærende (operatør-påstått; §16.6). Til stede for selvbeskrivelse, aldri et bevis. Validert > 0.
body_hash 10 bstr[32] bare kind=0x01 Utelatt på 0x02 — Worker-en mangler den under §13.
drand_round 12 u64 bare kind=0x01 Utelatt på 0x02.

En kind=0x02-leaf forplikter med vilje verken body_hash eller drand_round: den attesterer forpliktelsen til og rekkefølgen av en ugjennomsiktig ciphertext på innholdsadressen chash, som hevder qub_id og unlock_at — ikke dens klartekst eller runde. Klartekst-/runde-leddene for en påstått qub kommer fra den eksisterende §11 .qub-bundle-verifikasjonen, ikke fra loggen (§16.11). drand_chain_version er ikke i leaf-en (den er inne i omslaget på standardveien); kjedegranularitet bor på ankeret (§16.7). Enkoder-disiplin: avvis en helt-null ref eller chash, og avvis ikke-positiv unlock_at / received_at, og speiler outcome_at > 0-sentinel-vakten i cbor.rs.

16.2.1 Blinding av private qub-er

Loggen må ikke bli det enumereringsoraklet som det ytre §13-omslaget eksisterer for å forhindre (§13.1). For en privat (innpakket) qub forplikter asserted-leaf-en den blindede identifikatoren SHA3-256(qub_id ‖ log_blind_secret), der log_blind_secret er en server-holdt hemmelighet, og utelater body_hash. En tredjepart kan ikke knytte en slik leaf til en bestemt qub_id; qub-ens innehaver, som har leveringslenken og dermed qub_id, kan rekalkulere blindingen for å bekrefte sin egen inkludering. En offentlig qub (allerede enumererbar, bærer allerede Visibility: public-Arweave-taggen per §13.8) forplikter den rå qub_id. Dette er det ene stedet der frittstående verifiserbarhet med vilje viker for en bærende personvern-invariant; den frittstående bindingen for private qub-er er chash (§16.9).

log_blind_secret-forvaring (avklart — §16.15 Q4). Blindingen beskytter leaf-ulinkbarhet, ikke klartekst-konfidensialitet (§13-omslaget holder den uavhengig). Ved en log_blind_secret-kompromittering, for enhver qub_id motstanderen allerede holder eller kan rekonstruere (hver qub hvis bundle/URL den har, pluss enhver lav-entropi eller offentlig qub_id), rekalkulerer den leaf-ens ref i én hash og knytter den — dette er direkte linking av en kjent populasjon, ikke et brute-force over et ukjent rom. Klassifiser log_blind_secret som en korrelasjon/Sybil-grad-hemmelighet i samme forvaringsnivå som andre serverhemmeligheter, og roter bare forover (en rotasjon re-blinder framtidige leaf-er; den kan ikke retroaktivt avlinke allerede-forankrede).

16.3 Leaf- og node-hashing

RFC 6962 §2.1 domene-separert hashing med SHA-256 erstattet av 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

Domeneprefiks-bytene 0x02 (oppføringskjede, §16.4) og 0x03 (STH-hash, §16.6) er reservert og disjunkte fra disse. De er enkeltbyte og kan derfor ikke kollidere med de eksisterende 10-byte ASCII-domeneseparatorene (QUB_ID_V2, osv.). Treet er RFC 6962 venstre-fullt ubalansert tre (hver indre splitt ved den største toerpotensen strengt mindre enn antallet subtre-leaf-er), som lar inkluderings- og konsistensbevis dele én audit-path-algoritme. Referansespesifikasjonen bærer eksplisitt venstre/høyre-utlednings-pseudokode og fester en ikke-toerpotens (5-leaf) testvektor slik at høyre-kant-promoteringstilfellet — som en 4-leaf-vektor skjuler — utøves.

16.4 Hash-kjeding (intern)

LogDO-en vedlikeholder en intern oppføringskjede kun for krasj-konsistens. Den er aldri publisert og aldri verifikator-vendt:

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

Den publiserte append-only-autoriteten er den kumulative Merkle-roten + dens anker (§16.5–16.6), aldri den rå rekkefølgen operatøren tilfeldigvis serverer leaf-er i: kjeden rekalkulerer for enhver rekkefølge servert, så bare den forankrede roten fester kanonisk posisjon.

16.5 Kumulativt Merkle-tre og batching

Det er ett stadig voksende RFC 6962-tre over alle leaf-er i seq-rekkefølge — ikke isolerte per-batch-trær. (En carry-leaf-kjedet per-batch-konstruksjon ble avvist: den er ikke en ekte prefiks-relasjon, så dens «konsistensbevis» er usunne.) Det kumulative treet gir genuine RFC 9162-konsistensbevis og lar et enkelt nylig anker bevise inkludering for enhver eldre qub.

LogDO-Durable-Object-en er den eneste skriveren (blockConcurrencyWhile, og speiler QuotaDO / EntitlementDO) — å føye til en delt logg er les-modifiser-skriv på delt tilstand og MUST derfor gå gjennom en DO, aldri KV. Den cacher treets høyre-kant-frontier (O(log n) hasher) slik at å lukke en batch er O(batch). En batch er settet av leaf-er forankret sammen; dens utløsere er konfigurerbare, ikke protokoll-frosne: et tree_size-framskritt på minst LOG_BATCH_MAX_LEAVES (standard 4096), eller alder som når ankerkadensen, eller en tvungen flush når en betalt T3-forsegling lander. root_i er den kumulative Merkle Tree Hash over leaf-er 0 .. tree_size_i.

16.6 Signert tre-hode via Arweave-anker

Arweave-anker-transaksjonen er Signert tre-hode (Signed Tree Head) og erstatter en operatørsignatur for selve tre-hodet: det daglige ankeret trenger ingen qub-nøkkel fordi Arweave-tx-ens owner er signaturen. Voll-tesen holder — det uforanderlige substratet, ikke en qub-holdt hemmelighet, er bærende for den forankrede roten.

Det er nøyaktig én varm qub-signeringsnøkkel i designet, og den er festet: per-seal kvitteringsnøkkelen (§16.10). Dens offentlige nøkkel er forpliktet i LogProfile (distribuert med verifikatoren) og kryss-signert av anchor_owner, slik at en verifikator validerer en kvittering mot den samme festede roten som ankeret. Dette er avklaringen av §16.15 Q2 — en ufestet, operatør-roterbar kvitteringsnøkkel ville være benektbar (operatøren kunne benekte at nøkkelen var deres), noe som ville oppheve kvitteringens ansvarlighetsverdi mot den operatør-nivå-motstanderen kvitteringen eksisterer for å avskrekke. Altså: qub holder ingen ufestet logg-signeringsnøkkel; kvitteringsnøkkelen er festet og anchor_owner-kryss-signert.

SignedTreeHead er kanonisk CBOR (nøkler etter kodet lengde): size:u64, root:bstr[32], batch:u64, prev:bstr[32] (forrige sth_hash; genesis = 32 null-byte), log_id:bstr[32], first_seq:u64, anchored_at:i64. Dens hash er sth_hash = SHA3-256(0x03 || canonical_cbor(SignedTreeHead)).

Festet tillitsrot. log_id = SHA3-256("QUB_TLOG_V1" || anchor_owner_address). En samsvarende verifikator MUST kreve anchor_tx.owner == LogProfile.anchor_owner, der anchor_owner (og kvitteringsnøkkelens offentlige nøkkel) er bakt inn i qub_core som LogProfile — ved siden av quicknet-konstantene som allerede er i DrandTimelockProvider::quicknet() — og distribuert med verifikator-binæren. Verifikatoren MUST også verifisere Arweave-tx-ens data → tx_id-binding lokalt framfor å stole på et gateway-/raw/-svar. Dette lukker uærlige-lommebok-ekvivokasjonshullet: «forankret på Arweave» er meningsløst inntil verifikatoren fester hvilken lommebok.

Rotasjon er en §15-styrings-utvidelse, ikke en gjenbruk (avklart — §16.15 Q3). §15.2-profilflaten oppregner for øyeblikket bare sig_algs / drand-kjeder / omslagsversjoner / content types, og §15.3-utløserne lister ingen av disse — LogProfile / anchor_owner er ikke ennå i §15-flaten. Rotasjonsstyring må derfor bygges: §15.3 utvides (nedenfor) til å legge til LogProfile-utløseren, og en rotasjon er en signert LogProfile-bump levert i en verifikatoroppdatering. En planlagt rotasjon bærer en utgående → inngående kryss-signatur; en kompromiss-drevet rotasjon kan ikke (den utgående nøkkelen er ikke-tiltrodd/utilgjengelig nettopp da) og faller tilbake til den §15-styrte bumpen, med forrige-anker-fork-sjekken (nedenfor) som begrenser skaden i mellomtiden.

Ekvivokasjonsvindu (førsteklasses tillitsparameter). En leaf er ekvivokasjons-resistent først når dens dekkende anker er Arweave-bekreftet. Vinduet er received_at → anker-bekreftelse (≤ kadens + Arweave-finalitet). Innenfor det er de eneste garantiene den festede forseglingskvitteringen (§16.10) og qubs operasjonelle integritet. Tre ansvarlighets-artefakter gjør dette ærlig framfor håndviftet (vitnemodellen er §16.15 Q2s avklaring):

  1. Festet signert forseglingskvittering — SCT-analogen returnert i opplastingssvaret (§16.10), signert av den festede, anchor_owner-kryss-signerte kvitteringsnøkkelen. En leaf droppet før sitt anker etterlater offeret en ikke-benektbar kvittering å publisere, og lukker det stille-utelatelses-hullet.
  2. Publisert monitor-metodikk + forrige-kjede-gjennomgang — anker-prev-kjeden gås hode→genesis; en fork (to ankere på én size med ulik root, eller en brutt prev) er publiserbart bevis på misligheter. Ekvivokasjonsdeteksjon er en uttalt operasjonell forpliktelse, ikke en stille antagelse.
  3. Doble selvpubliserte hoder — hvert nytt hode {sth_hash, tree_size} postes til et dedikert qub-eid offentlig, append-only GitHub-repositorium (den bærende tukle-bevisbare selvpubliseringsbenet), med et sosialt innlegg som beste-anstrengelse-bekreftelse alene. En mislykket posting MUST varsle (ikke feile stille).

Ærlighetsgrense (bindende betingelse). Fordi qub kontrollerer begge postingsflatene, er dette selvpublisert, ikke uavhengig bevitnet. Ingen produkt-, markedsførings- eller juridisk flate kan hevde at loggen er «uavhengig bevitnet»; den tillatte påstanden er at ekvivokasjon er detekterbar og etterlater en ikke-benektbar kvittering. Et ekte uavhengig tredjeparts-vitne er utsatt til en framtidig §15-styringsbump.

received_at er operatør-påstått og ingen påstand kan lene seg på den — den overflatebehandles aldri som bevis eller som tvistebekreftelse på noen produkt- / juridisk / API- / bevis-gjengivelsesflate. Arweave-anker-blokktiden T er det eneste tillitsløse tidsstempelet (en øvre grense på «logget innen»). Enhver monitor-fornuftssjekk på received_at MUST sammenlignes mot T, ikke mot det operatør-kontrollerte anchored_at-STH-feltet; en slik sjekk er en vakt mot en ærlig operatørs klokkefeil alene, ikke en ansvarlighetskontroll mot en ondsinnet operatør (§16.15 Q5).

16.7 Anker-transaksjonsformat og kadens

AnchorBundle er den kanonisk-CBOR Arweave-transaksjonskroppen, skrevet via §16.8-bundleren: ver:u8, sth:bstr (kanoniske SignedTreeHead-byte), prev_anchor:bstr (forrige anker-tx-id rå byte; utelatt ved genesis), chain_hash:tstr (drand-kjeden i kraft — quicknet), og batchens leaf-CBOR-strøm i seq-rekkefølge slik at ankeret er selvstendig: en monitor re-utleder root fra kroppen med null qub-avhengighet. (Hvis leaf-strømmen blir stor ved høyt volum, kan en framtidig revisjon forplikte bare et leaf-område ved referanse; notert, ikke vedtatt i v1.)

Arweave-tagger er med vilje enumererbare — loggen er ment å bli funnet, i motsetning til private qub-er: App-Name: qub-tlog, Anchor-Format: 1, Log-Id: <hex>, Batch: <n>, Tree-Size: <n>, Root: <hex>, Prev-Anchor: <tx>, Content-Type: application/cbor. Tagger er ikke-tiltrodde hint; CBOR-kroppen er den eneste autoriteten.

Kadens: daglig som standard, revurdert med volum (størrelsesutløseren forkorter automatisk den effektive kadensen under last); en betalt T3-forsegling tvinger fram et anker slik at betalende kunder aldri venter en dag. Ankerlommeboken er dedikert og lav-hastighet, atskilt fra opplastingslommeboken — den MUST være sin egen JWK (en distinkt nøkkel, ikke en logisk rolle på opplastingslommeboken) slik at en opplastingslommebok-kompromittering ikke kan forfalske ankere — med et hardt per-dag anker-transaksjonsbudsjett (den degraderte ARWEAVE_DAILY_CEILING). Forvaringsholdningen er uttalt rett ut: en smal-omfangs varm nøkkel med en stram strømbryter og lav balanse, ikke «kald» — en lommebok som auto-signerer daglig kan ikke være kald, og spesifikasjonen later ikke som noe annet.

16.8 ANS-104-bundler

En egenutviklet ANS-104 DataItem-enkoder og deep-hash-signerer, omtrent 300 linjer, kun Web Crypto, null npm-avhengigheter (begge Turbo-SDK-ene feiler npm ci --ignore-scripts forsyningskjede-vakten). 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

Signering er Arweave deepHash — en rekursiv SHA-384-digest (Arweaves wire-krav, crypto.subtle.digest("SHA-384")) over ["dataitem", "1", sig_type, owner, target, anchor, encoded_tags, data] — deretter RSA-PSS over deep-hashen med lommebok-JWK-en via crypto.subtle; id = base64url(SHA-256(signature)). SHA-384 her er karantenesatt som en kun-Arweave-wire-primitiv, aldri en qub-tillitsprimitiv (§15 nedtegner gjerdet; qub-tillitshashing er SHA3-256 gjennomgående).

Én kodevei betjener tre konsumenter: betalt T3 per-qub-permanens, Arweave-utilgjengelighets-reserveløsningen (kø DataItem-en, returner R2-først-bekreftelsen uansett — dette lukker den nåværende ARWEAVE_UNAVAILABLE 503-blindgaten), og skriving av AnchorBundle. Signaturskjema (avklart — §16.15 Q8): v1 signerer med RSA-PSS (signaturtype 1) og gjenbruker den eksisterende Arweave-lommebok-JWK-mekanismen (null ny langlivet nøkkelforvaring, og betjener «én færre hemmelighet»-tesen); Ed25519 er utsatt til §15 PQ-migrasjonsveien.

Den håndlagde deep-hashen er den høyeste-risiko, laveste-naturlige-dekning-koden i W5, så dens gating er ikke-forhandlingsbar (§16.15 Q8):

  1. Tverr-språk-fixturen tlog_v1.json (Rust + TS, §14.5 wrapper_v1.json-mønsteret) dekker deep-hash, DataItem-byte + id, leaf-hasher, en 5-leaf-rot + audit-path, en STH-hash, et inkluderingsbevis og et konsistensbevis — i både signer- og verifiser-retningene (verifiser-retningen betyr noe fordi §16.6s lokale tx → tx_id-sjekk trekker deep-hashen inn i hver frittstående verifikator, ikke bare skriveren).
  2. En engangs interop-rundtur gjennom en referanse-ANS-104-bundler, konsumert som kun statiske testdata — aldri en npm-kjøretidsavhengighet (kun-Web-Crypto / ingen-install-skript-holdningen står).
  3. Deep-hash + RSA-PSS-veien må rundtur gjennom de samme crypto.subtle-primitivene produksjon bruker, slik at den egenutviklede enkoderen er byte-kompatibel.
  4. En løpende post-bundle-aksept-monitor bekrefter at hvert anker / reserve-DataItem faktisk oppnår Arweave-aksept, med en alarm + strømbryter — fordi deep-hashen også betjener Arweave-utilgjengelighets-reservekøen, slik at en stille regresjon ville fylle den køen med nettverk-avviste elementer under nettopp det avbruddet den eksisterer for å dekke.

16.9 Inkluderings- og konsistensbevis

Begge er RFC 9162, SHA3-256, servert som kanonisk CBOR.

InclusionProofGET /api/v1/qub/:tx_id/proof: ver:u8, leaf:bstr (den eksakte leaf-CBOR-en — verifikatoren rekalkulerer leaf_hash selv og stoler aldri på en tilført hash), 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 }.

ConsistencyProofGET /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. En enkelt utvetydig nøkkelliste, festet av testvektor.

Frittstående verifikasjon (ingen qub-server, utvider §11):

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).

Bevis-betjenende lagring MUST være koordinat-nøklet (avklart — §16.15 Q7, blokkerende forutsetning). Kald-leaf-bevisgenerering er korrekthetsnøytral bare hvis R2-audit-materialet er et persistent Merkle-node-lager nøklet etter absolutt tre-koordinat (level, index) — ikke per-batch-node-deltaer. Med et koordinat-nøklet lager er enhver (leaf i, size N)-audit-path et sett av O(log N) direkte R2-GET-er med ingen rekalkulering over batch-grenser; med et batch-nøklet lager er den ikke det, som er lagringslayout-gapet denne avklaringen lukker. Leaf-kroppene er likeledes innholdsadresserbare etter seq. En W5-testvektor MUST bevise en genesis-era kald leaf mot en mye-senere rot ved bruk av kun R2 + Arweave med LogDO-lagringen tørket, slik at gjenvinnings-sikkerhets-påstanden i §16.13 er underbygd framfor påstått. De O(log N) sekvensielle R2-GET-ene hører kun hjemme på det asynkrone bevis-endepunktet — aldri på forseglings-hot-veien (§16.10) eller en per-tick-cron.

16.10 R2-først bekreftelses-rekkefølge

POST /api/v1/upload-sekvensen blir:

  1. Front-halvdel-porter (auth, validering, idempotens-shard-nøkkel) — uendret.
  2. Synkront await QUB_CACHE.put(qub-cache/<tx_id>, wrappedBytes) — holdbarhetsgulvet; lukker også W1s precache-race (tidligere en ctx.waitUntil etter Arweave-submit-en).
  3. Synkront await LogDO.append(leaf) — én in-colo DO-RPC; den eneste skriveren tildeler seq, utvider oppføringskjeden, og oppdaterer frontier-en. (RMW på delt tilstand → DO, aldri KV.) append-RPC-en gjør kun det — det O(batch) Merkle-batch-lukke-arbeidet kjører utenfor denne RPC-en på LogDO-alarmen, ellers spiker append-p95 hver LOG_BATCH_MAX_LEAVES-te forsegling.
  4. Returner bekreftelsen nå — med forseglingskvitteringen (signert av den festede kvitteringsnøkkelen, §16.6) og { tx_id, log_seq, anchor_status: "pending" }. Den fler-sekunders Arweave-fan-out-en er fjernet fra den kritiske veien.
  5. Én ctx.waitUntil køer det utsatte arbeidet: per-qub Arweave-submit-en (nå beste-anstrengelse / betalt; ved feil rutes den til bundler-reservekøen framfor å 503-e brukeren) pluss de eksisterende provisoriske-meta-skrivingene. Batch-lukking og forankring kjører uavhengig fra LogDO-alarmen og den daglige anker-cron-en. Ingen ctx.waitUntil inne i en løkke; den eksisterende idempotens-shard-nøkkelen er bevart.

Latensbudsjett (avklart — §16.15 Q7). Det < 300 ms p95-målet er en målt lanseringsport, ikke en antagelse. Den ærlige kritiske veien er front-halvdel-KV-lesingene + én R2-PUT + to serialiserte Durable Objects — den eksisterende QuotaDO-forseglingskvote-debiteringen og den nye LogDO-append-en — så budsjettet må ta høyde for to in-colo DO-rundturer, ikke én. Lever en LogDO-latensalarm som speiler QuotaDOs, og behandle en p95-regresjon som en utgivelsesblokker.

16.11 Tillitsmodell — den presise påstanden, omfangsbestemt etter leaf-art

For kind=0x01 (attestert): «Dette innholdet — kropp som samsvarer med body_hash, identifisert av qub_id — ble forpliktet til qubs append-only-logg på posisjon seq og eksisterte ikke senere enn Arweave-blokktid T; det var kryptografisk uleselig fram til drand-runde R = unlock_round(unlock_at) Dette er den fulle {tlock-runde-binding + Merkle-inkludering + forankret rot}-trippelen.

For kind=0x02 (påstått, standarden): «En ugjennomsiktig ciphertext med innholdsadresse chash, som hevder qub_id og unlock_at, ble forpliktet til append-only-loggen på posisjon seq og eksisterte ikke senere enn Arweave-blokktid T Runde- og kropp-leddene tilføres av den eksisterende §11 .qub-bundle-verifikasjonen (qub_core::unlock), ikke av loggen; det loggen legger til over en bar per-qub-transaksjon er tukle-bevisbar rekkefølge, et tillitsløst øvre-grense-forpliktelsestidspunkt, og ekvivokasjonsresistens.

Begge påstandene utelukker, per §11: forfatterskap uten sig_alg ≥ 0x01, intensjon, og sub-anker-granularitets-tidspunkt. Ingen av dem lar noen påstand lene seg på received_at.

Påstandstak (bindende lanseringsbetingelse — avklart §16.15 Q1). For gratis / standard (kind=0x02) qub-er er den omfangsbestemte kind=0x02-påstanden over taket på hva noen produkt-, markedsførings-, betingelses- eller bevis-gjengivelsesflate kan hevde. Ingen flate kan oppgi eller antyde at loggen beviser innholdet eller opplåsingsrunden til en standard-qub — loggen beviser rekkefølge + et tillitsløst øvre-grense-forpliktelsestidspunkt av en ugjennomsiktig ciphertext. Innholds- og runde-bevis kommer utelukkende fra den eksisterende §11 .qub-bundle-verifikasjonen, som er logg-uavhengig. Dette er en hard lanseringsblokker på tekst, ikke en stilistisk preferanse; det er avklaringen som holder den byte-blinde standardveien ærlig.

16.12 Versjonering og W3-koordinering

Det er ingen SealedQub-wire-bump og derfor ingen protokollversjons-bump (§12.1): loggen er en sidecar som forplikter seg til eksisterende felt og byte, så den entrer ikke §12.2-protokollversjonshistorikken. W3s valgfrie drand_chain_version er urørt og forblir det eneste valgfrie SealedQub-feltet. Loggen introduserer i stedet sine egne uavhengige versjonsrom — LOG_VERSION_1, ANCHOR_FORMAT_1, InclusionProof.ver — og speiler §12.4-omslagsversjons-uavhengigheten (omslaget bærer en versjonsbyte uavhengig av protokollversjonen, og logg-versjonene følger samme separasjon).

Bevis-levering hentes som standard, med en valgfri følge-med. Et bevis kan ikke eksistere ved forseglingstidspunkt (ankeret er ikke skrevet ennå), så forseglings-tidspunkt-.qub-bundle-en forblir bevis-fri. W7s verifikator henter GET …/proof én gang, eller i fullt-frakoblet modus rekonstruerer beviset fra den offentlige AnchorBundle via en Arweave-spørring på Log-Id. .qub-bundle-en (W7) reserverer et valgfritt inclusion_proof-medlem — fraværende ved forsegling, fylt av en post-anker re-eksport for kald arkivering — og følger samme «valgfri, utelatt som standard, additiv»-mønster som W3s drand_chain_version.

16.13 Oppbevaring

Oppbevaringsvinduer for LogDO-åpen-halen, R2-bevis-betjenings-substratet, anker-strømbryter-tellerne og bundler-reservekøen er spesifisert i docs/DATA-RETENTION.md. Prinsipp: loggens varme per-oppførings-lagring (LogDO) er gjenvinnbar post-anker; dens audit-materiale — det koordinat-nøklede (level, index)-Merkle-node-lageret + de seq-adresserte leaf-kroppene (§16.9) + Arweave-ankerne — er permanent. Å gjenvinne en kald leaf fra DO-en ugyldiggjør aldri et utstedt bevis, fordi et bevis løses mot det permanente R2-node-lageret og Arweave-ankeret, ikke DO-en (og den §16.9-tørkede-DO-testvektoren beviser det).

16.14 Testvektorer

W5 leverer tverr-språk-fixturen tlog_v1.json (§16.8) pluss utarbeidede vektorer: en kind=0x01- og en kind=0x02-leaf → leaf_hash; den 5-leaf kumulative roten; ett inkluderingsbevis; ett konsistensbevis; en AnchorBundle; og en DataItem-id. Disse bor ved siden av §14.5-ytre-omslag-vektorene og utøves av både Rust- (qub-core) og TypeScript- (Worker) implementasjonene.

16.15 Gjennomgangsbeslutninger (W5 — avklart)

Den eksterne W5-gjennomgangen (en adversariell designgjennomgang + eier-godkjenning) er fullført. Hver beslutning nedenfor er avgjort og reflektert i §16-teksten over; de bindende lanseringsbetingelsene er gjentatt på slutten. Implementasjon kan fortsette under dem.

  1. Standardvei-(kind=0x02)-leaf-ærlighet — AVKLART. Lever to-leaf-art-splittet som spesifisert: kind=0x02 forplikter verken body_hash eller drand_round. Ingen *_body_hash-felt på den byte-blinde veien (det ville være det mest lesbare falske «verifisert»-signalet for integratorer, og er en bekvemmelighet §11 allerede tilbyr fra bundle-en). Krev ikke server-seal for logg-attesterte qub-er (det ville tvinge klartekst gjennom Worker-en og ødelegge krypto-makulerings-vollen). Enhver selvbeskrivende snarvei hører hjemme i .qub-bundle-en / bevis-konvolutten som et verifikator-rekalkulert felt, aldri et leaf-felt. Eier-bekreftet påstandstak: §16.11.
  2. Ekvivokasjons- / utelatelses-ansvarlighet — AVKLART. Forseglingskvitteringsnøkkelen er festet i LogProfile + anchor_owner-kryss-signert (lukker den tidligere «ingen signeringsnøkkel»-motsigelsen; §16.6). Lanserings-vitnemodell: festet kvittering + monitor-metodikk + forrige-kjede-gjennomgang + doble selvpubliserte hoder (qub-eid offentlig GitHub-repo, sosialt beste-anstrengelse), markedsført som detekterbar + kvittert, aldri uavhengig bevitnet. Et ekte tredjeparts-vitne er utsatt til en §15-styringsbump.
  3. Festet anker-owner-tillitsrot + rotasjon — AVKLART. Adopter LogProfile-festingen (§16.6); verifikatoren sjekker anchor_tx.owner == anchor_owner og verifiserer tx-ens data → tx_id-binding lokalt. Rotasjonsstyring er en §15-utvidelse å bygge (§15.3-utløser lagt til), ikke en gjenbruk; planlagte rotasjoner kryss-signerer, kompromiss-drevne rotasjoner faller tilbake til §15-bumpen med fork-sjekken som begrenser skaden.
  4. Privat-qub-leaf-blinding — AVKLART. Behold blinding for private qub-er (ref = SHA3-256(qub_id ‖ log_blind_secret)), rå qub_id for offentlige qub-er (allerede §16.2.1), chash som den frittstående bindingen. log_blind_secret er en korrelasjon/Sybil-grad-hemmelighet, kun roter-forover (§16.2.1).
  5. received_at — AVKLART. Behold den i leaf-en, forpliktet men eksplisitt ikke-bevisbærende; aldri overflatebehandlet som bevis eller tvistebekreftelse på noen flate. Enhver monitor-fornuftssjekk sammenligner mot Arweave-blokktiden T, ikke det operatør-kontrollerte anchored_at (§16.6).
  6. Gratis-nivå bevisbar-tidspunkt — AVKLART (eier-godkjenning). Holdbarhet regredierer ikke; bare det bevisbare øvre-grense-forpliktelsestidspunktet blir grovere til ankerets blokktid. Gratis-nivå-tekst bruker ingen numerisk SLA («…lagt til ved neste logg-anker, typisk daglig»); eksakt-time-bevis er en betalt T3-egenskap, opplyst på nivåsammenligningsflaten + i betingelsene (§16.1).
  7. Kumulativt tre på Workers — AVKLART. Enkelt kumulativt RFC 9162-tre + frontier-cachet enkelt-skriver-LogDO (komfortabel margin mot ~1k skrivinger/sek-DO-taket; utsett Merkle-of-shard-roots-sharding til nær det). Blokkerende forutsetning: koordinat-nøklet (level, index)-R2-node-lager + den tørkede-DO-kald-leaf-testvektoren (§16.9); < 300 ms er en målt lanseringsport over to serialiserte DO-er (§16.10).
  8. ANS-104-signaturskjema + deep-hash — AVKLART. RSA-PSS (sig-type 1, og gjenbruker den dedikerte anker-lommebok-JWK-en); Ed25519 utsatt til §15 PQ-veien. Den håndlagde SHA-384-deep-hashen er gatet på den begge-retninger tverr-impl-fixturen, en kun-statisk referanse-bundler-interop-sjekk, den delte-crypto.subtle-rundturen, og post-bundle-Arweave-aksept-monitoren (§16.8).

Bindende lanseringsbetingelser (før videre til implementasjon + produkt-/juridisk gjennomgang):