qub Protokollspesifikasjon
qub er en protokoll for kryptografiske temporale forpliktelser: et system for å forsegle ord til en fremtidig dato og senere verifisere nøyaktig hva som ble forseglet, hvilken drand-runde som styrte utgivelsen, og—når en lagringstransaksjon eller et bevis fra en transparenslogg er tilgjengelig—en uavhengig tidsstemplet øvre grense for når kryptoteksten ble forpliktet.
Tre primitive gjør det fungere. drand er en desentralisert tilfeldighetsbake—avsløringsdatoen håndheves kryptografisk i stedet for av qubs velvilje. Holdbar lagring bevarer anerkjente forseglede bytes mens de nåværende publiseringsbanene planlegger individuelle permanent-lagringstransaksjoner; vellykkede generelle opplastingslogg-tilføyelser kan i tillegg bli med i batchvise, forankrede forpliktelser. ML-DSA-65 er en post-kvantum digital signatur—når forfatterskap er aktivert, er quben knyttet til et nøkkelpar hvis hemmelighet aldri forlater forfatterens enhet.
Sammen utgjør disse primitive elementene en uttalelse som er tidslåst og manipulasjonssikker, valgfritt attribuerbar, og uavhengig tidsstempelbar—en kvittering hvis verdi øker etter hvert som verdens evne til å fabrikere fortiden forbedres.
Resten av dette dokumentet er den normative spesifikasjonen som kreves for interoperable implementasjoner.
qub-protokollspesifikasjon
| Felt | Verdi |
|---|---|
| Dokumentutgivelse | 1.0.0 (protocol-v1.0.0) |
| Trådløs protokoll | 0x01 |
| Ytre omslag | 0x01 |
| Ikrafttredelsesdato | 23.09.2026 |
| Status | Nåværende |
| Gjennomgått | 23.09.2026 |
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, // 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 (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>, // 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
}
Grunnlinje (usignert tekst qub): version = 0x01, content_type = 0x01, sig_alg = 0x00; signatur- og medsignaturfelt mangler. Andre valgfrie metadatafelt kan være til stede.
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 ved bruk av kanonisk CBOR (§3). Dette er den indre kabelartefakten: offentlig levering lagrer disse bytene som de er, mens privat levering pakker dem inn i OuterWrapper før lagring (§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 (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>, // 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. 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 (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 (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 (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].
outcome_at koding: En forhåndsutgivelsesimplementeringsrevisjon utvidet preimagen fra 92 til 100 byte for å folde det valgfrie outcome_at felt inn i bindingen. Fraværende outcome_at kodes som 8 null-byter; protokollvalidererne avviser outcome_at <= 0 overalt slik at denne vakten ikke kan kollidere med en legitim verdi. Se §3.2 (wire-format) og i treet tasks/verdict-uplift-plan.md for dommermekanismen som motiverer dette feltet.
drand_round koding: En senere pre-release implementeringsrevisjon utvidet preimagen fra 100 til 108 byte å brette drand_round (mål-drand-runden, §4.3) inn i bindingen, og økte domeneseparatoren til QUB_ID_V2. Dette binder timelock-runden til qub-identiteten: en gateway kan ikke binde krypteringsteksten til en annen (f.eks. allerede passert) runde enn den som vises unlock_at innebærer. Opphevingsprosedyren (§8) verifiserer i tillegg at runden som er innebygd i tlock-krypteringsstanzaen samsvarer unlock_round(unlock_at), så den viste opplåsningstiden er beviselig runden som åpner dekryptering.
Egenskaper:
- Endring av et hvilket som helst felt bundet av prebildet—
version,content_type,created_at,unlock_at,outcome_at,drand_round, den råbodybytes (gjennombody_hash), ellertitle(gjennomtitle_hash)—produserer en annenqub_id. - Qub_id beregnes før kryptering. Både QubEnvelope og SealedQub bærer den samme qub_id. Betrakteren verifiserer at de samsvarer etter dekryptering.
qub_idavhenger ikke avsender_label,reply_to, signaturbiter eller offentlige nøkler for signering. Under den nåværende V2-signaturkonstruksjonen, derimot,sender_labelogreply_toer autentisert direkte avsender_label_hashogreply_to_or_zero(§9.3) når signaturer er til stede.- Endre den forseglede Qub
title(med alt annet fikset) endringerqub_idviatitle_hash. En gateway kan derfor ikke bytte den ukrypterte tittelen som vises på nedtellingen uten å ugyldiggjøre qub-identiteten. - Endre den forseglede Qub
outcome_at(med alt annet fikset) endringerqub_idvia forbildet. En gateway kan ikke bytte ut den forhåndsavslørte avgjørelsesdatoen som vises på nedtellingen uten å ugyldiggjøre qub-identiteten. - Endring
drand_round(med alt annet fikset) endringerqub_idvia forbildet. En gateway kan ikke binde timelock-krypteringen til en annen runde uten å ugyldiggjøre qub-identiteten; kombinert med §8 unlock-tid stanza-runde-sjekken, visesunlock_ater runden som faktisk styrer dekryptering.
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 Lås-opp-Runde-kartlegging
drand_round = floor((unlock_at - chain_genesis_time) / chain_period_seconds) + 1
| Parameter | Kilde | Eksempel |
|---|---|---|
unlock_at |
Brukervalgt Unix-sekunder UTC | 1735689600 (2025-01-01 00:00:00 UTC) |
chain_genesis_time |
drand-kjedeinfo (genesis_time) |
1595431050 |
chain_period_seconds |
drand-kjedeinfo (period) |
30 |
Dette er referanse-tlock-kartleggingen (drand's CurrentRound). drand publiserer runde N at chain_genesis_time + (N - 1) * chain_period_seconds, så formelen velger rund strøm ved unlock_at — runden hvis signatur er den første en besøkende legger merke til unlock_at kan bruke.
Justeringsegenskap (tilfellet som er viktig i praksis): når (unlock_at - chain_genesis_time) er nøyaktig delbar med chain_period_seconds, det valgte rundens signatur blir publisert akkurat klokken unlock_at, aldri før det. Dette gjelder alltid for referanseutrullingen: quicknet sin starttid (1692803367) er delelig på sin 3-sekunders periode, og referanseappene låser opp pinner til hele minutter. For en ikke-justert unlock_at, den valgte rundens signatur publiseres strengt mindre enn ett period før unlock_at — den tidsmessige presisjonen til forpliktelsen er én beacon-periode.
Forhåndsutgivelseskartlegging av eldre versjon og toleranse på opplåsingsside: den opprinnelige kartleggingen var ceil((unlock_at - chain_genesis_time) / chain_period_seconds), som—for det periodetilpassede tilfellet ovenfor—valgte den runde publiserte én hele periode før unlock_at, noe som gjør chifferteksten mulig å dekryptere tidlig med nøyaktig én periode. De to avbildningene skiller seg fra hverandre med nøyaktig +1 når deltaen deler perioden, og ellers er enige. Fordi drand_round er foldet inn i det uforanderlige qub_id preimage (§4.1), artefakter forseglet under den gamle kartleggingen kan ikke gjenskapes; verifikatorer som utfører §8 trinn 6a runde krysskontroll MÅ derfor akseptere en lagret drand_round lik enten den utledede runden eller den avledede runde minus én (og MÅ kreve at tlock-stansrunden er nøyaktig lik den lagrede runden). Toleransen utvider den tidligste styringssignaturen med maksimalt én periode. Pact-stagingtjenesten anvender samme toleranse når den gjenavleder en iscenesatt pacts qub_id (på scenen og ved medunderskrift): hvis den nåværende kartleggingens runde ikke gjenskaper det forpliktede qub_id og deltaet deler perioden, det prøver på nytt med runden minus én, og det forsegler den endelige pakten til hvilken som helst runde qub_id faktisk binder—aldri blindt til den nyberegnede runden, noe som ville gjøre artefaktet permanent uframbringelig.
Validering: unlock_at MÅ være i fremtiden på segltid. unlock_at MÅ IKKE være mer enn 10 år fra created_at (for å begrense risikoen for avhengighet av langtidshorisont drand; UI-et BØR advare om opplåsingsdatoer som går 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 | Oppslukt av |
|---|---|---|---|
SealedQubCbor |
Kanonisk CBOR av SealedQub | serialize_sealed_qub() |
Indre tråd-artifakt; lagret ubeskyttet for offentlig levering eller pakket for privat levering, deretter hentet av seeren |
QubEnvelopeCbor |
Kanonisk CBOR av QubEnvelope | serialize_qub_envelope() |
tlock krypter input, tlock dekrypter output |
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 bytes), value: String (≤ 2,000 bytes) } // NFC
PartyIdentifier{ label: String (≤ 100 bytes), contact: Option<String (≤ 320 bytes)> }
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; "what changed, what did you learn"
evidence_url: Option<String>, // ≤ 2,048 bytes; HTTPS only; absent key when omitted
}
Kanonisk CBOR-nøkkelrekkefølge:
"outcome" (8 encoded bytes)
"reflection" (11 encoded bytes) ← only if present
"evidence_url" (13 encoded bytes) ← only if present
"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:
- Bare HTTPS. Strengen MUST starte med bytesekvensen
https://. Enhver annen skjema —http,ftp,javascript,data,file, osv. — avvises. - Lengdetak. ≤ 2 048 byte (praktisk grense for nettlesere-URL).
- NFC + sjekk på fiendtlige kodepunkter. Samme regel som
titleogreflection— bidi-override / nullbredde / tag-blokk / BOM / C0 / C1 kodepunkter avvises. Definisjonen samsvarer med Rustcrate::handle::contains_hostile_text_codepointog TSworkers/api/src/utils/unicode.ts::isHostileCodepoint(hold i lås). - Ingen mellomrom, ingen ASCII-kontrolltegn. Mellomrom / DEL / sub-
0x20-byte hvor som helst i URL-en avvises — lukker injeksjonsvektoren\n/\tsom bidi-regelen ikke dekker. - 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.
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.
Lagringstagglag (utenfor bånd). Qub-opplastningstjenesten legger ved et bevisst lite sett med lagrings-transaksjonstagger sammen med den valgte opplastingsdataen. Content-Type=application/octet-stream er normativt påkrevd. Referansetjenesten legger i tillegg ved tre valgfrie koder når skaperen velger å vise dem: Intent (tillatelsesliste-validert komponere hensikt—announcement, thesis, prediction, letter, secret, commitment, proof, eller systemutstedt verdict), Author (skaperens §9.3 offentlig nøkkelfingeravtrykk som 64-tegns små bokstaver hex), og Parent-Tx-Id (forelder qubs lagringstransaksjons-ID for svarrekker, 43-tegns base64url).
Den Author tag er meld deg på per qub: referanseskapingsappen legger det bare ved når brukeren eksplisitt aktiverer offentlig attribusjon ved segltidspunktet. Når bryteren er av — standardinnstillingen — ingen Author taggen er skrevet og quben er unattributed på kjeden: ingenting i permanent lagring kobler opplastingen til en skaper sin håndtak, e-post eller andre quber. Når bryteren er på, Author fingeravtrykk løser seg til skaperens valgte @handle via §9.5-attestasjonskjeden. Svar-kjede-relasjoner og Intent er ikke-identifiserende. For privat levering krypterer den ytre innpakningen (§13) den gjenkjennelige indre SealedQub artefakt, så innhøsting av lagrede innpakninger og innhenting av offentlige drand-signaturer er fortsatt utilstrekkelig for å gjenopprette kroppen uten K; lagringsmerker forblir bevisst offentlig metadata.
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 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. 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 |
Ordning | Nøkkelstørrelse | Signaturstørrelse | Status |
|---|---|---|---|---|
0x00 |
Ingen signatur (usignert) | — | — | Aktiv |
0x01 |
ML-DSA-65 (FIPS 204) | 1 952 byte | 3 309 byte | Aktiv |
0x02 |
Ed25519 | 32 byte | 64 byte | Reservert konstant; ikke støttet i protokoll v1 |
Protocol-v1-seere MÅ avvise enhver verdi utenfor {0x00, 0x01}, inkludert
den reserverte 0x02 verdi. Reservasjon forhindrer utilsiktet gjenbruk; det er ikke
aktivering. Aktivering av det krever den styrte endringen som er beskrevet i §15.
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 med signatur | Hvordan |
|---|---|---|
version |
✓ | Direkte inndata til sig_input |
qub_id |
✓ | Direkte inndata |
body_hash |
✓ | Direkte inndata |
unlock_at |
✓ | Direkte inndata |
sender_label |
✓ | Direkte inndata via sender_label_hash (V2 preimage — den eneste aksepterte formen) |
reply_to |
✓ | Direkte inndata via reply_to_or_zero (V2 preimage — den eneste aksepterte formen) |
content_type |
✓ | Transitivt, via qub_id forbilde |
created_at |
✓ | Transitivt, via qub_id forbilde |
outcome_at |
✓ | Transitivt, via qub_id forbilde |
drand_round |
✓ | Transitivt, via qub_id forbilde |
body |
✓ | Transitivt, via body_hash = SHA3-256(body) |
author_pubkey |
— (implisitt) | Nøkkelen som bekreftet signaturen er forfatteren, ifølge definisjonen |
cosigner_pubkey / cosigner_signature |
— | Uavhengig signert over det samme sig_input (se §9.7) |
drand_chain_id, tlock_ciphertext, visibility |
— | Ytre SealedQub felt, ikke inne i konvolutten — dekkes av sine egne strukturelle invariabler (rund / kjede-konsistens) men ikke av forfattersignaturen. (drand_round er nå bundet transitivt via qub_id preimage — se ovenfor.) |
Hvorfor V2 er det eneste aksepterte preimaget.
- Under det avviklede V1-preimaget kunne en part med skrivetilgang til de lagrede bytene bytte
sender_label(«Alice» → «Mallory») eller re-foreldrereply_to— og re-kryptere etter runden — uten å invalidere forfattersignaturen, fordi ingen av feltene var i det signerte preimaget. V2 dekker begge, så enhver endring av ett av feltene vipper verifikasjonen til «mislyktes». Fordi verifikatorer nå kun aksepterer V2, er dette byttet lukket for enhver signatur: en signatur som ikke binder noen av feltene (dvs. kun verifiserer mot V1) avvises direkte i stedet for å bli nedgradert til. author_pubkeyinne i envelopen forblir det sanne identitetsankeret — lesere MUST utlede vist identitet fraauthor_pubkey(via §9.5-attesteringslaget) i stedet for å stole påsender_label.
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:
cosigner_pubkey: ML-DSA-65 offentlig nøkkel til medundertegneren (Part B).cosigner_signature: Signatur over sammesig_inputsom forfatteren (§9.3).
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:
- Medundertegneren signerer den identiske
sig_inputsom forfatteren — begge parter forplikter seg til sammequb_id,body_hashogunlock_at(og, under V2, sammesender_label_hashogreply_to_or_zero). - For at motparten skal kunne rekonstruere V2-preimaget uten tilgang til de rå envelope-bytene, håndhever iscenesettelsestjenesten på iscenesettelsestidspunktet at en pakt-envelopes
sender_labeler likpact_terms.party_a.labelog atreply_toer fraværende. Begge holder for enhver referanseklient-pakt; envelope-er som bryter dette avvises ved iscenesettelse. qub_id-utledning (§4.1) inkluderer IKKE medsignatur-felt. Å legge til en medundertegner til en eksisterende envelope endrer ikkequb_id.- En pakt kan være kun forfattersignert (énsidig forpliktelse), kun medundertegnet (uvanlig) eller begge (fullt bilateralt bevis).
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
- Overskrifter:
#til####(ingen#####eller######) - Utheving: fet (
**), kursiv (*), gjennomstreking (~~) - Lister: ordnede (
1.) og uordnede (-,*) - Sitatblokker (
>) - Kode: inline-spenn (```) og inngjerdede blokker (`````)
- Horisontale linjer (
---) - Linjeskift (to mellomrom på slutten eller blank linje)
- Avsnitt
10.2 Forbudte elementer
| Element | Håndtering |
|---|---|
Rå HTML (<div>, <script>, osv.) |
Fjernes helt. Ingen HTML slipper gjennom. |
Bilder () |
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:
- Parse Markdown med
pulldown-cmark(eller tilsvarende). - Gå gjennom AST-en og dropp enhver node som ikke er på allowlisten (§10.1).
- For lenkenoder: send ut URL-en som synlig tekst, ikke som et klikkbart
<a>-element. - 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. - Gjengi fra den typede IR-en til mål-visningslaget (f.eks. reaktive visningskomponenter, DOM-noder). Ingen HTML-strengkonkatenering eller
innerHTMLpå 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
- Maksimal gjengivelsesdybde for overskrifter:
####(H4).#####og dypere gjengis som fet tekst. - Ingen grense for antall avsnitt (kroppsstørrelsesgrenser i §6 er begrensningen).
- Inngjerdede kodeblokker: ingen syntaksuthevning i MVP. Gjengis som monospace forhåndsformatert tekst.
11. Tredjepartsverifisering
Enhver tredjepart som holder de lagrede byteene (og K for en privat/innpakket qub) kan verifisere den kryptografiske gjenstanden uten qub-samarbeid. En uavhengig tidsstemplet eksistens krav krever i tillegg enten verifisert per-qub permanent-lagring inkludering eller en verifisert §16 gjennomsiktighetslogg-bevis.
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.
Hva verifiseringen beviser:
| Bevis inndata | Hva det fastsetter |
|---|---|
| Gyldig pakke / forseglet artefakt + drand-signatur | Det gjenopprettede liket samsvarer body_hash; metadataen bundet inn qub_id er intakt; chifferteksten er bundet til den erklærte drand-runden; og den runden har utløpt. Dette gjør ikke fastslå når chiffren ble laget. |
| Gyldig V2-forfatters/medsignatørs signatur | Innehaveren(e) av den tilsvarende hemmelige nøkkelen/de tilsvarende hemmelige nøklene autentiserte den signerte overflaten i §9.3. |
| Uavhengig verifisert per-qub lagringstransaksjon | Den eksakte lagrede chifferteksten eksisterte senest ved blokktidspunktet sitt. |
| Gyldig forankret transparensloggbevis | Blad-type-spesifikk påstand i §16.11, inkludert en øvre grense for forpliktelsestid fra ankerblokken. |
Hva verifisering IKKE beviser:
| Ikke-bevis | Hvorfor |
|---|---|
| Forfatterskap | Den sender_label er dekorativ. Uten sig_alg ≥ 0x01, hvem som helst kunne ha forseglet dette innholdet. |
| Intensjon | Artefakten beviser bytes og kryptografiske relasjoner, ikke hva skaperen subjektivt mente. |
Forhåndseksisterende forpliktelse fra .qub alene |
En skaper kan sette sammen en gyldig pakke etter at den bundne runden har utløpt. Den innebygde drand-signaturen beviser at runden har utløpt, ikke at kryptert tekst eksisterte før den. |
| Eksakt forsegling-knapp tid | Et lagrings- eller ankerblokk-tidspunkt er en uavhengig etterprøvbar øvre grense, og kan ligge bak brukerens lokale handling. sealed_at / received_at påstander er ikke-bevismessige. |
Den implementerte gjennomsiktighetsloggen (§16) utvider verifikasjonen på tvers av qubs med
plomberingssikker bestilling og uten tillit øvre-grense for forpliktelsestid (det
ankerblokk tid), avgrenset etter bladsort (§16.11). Det legger ikke til forfatterskap eller
hensikt; for standard byte-blinde opplastingsveien beviser det ikke i seg selv
body_hash eller drand_round, som fortsetter å komme fra artefaktsjekkene.
12. Versjonskontroll og utgivelsesstyring
Dokumentutgivelser, den indre ledningsprotokollen og den ytre innpakningen er separate versjonsrom. En dokument-basert avklaring gjør derfor ikke stille endre bytes, og en fremtidig overføring via ledning kan ikke late som en redaksjonell revisjon.
12.1 Dokumentutgivelsesversjon
Denne spesifikasjonen bruker semantiske dokumentslipp (MAJOR.MINOR.PATCH) og
en uforanderlig Git-tag kalt protocol-v<release>.
- LOMME: nøyaktighet eller redaksjonell korreksjon som ikke endrer samsvarende byte eller nødvendig oppførsel.
- MINDRE: bakoverkompatibel normativ tillegg, ny registeroppføring eller nytt uavhengig versjonert sidecar-format.
- MAJOR: inkompatibel normativ endring, inkludert en ny nødvendig ledningsfortolkning.
Utgivelsesstatus er en av Utkast (ennå ikke normativ), Nåværende (den eneste
anbefalt implementeringsmål), eller Erstattet (bevart for historiske
verifisering). Den uversjonerte /protocol ruten viser den nåværende utgivelsen;
utgivelsesmerket bevarer sin nøyaktige kilde og alle lokaliteter publisert med det.
Å endre status eller utgivelsesnummer krever oppdatering av denne tabellen og utgivelsen
historie i den samme gjennomgåtte endringen.
| Dokumentutgivelse | Ikrafttredelsesdato | Status | Trådløs protokoll | Innpakning | Kilde |
|---|---|---|---|---|---|
| 1.0.0 | 23.09.2026 | Nåværende | 0x01 |
0x01 |
protocol-v1.0.0 |
12.2 Protokollversjon
Den version felt (u8) i begge SealedQub og QubEnvelope identifiserer hovedprotokollversjonen.
- Seere MÅ avvise ukjente hovedversjoner med en tydelig feilmelding.
- Innenfor en kjent hovedversjon, MÅ dekodere forkaste ukjente kartnøkler (§3.1) — skjemaevolusjon skjer ved å introdusere en ny
version, ikke ved å legge til nøkler eksisterende dekodere ville hoppe over. (Tidligere revisjoner av denne spesifikasjonen tillot å tolerere ukjente valgfrie felt; den klausulen er trukket tilbake — det gjordeencode(decode(x))ikke-injektiv og åpnet en skjult-signert-innholdsvektor på pakt-payloads.) - Innholdstyper (
content_type) og signaturordninger (sig_alg) er versjonsbegrenset: nye verdier kan kun introduseres sammen med en ny protokollversjon eller eksplisitt registeroppdatering.
12.3 Protokollversjonshistorikk
| Versjon | Verdi | Beskrivelse |
|---|---|---|
| v1 | 0x01 |
Privat/innpakket og offentlig/ubar levering; tekst (0x01), pakt (0x03), og dom (0x04) organer; ML-DSA-65 V2 forfatter/medunderskriver signerer; drand quicknet tlock; SHA3-256. |
12.4 Fremoverkompatibilitet
En v1-viser som støter på en QubEnvelope med ukjente CBOR-kartnøkler (nøkler som ikke er i §3.2-kanonisk rekkefølge) MÅ avvise den med en dekodingsfeil (§3.1). Fremadkompatibilitet avhenger av version felt, ikke på nøkkel toleranse: fremtidige tillegg — selv mindre metadata — leveres under en ny version verdi, som en v1-viser avviser med en tydelig «nyere protokoll»-feil i stedet for å stille og rolig droppe innholdet som signaturene forplikter seg til.
En v1-seer som møter sig_alg = 0x01 (ML-DSA-65) men uten støtte for ML-DSA-65-verifisering BURDE vise qub-innholdet med en merknad om “signatur til stede, men ikke verifiserbar”, og ikke avvise qub helt. Referanseimplementeringen i dag avviser alle sig_alg verdi annet enn 0x00 og 0x01 fordi v1-registeret ikke inneholder noen annen gyldig algoritme — streng avvisning og myk-feil er observasjonelt identiske inntil en tredje algoritme registreres. Myk-feil-adferden ovenfor blir bærende når §9.2 tillater en ny oppføring, og referanseviseren vil bli oppdatert til myk-feil på det tidspunktet.
12,5 Ytre innpakningsversjon
Den ytre innpakningen beskrevet i §13 har sin egen version byte, uavhengig av SealedQub.version og QubEnvelope.version. De to versjonsrommene utvikler seg separat: en fremtidig post-kvantesikker symmetrisk erstatning øker wrapper-biten uten å endre den indre protokollversjonen, og en fremtidig protokolllag-tillegg (f.eks. et nytt konvoluttfelt) øker den indre versjonen uten å endre wrapper-biten.
OUTER_WRAPPER_VERSION_* |
Verdi | Algoritme | Status |
|---|---|---|---|
OUTER_WRAPPER_VERSION_1 |
0x01 |
AES-256-GCM med 12-byte nonce, 16-byte autentiseringstag, AAD bundet til qub_id |
Aktiv for privat levering |
| — | 0x02–0xFF |
Reservert | Fremtid |
Seere MÅ avvise ukjente wrapper-versjoner med en tydelig feilmelding. Protokollen holder med vilje wrapper-versjonområdet begrenset inntil en konkret migrasjonsdriver dukker opp (f.eks. NIST-veiledning som favoriserer en annen AEAD); a 0x02 Tidsluken 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.
For privat levering lukker den ytre krypteringsinnpakningen den kanalen ved å sette inn et ekstra symmetrisk AEAD-lag mellom den kanoniske SealedQubCbor og de lagrede byteene. I nettleser-forseglingsbanen, nøkkelen på 256 biter K liv bare i URL-fragmentet av leverings-URL-en og på brukerens enheter; nettlesere sender ikke URL-fragmenter til servere, så qub.social, hver lagringsgateway og hver CDN foran noen av dem er observasjonelt blinde for K. En privat qub's lagrede representasjon er derfor uigjennomsiktig kryptert tekst hvis klartekst ikke kan gjenopprettes uten URL-en som skaperen valgte å dele. Offentlig levering utelater bevisst dette laget (§13.8).
Netto effekt:
- Oppramsingsmotstand for privat levering.
OuterWrapperforblir gjenkjennelig strukturert CBOR—det er ikke bokstavelig talt uatskillelig fra tilfeldige byte—men dets krypterte felt skjuler den gjenkjennelige innsidenSealedQubform. Den dokumenterte høsterstrategien «GraphQL-spørring for bare qub-formede opplastinger, masse-dekryptering med offentlige drand-signaturer» avsluttes ikke med klartekst uten K. - Krypto-shredding personvernsinnstilling for standard private nettleserflyt. qub.social kan ikke dekryptere de lagrede artefaktene fra sin standard server-side data. Eksplisitt gjenoppretting, offentlig levering og pålitelig server-side forsegling har ulike avslørte tillitsgrenser.
- To-nivås konfidensialitetsstige. Standard = lenkekontrollert tilgang (denne seksjonen). Mottaker-krypterte private qubs (en reservert Fase-2-funksjon, ennå ikke spesifisert) legges oppå som det andre laget.
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
├─ 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
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.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
}
Felt-invarianter.
versionMÅ være lik0x01for v1.0 wrapper-biter.qub_idMÅ være likqub_idfeltet til SealedQub hentet etter åpning. Begge referanserwrap_sealed_qubogunwrap_sealed_qubanalyser den indre CBOR og håndhev denne likheten direkte; AAD-bindingen separat gjør etter-innpakning-manipulering med den ytrequb_idautentisering mislyktes.nonceMÅ være 96 biter (12 byte), generert på nytt av en CSPRNG for hver wrap-operasjon. Gjenbruk av en nonce under samme nøkkel tillater AEAD nonce-gjenbruksangrep som gjenoppretter klarteksten; produsenter MÅ behandle (key,nonce) par som ettforsøk.ciphertexter AES-256-GCM-utdataene: krypterte byte kombinert med den 16-byt lange autentiseringstaggen.ciphertext.len() == SealedQubCbor.len() + 16akkurat.
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 chiffertekst under en annen qub_id felt i innpakningen |
AAD-mismatch → AEAD-autentisering mislykkes |
| Bland URL-fragmentet til qub A med de lagrede byteene til qub B | Feil nøkkel (og uavhengig bundet AAD) → AEAD-autentisering mislykkes |
Tøye med qub_id feltet til omslaget etter opplasting |
AAD-mismatch → 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
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
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:
- WASM-skaper:
getrandom(WebCrypto underwasm_js-backend). - API-kalleren for tjenerside-forsegling: sin lokale CSPRNG; kalleren oppgir og beholder
Ksomwrapper_key_b64url. Workeren brukerKi minnet for innpakningen, men MUST NOT vedvare den. Dette lar et idempotent nytt forsøk gjenopprette et sladdet svar ved hjelp av kallerens beholdte kapabilitet i stedet for å være avhengig av en engangs, server-generert hemmelighet.
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
- Forfatterskapsunderskrift (§9) er uendret: signaturer beregnes inne i den indre
QubEnvelopeog gjenopprettes etter pakke ut → tlock dekrypter → CBOR parse. - Mottaker-offentlig-nøkkel-kryptering (den reserverte
recipient_pubkeyfelt) er en fremtidig funksjon som skiller seg fra dagens private, lenkebasert begrensede wrapper-modus. - Den nåværende serverside-pakt medmedsigneringsflyten sender ut offentlig/ren
SealedQubCbormed synlighet0x01; det kan ikke tilfredsstille nettleser-spesifikke K-hemmelighetsmodellen fordi endelig forsegling skjer etter server-mediert medsignering. En fremtidig produsent av private avtaler kan bruke den samme innpakningen, som er byte-blind for den indre innholdstypen.
13.8 Offentlige qubs (utelatelse av omslag)
Den ytre innpakningen er valgfritt på leveringslaget. En skaper kan forsegle en qub som offentlig, i hvilket tilfelle den kanoniske SealedQubCbor går inn i lagringspipeline direkte, uten OuterWrapper lag og ingen nøkkel K:
SealedQubCbor bytes ──(public)──▶ stored as-is
SealedQubCbor bytes ──(private)─▶ AES-256-GCM(K, …) ▶ OuterWrapper ▶ stored
En offentlig pub er tidslåst men ikke lenkebeskyttet: det forblir uleselig til dens tilfeldige runde publiseres (tlock-laget er uendret), men etter opplåsning kan hvem som helst som har lagringstransaksjons-IDen dekryptere det — ingen URL-fragment er nødvendig, fordi det ikke finnes K. Dette er den bevisste handelen for overflater som serveren må drive: e-poster for avsløringsvarsler, fragmentfrie oEmbed/auto-embed-lenker, og rikere SEO etter avsløring krever alle en lenke som fungerer uten en hemmelighet serveren aldri har (§13.6). En privat qub kan fortsatt bruke den eksplisitte <qub-embed src="full_delivery_url"> form når utgiveren leverer sin komplette fragmentbærende kapasitet.
Konsekvenser en produsent MUST ta høyde for:
- Ingen oppramsingsimmunitet. Offentlige qubs frasier seg §13.1-opptellingsimmunitetsegenskapen ved konstruksjon. Referanseopplastningstjenesten stempler en
Visibility: publicpermanent-storage-merke på dem (og bare dem) slik at de er bevisst oppdagbare; private qubs har ikke et slikt merke og beholder sin byte-uatskillbarhet. - Ren teksttittel eksponert ved forseglingsøyeblikket. §3.2
titlefelt er ren tekst inniSealedQubCbor. Under innpakningen er det skjult til en seer levererK; uten innpakningen er den verdenslesbar på permanent lagring fra øyeblikket opplastingen skjer, før opplåsing. Apper fra samsvarende skapere MÅ oppgi dette ved seglingstidspunktet. - Deteksjon er strukturell og krysskontrollert. En samsvarende visnings-/innebygging skiller de to lagrede formene ved parsing: bytes som parses som
OuterWrapperta pakk-opp-med-Ksti; bytes som parses som en bareSealedQubCboraksepteres direkte. Den gjenvunne indre verdien MÅ være i samsvar (0x00for innpakket/privat,0x01for bare/offentlig).qub_idbinder ikke synlighet, men den kanoniskeSealedQubbytes bærer det, så de offentlige og private indre kodene er ikke byte-identiske.
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 = 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
Implementeringer MÅ produsere identiske body_hash og qub_id verdier for denne inngangen. Denne testvektoren SKAL være den første enhetstesten som skrives. De kanoniske verdiene ovenfor ble beregnet av referanseimplementeringen og MÅ samsvare bit-for-bit. Historiske pre-lanserings prototypeoppsett (ingen live qubber var avhengig av de to første) brukte 92 bytes tidligere outcome_at (3d9fc2390eab043d38a1669ed3b71be76f9eefe872b9569ab1aaa027b88392b0) og 100 byte etter å ha lagt til outcome_at_or_zero (b0d032898ad629795150fdcb3f84e518f59ed05b7a2a82bc24ebdb87f52144ed). Den nåværende 108-bytes layouten ble deretter lagt til drand_round og den QUB_ID_V2 domeneavskiller. En tidlig 108-byte vektor brukte den eldre ceil rund kartlegging (drand_round = 4695445) og produsert 3a9fcb31b750d985c262fada6d4f777fd6a28be831d941d85c131f5a4bbaf8a4—fortsatt gyldig qub_id for den runde inngangen, mens eksemplet ovenfor følger §4.3 gjeldende rundekartlegging.
14.2 Låse-til-Runde Kartlegging
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
Runde 4675286 publiseres på 1595431050 + (4675286 - 1) * 30 = 1735689600—akkurat kl unlock_at, aldri før. (Den eldre forhåndsutgivelsen ceil kartlegging ga 4675285, publisert på 1735689570—30 sekunder tidlig; verifikatorer aksepterer den gamle runden i henhold til §4.3.)
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:
- Rust:
crates/qub-core/tests/wrapper_vectors.rs(cargo test -p qub-core --test wrapper_vectors). - TypeScript:
workers/api/src/crypto/__tests__/wrapper.test.ts(npm test).
Innstillingen spenner for øyeblikket tre lavnivå-innpakningstilfeller. De tester deterministisk OuterWrapper koding og AEAD-interoperabilitet uavhengig av §13.8 leveringsform-invarianten; spesielt det historiske navnet basic-text-public og dens indre visibility = 0x01 gjøre ikke gjør de resulterende innpakkede byte til en samsvarende offentlig levering. En produsent MÅ fortsatt lagre offentlige interne byte nakne og bare pakke inn private (0x00) indre bytes.
| Sak | Dekning |
|---|---|
basic-text-public |
Historisk lavnivå-armaturnavn. Minst realistisk SealedQub form, uten valgfrie felt; tester kun wrapper-bytess og er ikke en samsvarende §13.8 lagret levering. |
with-recipient-pubkey |
SealedQub med recipient_pubkey sett (reservert fremtidig bane). Øver et annet indre CBOR-nøkkelsett; dets distinkte innholdsoppsett gir uavhengig et annet qub_id (recipient_pubkey selv er ikke i §4.1 forbildet). |
longer-body |
~4 KiB kropp — øver på flerbytede CBOR-lengde-prefikser både inne i den indre konvolutten og den ytre krypterte teksten. |
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:
- Signatur: ML-DSA-65 (
sig_alg = 0x01; 1952-byte offentlig nøkkel, 3309-byte signatur) og usignert (sig_alg = 0x00). Kodebasen reserverer0x02for Ed25519, men protokoll v1 aktiverer det ikke; en v1-verifikator MÅ avvise hversig_algutenfor{0x00, 0x01}. - Tidslås: drand quicknet bare — kjede-hash, offentlig nøkkel, genesis-tid og periode er faste nettverksparametere som bæres av referansen
DrandTimelockProvider::quicknet()(crates/qub-core/src/tlock.rs) ogconfig/drand-endpoints.json. - Ytre omslag: AES-256-GCM v1 bare (§13).
Verifikatorer hårdkoder for øyeblikket nøkkel- og signaturlengder per aktiv primitiv. sig_alg og wrapper-versjon bytes er eksplisitte velgere, men v1 utfører ingen intern forhandling og godtar bare de aktive verdiene ovenfor.
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:
- Et sekund
sig_algbyte (Ed25519-aktivering, ML-DSA-87, eller enhver ny oppføring i §9-registeret). - En andre drand-kjede i produksjonsbruk.
- En andre ytre-innpakningsversjon.
- En rotasjon av tillitsroten til gjennomsiktighetsloggen —
LogProfile.anchor_owneradresse eller den fastlåste kvitteringsnøkkelens offentlige nøkkel (§16.6). DenLogProfileslutter seg til §15.2-profilflaten som en styrt primitiv: en rotasjon er signertLogProfilebump sendt i en verifikatoroppdatering (planlagte rotasjoner krysserigner utgående → innkommende; kompromittdrevede rotasjoner kan ikke, og er avhengige av denne bumpen med prev-anchor gaffelsjekk som begrenser midlertidig skade). Transparensloggversjonene rom (LOG_VERSION,ANCHOR_FORMAT) utvikle seg som uavhengige søsken, akkurat som §12.5-wrapper-versjonen er uavhengig av protokollversjonen.
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 delen er implementert (W5/UP-B1, Stadier 1–8), med produsenten og tillits-rot-omfanget angitt her. Kabelformatene, hashing og verifikatorbanene er aktive: kjernen Merkle + kanoniske CBOR-typer (
qub-core), TypeScript-speilet + ANS-104-pakkeren (workers/api/src/crypto/), den enkeltstående forfatterenLogDO+ koordinat-nøkkel-R2 nodelager, den/uploadlogg-append forsøk, de daglige anchor + bundler-drain cronene, deGET /api/v1/qub/:tx_id/proof(inkludering) ogGET /api/v1/log/consistency(RFC 9162) bevisendepunkter, det typede inklusjonsbeviset som bæres i.qubpakke (§17.5), den innebygde ANS-104-ankerverifisereren (tools/qub-verify), og den dobbelte selvpubliserte-hodene-kroken (§16.6). En vellykket/uploader alltid R2-holdbar, men er bare dekket av jord nårLOG_DOer konfigurert og den innebygde tilleggelsen lykkes; først da bærer responsen denslog_seq,receipt, oganchor_status. HvisRECEIPT_SKer fraværende eller ugyldig, den kvitteringensig_b64urler tom og gir ingen bevis for ikke-avvisning. Den nåværende/sealog avtale publiseringsbaner planlegger individuelle Arweave-transaksjoner, men legger ikke til et loggblad. Ingen kode utfører for øyeblikket/uploadkommenterer det foreslåtte senere forsoningsforsøket etter en append-feil. Den eksterne gjennomgangen W5 er fullført: §16.15 dokumenterer designbeslutninger og lanseringsbegrensninger, men disse begrensningene utvider ikke produsentdekningen som nettopp er oppgitt. Tre tillits-/distribusjonselementer gjenstår som sperret: (a) den dedikerte ankertutbørsen (ANCHOR_JWK;LogProfile.anchor_ownerer fortsatt den[0xAB; 32]plassholder); (b) kvitteringssigneringsnøkkelen og samsvarende offentlig nøkkel-pin (RECEIPT_SKer valgfritt ogLogProfile.receipt_pubkeyer for øyeblikket tom); og (c) det selvpubliserte heads GitHub-repositoriet + token (§16.6). Inntil anker-/profil-pinnene er provisjonert, rapporterer en frittstående verifikator bevisstatus ærlig i stedet for å hevde en fullstendig ankret, pinnet verifikasjon. Designet er strengt additivt og det er ingen endring påSealedQub/QubEnvelopetrådfomat.
16.1 Begrunnelse og holdbarhetsnivåer
De nåværende publiseringsveiene frakobler bekreftelse fra Arweave: de utleder og signerer en individuell transaksjon, lagrer artefakten og eksakt innsendingstilstand i R2, og legger deretter ut asynkront. Gjennomsiktighetsloggen legger til et uavhengig forankret sorteringslag for delmengden av generell /upload forespørsler hvis LogDO append lykkes:
| Kjempe | Navn | Garanti | Når |
|---|---|---|---|
| T1 | R2-første synkrone kvittering | Holdbarhetsnivå — forseglede bytes og eksakt publiseringsstatus skrives til varig lagring før suksess returneres. | Implementert på tvers av nåværende publiseringsveier. |
| T2 | Samlet inkludering av transparenslogg | Bare-legg-til, manipulasjonssikker forpliktelse + total sortering når inkludert og forankret. | Nåværende produsent: vellykket LogDO føyer til fra /upload; responsen inneholder kvitterings-tuppelen. Ikke universell. |
| T3 | Per-qub Arweave permanenthet | En individuell Arweave-transaksjon for qubben. | For øyeblikket forberedt for hver akseptert publisering og sendt asynkront; den eksakte signerte transaksjonen forblir i den tappbare utboksen til den er levert. |
Nivåene beskriver forskjellige bevis- og holdbarhetsegenskaper, ikke den nåværende kommersielle planen. Den nåværende koden planlegger fortsatt en individuell Arweave-transaksjon for hver akseptert publisering; den eksponerer ikke T3 kun som et betalt tillegg. API-nøkkel-/kontokvotatak forblir separate applikasjonskontroller.
Holdbarhet ærlighet. T1-skrivingen er synkron, så et vellykket svar etablerer applikasjonsnivåvarighet uten å vente på en Arweave-gateway. Den etablerer ikke i seg selv et uavhengig tidsstempel. En bekreftet individuell transaksjon gir sin blokk-tid øvre grense. For et svar som bærer det komplette T2-kvitterings-tuplet, kan neste bekreftede anker gi loggbeviset beskrevet nedenfor. Hvis tuplet mangler, kan ingen overflate antyde at denne quben allerede er i transparensloggen. Anker- og publiseringsforsinkelse har ingen protokollnivå numerisk 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).
Bladet har to former valgt av en kind byte, fordi den generelle opplastingsstien er byte-blind: POST /api/v1/upload behandler bevisst begge aksepterte lastformer som ugjennomsiktige og mottar qub_id og unlock_at bare som uavhengige klientpåstander. På standard privat sti, body_hash, drand_round, created_at, og drand_chain_version er i tillegg skjult inne i §13 ytre omslag, hvis nøkkel Arbeideren aldri har. Typesystemet definerer også en attestert form for en produsent som utledes body_hash / drand_round seg selv. Den nåværende /seal ruten har de verdiene, men kaller ikke LogDO, så produksjonen slipper for øyeblikket bare ut påstått (0x02) forlater vellykkede generelle opplastingsvedlegg. Delingen holder hver forpliktet verdi ærlig uten å late som den attesterte produsenten er koblet:
| Nøkkel | Vedlagt. len | Type | Tilstedeværelse | Betydning |
|---|---|---|---|---|
seq |
fire | u64 |
påkrevd | Global 0-basert bladindeks; posisjonen som inklusjonsbeviset forplikter seg til. |
kind |
fem | u8 |
påkrevd | 0x01 attestert-kapasitetsdyktig (definert, ikke for tiden utsendt) eller 0x02 påstått (klient-stempel / byte-blind opplasting). |
ref |
fire | bstr[32] |
påkrevd | Bladreferanse-id. Bekreftet → rå qub_id. Påstått → the blindet ID SHA3-256(qub_id ‖ log_blind_secret) (§16.2.1). |
chash |
seks | bstr[32] |
påkrevd | Innholdsadresse SHA3-256(stored_bytes) — det ene innholdsbåndet Arbeideren alltid ærlig kan beregne, på begge stier. |
unlock_at |
ti | i64 |
påkrevd | Kopiert (bekreftet) eller hevdet (påstått); validert > 0 før det går inn i bladet. |
received_at |
tolv | i64 |
påkrevd | Arbeider veggklokke på R2-ack. Ikke-bevisbasert (operatør-erklært; §16.6). Tilstede for selvbeskrivelse, aldri et bevis. Validert > 0. |
body_hash |
ti | bstr[32] |
kind=0x01 bare |
Utelatt på 0x02 — arbeideren mangler det under §13. |
drand_round |
tolv | u64 |
kind=0x01 bare |
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.
Den LogDO Holdbart objekt er enkeltforfatter (blockConcurrencyWhile, speilvendt QuotaDO / EntitlementDO) — å legge til i en delt logg er lese-endre-skrive på delt tilstand og må derfor gå gjennom en DO, aldri KV. Den cacher treets høyre-kant-front (O(log n) hashes) så å lukke en batch er O(batch). En parti er settet av blader som er festet sammen; de implementerte triggerne er en tree_size forhånd på minst LOG_BATCH_MAX_LEAVES (standard 4096), alder som når ankerkadensen, eller en eksplisitt administrativ/cron tvangslukking. root_i er den kumulative Merkle-tre-hashen over bladene 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.
Loggdesignet krever én varm vellykket-append kvitteringsnøkkel (§16.10), festet inn LogProfile og kryss-signert av anchor_owner. Den nåværende implementeringen har ikke fullført den tillitsrot-leveringen: RECEIPT_SK er valgfritt, en fraværende/ugyldig nøkkel gir sig_b64url: "", og den kompilerte LogProfile.receipt_pubkey er tom. Et slikt kvittering kan beskrive den vedlagte siden, men er ikke en ikke-avvisbar signert kvittering. Den sterkere designpåstanden gjelder bare etter at en verifikatorutgivelse låser den tilsvarende offentlige nøkkelen og eieren av ankeren kryssignerer den. Et publiseringssvar uten den komplette kvitteringstuplet gjør ingen logg-akseptpåstand; en med en tom signatur gjør en påstand om legg-til-posisjon, men ingen påstand om signatur-verifisering.
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.
Tvetydighetsvindu (førsteklasses tillitsparameter). Et blad er kun motstandsdyktig mot tvetydighet når dets dekkende anker er Arweave-bekreftet. Vinduet er received_at → anchor confirmation (kadens + Arweave-finalitet, uten garanti for protokollatvise forsinkelser). Før tilførsel av tillitsrot, gir den nåværende implementeringen qub's operasjonelle integritet pluss hvilken som helst usignert tilleggmetadata som er til stede; den leverer ikke den planlagte ikke-avvisningsgarantien. Tre ansvarlighetsartefakter definerer det fullførte designet (vitnemodellen er §16.15 Q2s resolusjon):
- Forseglet kvittering (avhengig av forsyning) — SCT-analogen returnerte når en opplastings loggtillegg lykkes (§16.10). Den blir ikke-avviselig bare når
sig_b64urler ikke-tom og det tilsvarende forholdet mellom offentlig nøkkel/anker-eier er festet i verifikatoren. Den nå tomme produksjonsprofil-pinnen kan ikke støtte den avgjørelsen. Denne kontrollen gjelder ikke for en utelatt kvitterings-tuple eller en usignert kvittering. - Publisert overvåkingsmetodikk + tidligere-kjede-gjennomgang — ankeret
prevkjede blir gått hode→genesis; en gaffel (to ankere på ensizemed forskjelligroot, eller en ødelagtprev) er publiserbart bevis på dårlig oppførsel. Oppdagelse av tvetydighet er en uttalt operasjonell forpliktelse, ikke en stille antakelse. - Dobbelt egenpubliserte hoder — hvert nytt hode
{sth_hash, tree_size}er lagt ut på en dedikert qub-eid offentlig, bare-legg-til GitHub-repositorium (den bærekraftige, manipulasjonssikre egenpubliseringsbeinet), med et sosialt innlegg kun som beste forsøk på bekreftelse. En mislykket posting MÅ sende side (ikke feile stille). Implementert (Trinn 8) sompublishHeadhekte på ankret cron (workers/api/src/utils/heads-publish.ts): enPUTtil innholds-API-et uten enshaer kun-legg-til (en422betyr at head allerede er publisert, aldri en overskriving); valgfritt / utplassering-sperret påPUBLISH_HEAD_GITHUB_{TOKEN,OWNER,REPO}og inaktiv til depotet er gjort tilgjengelig. En hard GitHub-feilsiden viahealth_alertkanal og støter på en holdbar feilmåling (m:tlog_publish_head_fail); Arweave-ankeret ruller aldri tilbake ved en publiseringsfeil. "Ikke fail silent" er garantert av denne holdbare metrikken — som operatører MÅ varsle dashbord på — selv om e-postsiden med beste innsats ikke kan leveres. To ærlige begrensninger følger av "anchor-on-advance" (cronen publiserer bare når størrelsen øker): en midlertidig GitHub-feil etterlater en gap i den publiserte-hoder-sekvensen for den størrelsen — begrenset, ikke stille (den sider), og fordi hvert hode begår et superset-tre, bygger en §16.9 konsistensbevis broen; avgjørende, det konsistensbeviset beregnes fra autoritativ Arweave-ankret tre, ikke fra GitHub-overflaten, så en GitHub-gap svekker aldri verifiserbarhet. En opphentingstilfylling som fyller publiserte-hoder-gap er en utsatt forbedring.
Ærlighet bundet (bindende begrensning). Fordi qub kontrollerer begge planlagte postflater, er dette selvutgitt, ikke uavhengig vitnet. Ingen produkt-, markedsførings- eller juridisk flate kan hevde at loggen er "uavhengig vitnet". Etter at kvitteringen/profilen/hodeportene er provisionert, er den tillatte påstanden at tvetydighet er oppdagbar og et vellykket signert tillegg etterlater en ikke-avvisbar kvittering. Før den tid er påstanden utilgjengelig. Et ekte uavhengig tredjepartsvitne utsettes til en fremtidig §15 styringsoppdatering.
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, sett på nytt med volum (størrelsestriggeren forkorter automatisk den effektive rytmen under belastning). Den nåværende produsenten implementerer ikke en betalt-segl tvinge-feste krok. Den anker lommebok er dedikert og lavhastighets, atskilt fra opplastingslommeboken — det MÅ være sin egen JWK (en egen nøkkel, ikke en logisk rolle på opplastingslommeboken) slik at et kompromiss av opplastingslommeboken ikke kan forfalske ankere — med et fast daglig budsjett for anker-transaksjoner. Forvaltningsinnstillingen er klart uttalt: en smalområde-hurtigtast med en stram kretsbryter og lav balanse, ikke «kald» — en lommebok som automatisk 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).
ANS-104-kodebanen betjener den utsatte tilbakefalls-/tømmemekanismen og skriver AnchorBundle DataItems. Den vanlige publiseringsveien lager først en eksakt signert Arweave-transaksjon og lagrer dens JSON i en holdbar utboks; direkte publisering er en latensoptimalisering, og drain-vei prøver den samme transaksjonen på nytt før den bruker bundler-fallbacken. Signaturordning (løst — §16.15 Q8): v1 signerer med RSA-PSS (signaturtype 1) gjenbruk av den eksisterende Arweave-lommebokens JWK-mekanisme (ingen nye langvarige nøkkelhåndteringer, som tjener hypotesen "et hemmelighet mindre"); Ed25519 er utsatt til §15 PQ-migrasjonsvei.
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):
- Tverr-språk-fixturen
tlog_v1.json(Rust + TS, §14.5wrapper_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). - 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).
- Deep-hash + RSA-PSS-veien må rundtur gjennom de samme
crypto.subtle-primitivene produksjon bruker, slik at den egenutviklede enkoderen er byte-kompatibel. - 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.
InclusionProof — GET /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 }.
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. 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
Den implementerte POST /api/v1/upload sekvens er:
- Fremre halvdel porter (aut., validering, idempotens sharding-nøkkel) — uendret.
- Opprett, merk og signer den eksakte individuelle Arweave-transaksjonen. Dette utleder
tx_idlokalt, selv om oppretting av transaksjon kan hente belønnings-/anker-metadata fra en gateway. En forberedelsesfeil mislykkes fortsatt før forespørselen blir bekreftet. - Synkront skriv det valgte artefaktet ved
qub-cache/<tx_id>og vedvarer de stabile opprettelses-/outbox-operasjonsregistrene. Dette er holdbarhets- og gjenprøvingsnivået; feil før oppgjør returnerer 503. - Når
LOG_DOer konfigurert, synkront forsøkeLogDO.append(leaf). Den enkeltstående forfatteren tildelerseq, utvider oppføringskjeden, og oppdaterer grensen. Append RPC gjør bare det; batch close kjører utenfor banen på alarmen. En append transport-/applikasjonsfeil er for øyeblikket feil-mykt: svaret kan fortsatt lykkes utenlog_seq,receipt, elleranchor_status. Til tross for en implementeringskommentar, er ingen automatisk senere loggforlik koblet i dag. - Returner bekreftelsen. Inkluder
{ log_seq, anchor_status: "pending", receipt }kun når append returnerte den fullstendige vellykkede tuplen.receipt.sig_b64urler tom når kvitteringsundertegneren ikke er tilgjengelig; klienter MÅ IKKE kalle den verdien signert eller ikke-benektningsbar. Fravær av tuplen betyr bare varig publisering, ikke aksept i gjennomsiktighetsloggen. - Bruk en utsatt oppgave for å sende den nøyaktige signerte transaksjonen. Suksess fjerner utboksen; feil lar den stå for den begrensede drain-cronen og må ikke endre det som allerede er bekreftet
tx_id. Foreløpige metadata og andre beste-innsats sidevogner utsettes også.
Forsinkelsesgrense. Forespørselsstien inkluderer front-half myndighet/kvotearbeid, transaksjonsforberedelse/signering, varige R2-skrivinger, og (når konfigurert) LogDO forsøk. < 300 ms vises i designgjennomgangen som et operasjonelt mål, ikke en protokollgaranti; det nåværende transaksjonsforberedelsestrinnet kan utføre en forespørsel om gateway-metadata. Latensalarmer og lanseringsporter er operasjonelle kontroller, ikke bevis tilgjengelig for en verifikator.
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.
Krev tak (bindende lanseringsbegrensning — løst §16.15 Q1). For et hevdet (kind=0x02) bladet, den avgrensede påstanden ovenfor er tak på hva som helst produkt, markedsføring, vilkår eller bevisviseflate kan hevde. Ingen flate kan angi eller antyde at loggen beviser innholdet eller låserunden for en byte-blind opplasting — loggen beviser rekkefølge + en tillitsløs øvre grense for forpliktelsestid for en utydelig krypteringstekst. Innholds- og rundebevis kommer utelukkende fra eksisterende §11 .qub-pakkeverifisering, som er logguavhengig. En publisering uten vellykket vedlegg/mottak har ingen loggkrav i det hele tatt.
16.12 Versjonering og W3-koordinering
Det er nei SealedQub trådkule og derfor ingen protokollversjonsøkning (§12.2): loggen er en sidecar som gjør forpliktelser til eksisterende felter og bytes, så den går ikke inn i §12.3-protokollversjonshistorikken. W3s valgfrie drand_chain_version er uberørt og forblir den eneste valgfrie SealedQub felt. Loggen introduserer i stedet sine egne uavhengige versjonsrom — LOG_VERSION_1, ANCHOR_FORMAT_1, InclusionProof.ver — som speiler §12.5 wrapper-versjonsuavhengighet (wrapperen har en versjonsbyte uavhengig av protokollversjonen, og loggversjonene 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 — avgjort)
Den eksterne W5-gjennomgangen (en adversariell designgjennomgang + eiergodkjenning) er fullført. Hver beslutning nedenfor er avgjort og reflektert i §16-teksten ovenfor; bindende lanseringsbegrensninger blir gjentatt på slutten. Implementering kan fortsette under dem.
- Standardbane (
kind=0x02) blad ærlighet — LØST. Send den to-flik-typen delt som spesifisert:kind=0x02forplikter seg ikkebody_hashellerdrand_round. Nei*_body_hashfelt på den byte-blinde stien (det ville være det mest lesbare falske «verifiserte» signalet for integratorer og er en bekvemmelighet §11 allerede gir fra pakken). Gjør ikke kreve server-sigill for log-godkjente qubs (det ville tvinge klartekst gjennom Worker og ødelegge den kryptografiske shredding-volden). Enhver selvbeskrivende kortslutning hører hjemme i.qubpakke / beviskonvolutt som et verifikator-tilberegnet felt, aldri et bladfelt. Eiere-bekreftet kravtak: §16.11. - Tvetydighet / utelatelsesansvar — DESIGN LØST, TILFØRSEL UFULLSTENDIG. Designet krever at segl-mottakstasten blir pinnet inn
LogProfileog kryss-signert avanchor_owner, pluss overvåkingsmetodikk, tidligere kjedevandring og doble selvpubliserte hoder. Den sammenstilte profilen og distribusjonshakene er fortsatt plassholdere/valgfri som beskrevet i §16.6, så det sterkere oppdagbare + kvitterte kravet er ikke aktuelt før disse portene lukkes. Det må aldri markedsføres som uavhengig vitnet. En ekte tredjepartsvitne utsettes til en §15 styringsoppgradering. - Festet rotfesteeier-tillit + rotasjon — LØST. Adopter
LogProfilepin (§16.6); verifikatoren sjekkeranchor_tx.owner == anchor_ownerog verifiserer tx-data → tx_id-binding lokalt. Rotasjonsstyring er en §15 utvidelse for å bygge (§15.3-trigger lagt til), ikke en gjenbruk; planlagte rotasjoner kryss-signeres, kompromissdrevne rotasjoner faller tilbake til §15-bump med gaffelsjekk som begrenser skade. - Privat qub-blad blindende — LØST. Fortsett å blinde for private qubs (
ref = SHA3-256(qub_id ‖ log_blind_secret)), råqub_idfor offentlige qubs (allerede §16.2.1),chashsom det frittstående båndet.log_blind_secreter en korrelasjon/Sybil-gradert hemmelighet, kun roter-fremover (§16.2.1). received_at— BESLUTTET. Hold det i bladet, forpliktet men eksplisitt ikke-beviselig; aldri brukt som bevis eller tvisteløsning på noen overflate. Enhver overvåkings-sanity-sjekk sammenlignes med Arweave blokk-tid.T, ikke den operatørkontrollerteanchored_at(§16.6).- Lagvis bevisbar-timing — DESIGNLØSNING, IKKE NÅVÆRENDE ROUTING. Det gjennomgåtte designet tildeler ankeblokk-tidspunkt til den batch-behandlede tieren og eksakt-times bevis til betalt T3, uten numerisk SLA for førstnevnte. De nåværende rutene har ikke implementert den kommersielle forskjellen: de planlegger en individuell transaksjon for hver akseptert publisering, og loggdekning forblir betinget som angitt i §16.1/§16.10. Produktteksten må beskrive implementeringen, ikke denne fremtidige tier-splittelsen.
- Kumulativt tre over arbeidere — VEDTATT. Enkelt kumulativt RFC 9162-tre + frontier-cachet enkel-skriver LogDO (komfortabelt spillerom i forhold til ~1k skriver/sek DO-taket; utsett Merkle-av-shard-røtter sharding til nær det). Den koordinatnøkkel-baserte
(level, index)R2 node store + wiped-DO cold-leaf testvektor er implementert (§16.9).< 300 msforblir et design-/operasjonelt mål, ikke et protokollløfte (§16.10). - ANS-104 signaturordning + dyp-hash — LØST. RSA-PSS (sig type 1, gjenbruker den dedikerte anchor-wallet JWK); Ed25519 utsatt til §15 PQ-banen. Den håndrullede SHA-384 dype hashen er begrenset til both-directions cross-impl-fixturen, en statisk-only reference-bundler interoperabilitetskontroll, den delte-
crypto.subtletur-retur, og post-pakke Arweave-akseptovervåker (§16.8).
Bindende lanseringsbegrensninger (føre til implementering + produkt/juridisk gjennomgang):
- Kravtopp (Q1/Q6). Ingen overflate kan si stammen beviser innholdet til et påstått blad eller låse opp runde; det tillatte kravet for en vellykket forankret
kind=0x02bladet er ordnet, manipulasjonssikkert, med en tillitsløs øvre grense for forpliktelsestid. Et svar uten kvitteringstuple har ingen loggkrav. Ingen tidsstemplekopi har noen numerisk latensergaranti. - Vitne ærlighet (Q2). Markedsviker som påviselig + kvittert, aldri uavhengig bevittnet.
- Kvittering + ankerkoder (Q2/Q8). Før kravene om ikke-avvisning/forankret-verifisering sendes, sørg for og samle inn kvitteringens offentlige nøkkel, kryssignér den med den prosjekterte ankereieren, og behold anker-lommeboken som sin egen JWK atskilt fra opplastingslommeboken.
- Dyp-hash port (Q8). Ingen anker- eller T3-transportskip før begge-retninger fastsettelse + interoperabilitetssjekk er bestått; akseptmonitoren varsler ved feil.
- Lagringsforutsetning (Q7). Koordinat-nøkkel-node-lager + slettet-DO kald-blad vektor er forutsetninger for garantien 'gjenoppretting ugyldiggjør aldri et bevis'.
17. Bærbar verifiseringspakke (.qub)
Status. Denne delen er implementert (W7 / UP-C2):
qub_core::exportproduserer og analyserer bunten, ogtools/qub-verifyer en offentlig, selvstendig CLI som verifiserer en offline. §11 og §16.9 refererer allerede til "the".qubpakke som enheten en frittstående verifikator bruker; denne seksjonen angir dens byte og verifikasjonsgjennomgang. Den er strengt additiv — pakken pakker eksisterende §11-inndata og endrer ingen on-chain ledningsformat.
17.1 Formål
§11 fastslår at enhver tredjepart kan verifisere en qubs kryptografiske artefakt uten qubs samarbeid. The .qub pakken utfører den verifiseringen bærbar og frakoblet: det pakker den forseglede CBOR og drand-rundesignaturen som låser den opp, inn i en enkelt selvstendig artifakt, slik at en mottaker kan verifisere innholdsintegritet, runde-binding og eventuelle forfattersignaturer med ingen nettverksanrop i det hele tatt (ingen lagringshenting, ingen live drand-forespørsel, ingen qub-API). En pakke alene beviser ikke når dens krypterte tekst ble opprettet; en uavhengig verifisert lagringstransaksjon eller forankret loggbekreftelse gir det separate eksistens-tidskravet (§11, §17.5).
17.2 Pakkeformat
A QubBundle er håndskrevet kanonisk CBOR under §3.1-profilen (definittlengde, ingen tagger, ingen flyttall, heltall i kortest mulig form, NFC-tekst, valgfrie felter utelatt når de er fraværende, nøkler sortert etter kodet byte-lengde stigende deretter bytevis). De tre 15-tegns nøklene rekkefølge d < i < s. Rå .qub filen er nøyaktig disse bytene; for URL- eller kopier-inn-lim-overføring er de samme bytene base64url(uten-padd).
| Nøkkel | Vedlagt. len | Type | Tilstedeværelse | Betydning |
|---|---|---|---|---|
version |
åtte | u8 |
påkrevd | Pakkeformatversjon (0x01). |
sealed_at |
ti | i64 |
valgfri | Skaper-oppgitt segl-tid (Unix-sekunder); selvbeskrivende, ikke-bevismessig. |
drand_round |
tolv | u64 |
påkrevd | Runden som quben er låst til. En projeksjon av den innebygde forseglede quben. |
arweave_tx_id |
fjorten | tstr |
påkrevd | Transaksjons-IDen de forseglede bytene ble lagret under (provenanspeker). |
drand_chain_id |
femten | tstr |
påkrevd | Den drand kjeden (heks). En projeksjon av den innebygde forseglede qub. |
drand_signature |
seksten | bstr |
påkrevd | Den drand beacon-signaturen for drand_round — verdien som låser opp chifferteksten. |
inclusion_proof |
seksten | bstr |
valgfri | §16 gjennomsiktighets-logg Merkle inklusjonsbevis, etter at et forankret bevis er tilgjengelig (§17.5). |
sealed_qub_cbor |
seksten | bstr |
påkrevd | Den indre SealedQubCbor bytes (etter §13-opprulling), altså §11 verifikasjonsinngangen. |
drand_round og drand_chain_id er bekvemmelighetsprojeksjoner av sealed_qub_cbor, bærtes slik at verktøy kan lese dem uten å analysere den indre CBOR-en. De er avledet ved konstruksjon og kontrollert på nytt under dekoding mot den parserte forseglede qub; en bunt hvis toppnivåfelt er uenig med nyttelasten, blir avvist. Koderdisiplin speiler resten av ledningsformatet: avvis en tom drand_signature eller arweave_tx_id, og bundet hvert felt med variabel lengde.
17.3 Hva den innebygde drand-signaturen beviser
Pakke bærer drand-signaturen i stedet for å kreve at verifikatoren henter den. Timelock-dekryptering (tlock over drand-kjeden, §8) kan bare lykkes med ekte fyrlyssignatur for den bundne runden—en verdi kjeden kun publiserer når runden er over, og som er en gyldig BLS-signatur under kjedens offentlige nøkkel. En forfalsket eller feil signatur mislykkes i BLS-verifikasjon eller IBE/AEAD-dekryptering. En pakke som dekrypterer, beviser derfor: chifferteksten er bundet til runde R, og runde R har passert. Verifikatoren fester kjeden (DrandTimelockProvider::quicknet()) og anvender §11 sjekk for rundebinding, slik at en bunt ikke kan hevde en runde dens krypteringstekst ikke er bundet til.
Dette er et bevis for utgivelsesbetingelse, ikke et opprettelsestidspunkt. Etter runde R har
som har gått, kan alle lage en ny chiffertekst for R og pakke den som allerede er offentlig
signatur. Derfor må ikke pakken alene beskrives som bevis på at
chiffertekst eller innhold eksisterte før R, før unlock_at, eller før en hvilken som helst hendelse.
17.4 Veiledning for offline-verifisering
qub-verify <file.qub> kjører standard §11-prosedyre helt fra bunten, og driver qub_core::unlock::unlock med en festet 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.
CLI avsluttes 0 (verifisert), 1 (verifisering mislyktes — fortsatt låst, body-hash samsvarer ikke, ødelagt runde/kjedebinding, eller en signatur som ikke kan verifiseres), eller 2 (bruk / feilaktig bunt). A --json rapporten har de samme avgjørelsene for automatisering. Fordi pakken er selvstendig, verifiseringskraten (qub-core) og CLI (qub-verify) er den eneste programvaren en tredjepart trenger; begge er offentlige og gjenbruker protokollens eksisterende verifikasjonsvei — ingen spesiallaget kryptografi.
17.5 Forhold til gjennomsiktighetsloggen
inclusion_proof er en valgfri plass for §16 Merkle-inklusjonsbeviset. Kun bundle-verifisering (§17.4) er fullstendig for integritet, rund binding / rund forløpt, og valgfritt forfatterskap, men har med vilje ingen selvstendig tidsstemplet eksistenspåstand. En befolket, fullstendig anker-verifisert inclusion_proof legger til løv-typespesifikk forpliktelse og øvre tidsgrense fra §16.11 uten å endre pakkens formatversjon. Et fraværende bevis betyr bare «ingen bevis inkludert»—ikke «ugyldig» og ikke nødvendigvis «ikke forankret».
I referanseimplementeringen er sporet nå skrevet: qub_core::export::QubBundle::inclusion_proof_typed() returnerer en Option<InclusionProof> bærende den fullstendige §16.9-strukturen (blad, revisjonssti, forankret rot, og AnchorRef) gjennom det samme ugjennomsiktige CBOR-feltet — ingen økning i bundle-formatversjon. Den frittstående qub-verify CLI bruker det via sin --anchor ben, og — inntil anker-lommeboken er provisjonert (§16, Status) — rapporterer en befolket-men-plassholder-eier-bevis som kun-inkludering i stedet for fullstendig ankret-verifisert.