qub Protokol Spesifikasyonu

qub, kriptografik zamansal taahhütler için bir protokoldür: sözleri gelecekteki bir tarihe mühürlemek ve o tarih geldiğinde tam olarak ne söylendiğini ve ne zaman söylendiğini kanıtlamak için bir sistem.

Bunu mümkün kılan üç ilkel öğe vardır. drand, merkezsiz bir rastgelelik işaretçisidir — açıklama tarihi herhangi bir tarafın iyi niyetiyle değil, fizik tarafından uygulanır. Kalıcı herkese açık depolama, kurcalanamaz bir kamu deposudur — bir qub mühürlendikten sonra hiçbir taraf onu düzenleyemez veya silemez. ML-DSA-65, post-kuantum bir dijital imzadır — her qub, gizli anahtarı yazarın cihazından asla çıkmayan bir anahtar çiftine bağlıdır.

Bu ilkel öğeler birlikte, zamana kilitli, kurcalama kanıtlı ve atfedilebilir bir ifade oluşturur — dünyanın geçmişi uydurma yeteneği geliştikçe değeri artan bir makbuz.

Bu belgenin geri kalanı, birlikte çalışabilir uygulamalar için gerekli olan normatif spesifikasyondur.


qub Protokol Spesifikasyonu

Alan Değer
Sürüm 1.0 (protokol sürümü 0x01, dış sarmalayıcı sürümü 0x01)
Tarih 2026-05-01
Durum Taslak
İncelendiği tarih 2026-05-01

Bu belge, qub zamanlı taahhüt sistemi için normatif protokol spesifikasyonudur. Birlikte çalışabilir uygulamalar için gerekli olan veri yapılarını, serileştirme kurallarını, türetme formüllerini ve doğrulama prosedürlerini tanımlar.

Kapsam: protokol katmanı kasıtlı olarak dilden bağımsızdır — qub gövdesi opak düz metin / markdown / pakt baytlarıdır ve yerel ayarlara duyarlı sunum izleyicinin sorumluluğundadır (qub.social web uygulaması, <qub-embed> iframe'i, MCP istemcileri, vb.).


1. Gösterim ve sözleşmeler

Gösterim Anlam
u8, u64, i64 Belirtilen bit genişliğinde işaretsiz/işaretli tam sayılar
[u8; N] N bayttan oluşan sabit uzunluklu bayt dizisi
Vec<u8> Değişken uzunluklu bayt dizisi
Option<T> T türünde değer veya yok
String UTF-8 metin dizesi, NFC normalleştirilmiş
`
SHA3-256(x) x bayt dizisinin NIST SHA3-256 özeti (FIPS 202)
ceil(x) Tavan fonksiyonu: x'ten büyük veya eşit en küçük tam sayı
CBOR Concise Binary Object Representation (RFC 8949)
big-endian En anlamlı bayt önce

Ön görüntü yapılarındaki tüm tam sayılar, aksi belirtilmedikçe big-endian sabit genişlikli bayt dizileri olarak kodlanır (i64 → 8 bayt, u8 → 1 bayt).

Tüm zaman damgaları UTC cinsinden Unix saniyeleridir.


2. Veri yapıları

2.1 ComposeQub (Yaratıcının bellek içi durumu)

CBOR'a serileştirilmez. Kalıcı depolamada saklanmaz. Yaratıcı uygulamasına yereldir.

ComposeQub {
    draft_id:       [u8; 16],        // Random, generated locally
    created_at:     i64,             // Unix seconds UTC
    unlock_at:      Option<i64>,     // Unix seconds UTC; None while composing
    visibility:     u8,              // 0x01 = public (only value in MVP)
    content_type:   u8,              // 0x01 = text (only value in MVP)
    plaintext:      Vec<u8>,         // UTF-8 qub body
    sender_label:   Option<String>,  // Decorative display name; not authenticated
    status:         DraftStatus,     // Composing | Sealed | Uploaded | Failed
}

2.2 QubEnvelope (Şifresi çözülmüş yük)

Kanonik CBOR (§3) kullanılarak serileştirilir. SealedQub içinde şifrelenir. Bu, şifre çözüldükten sonra içerik bütünlüğünü kanıtlayan yapıdır.

QubEnvelope {
    version:             u8,              // Protocol major version (0x01 for v1)
    qub_id:              [u8; 32],        // Derived (see §4.1)
    content_type:        u8,              // Content type registry (see §6)
    created_at:          i64,             // Unix seconds UTC
    unlock_at:           i64,             // Unix seconds UTC
    outcome_at:          Option<i64>,     // V1.1 — when reality renders judgment (verdict-uplift-plan §3.1)
    sender_label:        Option<String>,  // Decorative; not authenticated in MVP
    reply_to:            Option<[u8; 32]>,// Parent qub_id for reply chains; not in qub_id preimage; not signed (see §9.3)
    body:                Vec<u8>,         // Content payload (UTF-8 for text, CBOR for pact)
    body_hash:           [u8; 32],        // SHA3-256(body) (see §4.2)
    sig_alg:             u8,              // Signature algorithm (see §9.2)
    author_signature:    Option<Vec<u8>>, // Set when sig_alg != 0x00
    author_pubkey:       Option<Vec<u8>>, // Set when sig_alg != 0x00
    cosigner_pubkey:     Option<Vec<u8>>, // Set for cosigned pact bilateral agreements
    cosigner_signature:  Option<Vec<u8>>, // Set for cosigned pact bilateral agreements
}

Temel hat (imzasız metin qub): version = 0x01, content_type = 0x01, sig_alg = 0x00, tüm Option alanları yok.

Diğer v1 yapılandırmaları: content_type = 0x03 (pakt gövdesi, bkz. §6.1); author_signature ve author_pubkey mevcut olarak sig_alg = 0x01 (ML-DSA-65) (bkz. §9.3); birlikte imzalanan paktlar için cosigner_pubkey ve cosigner_signature birlikte mevcut (bkz. §9.7); yanıt zinciri qub'ları için reply_to, üst qub'un qub_id'sine ayarlanır (imza kapsamı sonuçları için bkz. §9.3).

2.3 SealedQub (Kanonik aktarım biçimi)

Kanonik CBOR (§3) kullanılarak serileştirilir. Kalıcı depolamaya yazılır. Bu, zincir üzerindeki artefakttır.

SealedQub {
    version:           u8,              // Protocol major version (0x01 for v1)
    qub_id:            [u8; 32],        // Same as QubEnvelope.qub_id
    visibility:        u8,              // 0x01 = public; v1 viewers reject other values
    unlock_at:         i64,             // Unix seconds UTC
    outcome_at:        Option<i64>,     // V1.1 — surfaced on the verdict-watch CTA
                                        //   before reveal; mirrors QubEnvelope.outcome_at;
                                        //   bound to qub_id via the §4.1 preimage.
    drand_chain_id:    String,          // drand chain hash (hex string)
    drand_round:       u64,             // Target drand round number
    tlock_ciphertext:  Vec<u8>,         // tlock-encrypted QubEnvelope CBOR bytes
    recipient_pubkey:  Option<[u8; 32]>,// Reserved field; accepted by canonical CBOR
                                        //   but not interpreted by the v1 reference viewer
    title:             Option<String>,  // Plaintext title surfaced on the viewer
                                        //   countdown before reveal. Bound to qub_id
                                        //   via title_hash (§4.1). 1..=100 NFC code
                                        //   points, no control characters.
}

2.4 RevealedQub (İzleyici uygulama durumu)

CBOR'a serileştirilmez. İzleyici uygulamasına yereldir. Başarılı şifre çözme ve doğrulamadan sonra inşa edilir.

RevealedQub {
    qub_id:              [u8; 32],
    arweave_tx_id:       String,
    visibility:          u8,
    content_type:        u8,
    created_at:          i64,
    unlock_at:           i64,
    outcome_at:          Option<i64>,       // V1.1 — QubEnvelope.outcome_at / SealedQub.outcome_at'tan taşınır; açıklama sayfasındaki karar-izleme bloğunu sürer (verdict-uplift-plan §5.1)
    drand_chain_id:      String,
    drand_round:         u64,
    sender_label:        Option<String>,
    title:               Option<String>,    // Carried forward from SealedQub.title
    reply_to:            Option<[u8; 32]>,
    body:                Vec<u8>,
    body_hash:           [u8; 32],
    body_hash_verified:  bool,
    author_signature:    Option<Vec<u8>>,
    author_pubkey:       Option<Vec<u8>>,
    signature_verified:  Option<bool>,
    cosigner_pubkey:     Option<Vec<u8>>,
    cosigner_signature:  Option<Vec<u8>>,
    cosigner_verified:   Option<bool>,
}

3. Kanonik CBOR profili

Tüm SealedQub ve QubEnvelope serileştirme bu profille uyumlu OLMALIDIR. Aynı mantıksal yapı verildiğinde iki uygulama aynı baytları üretmek ZORUNDADIR.

3.1 Kodlama kuralları

Kural Spesifikasyon
Standart RFC 8949 §4.2.1 (Çekirdek Deterministik Kodlama Gereksinimleri)
Harita anahtarı sıralaması Önce kodlanmış bayt uzunluğuna göre sıralanır (kısa olan önce gelir), sonra sözlüksel olarak (aynı uzunluktaki kodlamalar için bayt bayt)
Tam sayı kodlaması En kısa biçim: ilk baytta 0–23; 2 baytta 24–255; 3 baytta 256–65535; vb.
Uzunluk kodlaması Yalnızca kesin uzunluklar. Belirsiz uzunluklu diziler, haritalar, bayt dizileri veya metin dizeleri yoktur (ek bilgi = 31 yasaktır).
Etiketler CBOR etiketi yoktur (ana tür 6 yasaktır).
Kayan nokta Kayan nokta yoktur (ana tür 7, 0xF9–0xFB değerleri yasaktır).
Metin dizeleri UTF-8 kodlamalı, NFC normalleştirilmiş (Unicode Normalleştirme Formu C).
Bayt dizeleri Ham baytlar. CBOR katmanında base64 kodlaması yoktur.
Yinelenen anahtarlar Hata ile reddet. Ayrıştırıcılar yinelenen harita anahtarlarını sessizce kabul ETMEMELİDİR.
Basit değerler Yalnızca true (0xF5), false (0xF4) ve null (0xF6) izin verilir.
İsteğe bağlı alanlar Mevcut olmayan isteğe bağlı alanlar CBOR haritasından tamamen çıkarılır (null olarak kodlanmaz). Mevcut isteğe bağlı alanlar sıralı anahtar düzeninde dahil edilir.

3.2 Doğrulanmış kanonik anahtar sıraları

Bu anahtar sıraları normatiftir. Uygulamalar anahtarları tam olarak bu sırayla yaymak ZORUNDADIR. Hata ayıklama onaylamaları, üretim dışı yapılarda sıralamayı doğrulamalıdır (SHOULD).

QubEnvelope (sürüm 0x01, imzasız, tüm isteğe bağlı alanlar yok):

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

QubEnvelope anahtar sırası türetmesi: her anahtar bir CBOR metin dizesidir. Kodlanmış uzunluk = 1 baytlık başlık + dize uzunluğu (24 bayttan kısa dizeler için). Önce toplam kodlanmış uzunluğa göre, aynı uzunluktaki anahtarlar için sözlüksel olarak sıralayın.

SealedQub (sürüm 0x01, herkese açık, alıcı yok):

"title"             (6 encoded bytes)   ← only if present
"qub_id"            (7 encoded bytes)
"version"           (8 encoded bytes)
"unlock_at"         (10 encoded bytes)
"outcome_at"        (11 encoded bytes)  ← only if present (V1.1 verdict mechanic)
"visibility"        (11 encoded bytes)
"drand_round"       (12 encoded bytes)
"drand_chain_id"    (15 encoded bytes)
"recipient_pubkey"  (17 encoded bytes)  ← only if present
"tlock_ciphertext"  (17 encoded bytes)

PactTerms (pakt gövdesi, 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 (terms dizisinin satırı):

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

PartyIdentifier (party_a / party_b haritası):

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

3.3 Bayt kodlama referansı

Tür CBOR kodlaması Örnek
SHA3-256 özeti (32 bayt) 0x58 0x20 + 32 bayt body_hash, qub_id
Zaman damgaları (i64) Ana tür 0 (pozitif) veya 1 (negatif), en kısa kodlama Unix saniyeleri
Sürüm (u8, değer 1) 0x01 (tek bayt)
İçerik türü (u8, değer 1) 0x01 (tek bayt)
sig_alg (u8, değer 0) 0x00 (tek bayt)
ML-DSA-65 imzası (3.309 bayt) 0x59 0x0C 0xED + 3.309 bayt author_signature, cosigner_signature
ML-DSA-65 açık anahtarı (1.952 bayt) 0x59 0x07 0xA0 + 1.952 bayt author_pubkey, cosigner_pubkey

4. Normatif türetmeler

4.1 qub_id

qub_id, bir qub'u benzersiz şekilde tanımlar ve QubEnvelope'u SealedQub'a bağlar. Zarf içeriğinden deterministik olarak türetilir.

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

Etki alanı ayırıcı kodlaması: "QUB_ID_V2" dizesi 9 ASCII bayttır. Hizalama için 10 bayta ulaşmak amacıyla tek bir 0x00 dolgu baytı eklenir. Uygulamalar tam olarak bu 10 baytı kullanmak ZORUNDADIR: [0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00].

outcome_at kodlaması: V1.1, isteğe bağlı outcome_at alanını bağlamaya katmak için ön görüntüyü 92 bayttan 100 bayta genişletti. Yok olan outcome_at, 8 sıfır bayt olarak kodlanır; protokol doğrulayıcıları her yerde outcome_at <= 0 değerini reddeder, böylece bu nöbetçi değer meşru bir değerle çakışamaz. Bkz. §3.2 (aktarım biçimi) ve bu alanı motive eden hüküm mekaniği için ağaç içi tasks/verdict-uplift-plan.md.

drand_round kodlaması: V1.2, drand_round'u (hedef drand turu, §4.3) bağlamaya katmak için ön görüntüyü 100 bayttan 108 bayta genişletti ve etki alanı ayırıcısını QUB_ID_V2'ye yükseltti. Bu, zaman kilidi turunu qub kimliğine bağlar: bir ağ geçidi, şifreli metni gösterilen unlock_at'ın ima ettiğinden farklı bir tura (örneğin halihazırda geçmiş bir tura) yeniden bağlayamaz. Açılma yordamı (§8) ayrıca tlock şifreli metin stanzasına gömülü turun unlock_round(unlock_at) ile eşleştiğini doğrular, böylece gösterilen açılma zamanının, şifre çözmeyi kapılayan tur olduğu kanıtlanabilir.

Özellikler:

4.2 body_hash

body_hash = SHA3-256(body)

Burada body, ham Vec<u8> içerik yüküdür. Metin qub'ları için bu, UTF-8 kodlamalı qub gövdesidir.

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

Burada title, açıklama öncesinde izleyici geri sayımında gösterilen isteğe bağlı düz metin başlığıdır (bkz. §3.2). NFC normalleştirme, özetin görsel olarak eşdeğer kod-nokta dizileri arasında kararlı olması için özet zamanında çalışır. Tümü sıfır olan işaretçi, yok durumu için ayrılmıştır; boş bir dize, kanonik CBOR sınırında "yok"un kanonik olmayan kodlaması olarak reddedilir (kanonik kodlama alanı tamamen çıkarır).

4.3 Açılma turu eşleştirmesi

drand_round = ceil((unlock_at - chain_genesis_time) / chain_period_seconds)
Parametre Kaynak Örnek
unlock_at Kullanıcı tarafından seçilen UTC Unix saniyeleri 1735689600 (2025-01-01 00:00:00 UTC)
chain_genesis_time drand zincir bilgisi (genesis_time) 1595431050
chain_period_seconds drand zincir bilgisi (period) 30

ceil() işlemi, açıklama zamanı ≥ unlock_at olan ilk drand turunu seçer. Bu, qub'un seçilen açılma zamanından önce şifresinin çözülememesini sağlar.

Sınır durumu: (unlock_at - chain_genesis_time) tam olarak chain_period_seconds'a bölünebiliyorsa, sonuç tam olarak o turdur — qub o turun açıklama zamanında tam olarak açılır.

Doğrulama: unlock_at, mühürleme anında gelecekte olmak ZORUNDADIR. unlock_at, created_at'tan en fazla 10 yıl uzakta olmak ZORUNDADIR (uzun ufuklu drand bağımlılık riskini sınırlamak için; arayüz 2 yılın ötesindeki açılma tarihleri için uyarı vermelidir — SHOULD).


5. Aktarım biçimi yeni tipleri

Aktarım biçimi yeni tipleri (newtypes), CBOR baytlarını JSON, ham düz metin veya diğer bayt kodlamalarıyla karıştırmaya karşı derleme zamanı güvenliği sağlar.

Tür İçerir Üreten Tüketen
SealedQubCbor SealedQub'ın kanonik CBOR'u serialize_sealed_qub() Kalıcı depolama yüklemesi, izleyici alımı
QubEnvelopeCbor QubEnvelope'un kanonik CBOR'u serialize_qub_envelope() tlock şifreleme girdisi, tlock şifre çözme çıktısı

5.1 İnşa kuralları

// 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 İnşada doğrulama

from_encoded(), çift ayrıştırmayı önlemek için girdinin geçerli bir CBOR harita başlığıyla başladığını doğrulamalıdır (SHOULD). Tam yapısal doğrulama inşa zamanında değil, ayrıştırma zamanında gerçekleşir.


6. İçerik türü kayıt defteri

Değer Tür Maksimum gövde boyutu Notlar
0x00 Ayrılmış (geçersiz) Kullanılmamalıdır (MUST NOT)
0x01 Düz metin (UTF-8, kısıtlı Markdown) 50 KB ücretli / 10 KB ücretsiz Sunum kuralları için bkz. §10. Ücretsiz / ücretli ayrımı yükleme hizmeti tarafından uygulanır; protokol katmanı kesin tavanı 50 KB'dir.
0x02 Ayrılmış (gelecek) Gelecekteki bir içerik türü için ayrılmıştır; v1'de geçerli değildir. İzleyiciler aşağıdaki kural uyarınca reddetmek ZORUNDADIR.
0x03 Pakt (ikili anlaşma, CBOR gövdesi) 100 KB Gövde kanonik CBOR PactTerms'tür (§6.1). §9.7 uyarınca birlikte imzalayan imzalama.
0x04 Karar (yaratıcı öz-değerlendirmesi, CBOR gövdesi) 8 KB Gövde kanonik CBOR VerdictBody'dir (§6.2). Yalnızca sistem tarafı verdict niyeti tarafından yayılır. Üst ilişki, gövdede değil, Parent-Tx-Id Arweave etiketindedir. Bkz. verdict-uplift-plan §3.4.

İzleyiciler bilinmeyen içerik türlerini açık bir kullanıcıya görünür hata ile reddetmek ZORUNDADIR. İzleyiciler bilinmeyen türleri metin olarak sunmaya çalışMAMALIDIR.

6.1 Pakt gövdesi (content_type = 0x03)

Bir pakt gövdesi, bir PactTerms değerinin kanonik CBOR kodlamasıdır:

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

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

Üç harita için kanonik CBOR anahtar sıraları §3.2'de verilmiştir. Toplam serileştirilmiş pakt CBOR 100 KB'yi aşMAMALIDIR (§6 ile eşleşir).

Şema ayırt edicisi. Bir structured/v1 paktında terms'teki ilk satır { key: "pact_schema", value: "structured/v1" } OLMALIDIR. Bu işareti olmayan satırlar "özel" paktlardır ve yapılandırılmış doğrulama veya şemaya duyarlı sunum almazlar.

Donmuş onay yuvaları. structured/v1 paktları bu anahtarlar altında tam olarak dört onay satırı taşır:

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

Her birinin value'su, (role, kind) çifti ile seçilen sekiz donmuş İngilizce dizeden biridir; burada role ∈ { seller, buyer, provider, client } ve kind ∈ { standard, capacity }. Dizelerin kendisi normatif protokol verisidir — her iki tarafın ML-DSA-65 imzaları, body_hash aracılığıyla tam baytlara taahhüt eder. Bunlar yerelleştirilMEZ; imzalanan gövde dilden bağımsızdır. Herhangi bir ifade değişikliği yeni bir şema sürümü gerektirir (structured/v2).

Sekiz dize, aramaları (acknowledgement_for(role, kind)) ve her birinin gerekçesi referans uygulama tarafından sabitlenir. Uyumlu uygulamalar bayt-aynı onay değerleri yaymak ZORUNDADIR; dört rol kombinasyonunun tamamını kapsayan altın-fikstür SHA3-256 gövde-özeti testleri herhangi bir kaymayı yakalar.

İzleyici görüntüleme sırası. Onay dizeleri, açıklama / kapsam satırlarının onaylardan önce sunulduğunu varsayan "yukarıda açıklandığı gibi" ("described above") gibi ifadeler içerir. İzleyiciler terms dizisini CBOR sırasında sunmak ZORUNDADIR; yeniden sıralama metnin anlamsal bütünlüğünü bozar.

Karşı taraf iletişimi. Taraf B'nin contact bilgisi geçerli bir e-posta adresi olduğunda, qub yükleme hizmeti, hazırlama anında otomatik olarak bir inceleme / birlikte imzalama davet e-postası gönderir ve nihai birlikte imzayı aynı adresin doğrulamasına bağlar (§9.7). Taraf B iletişimi olmayan paktlar yine de birlikte imzalanabilir, ancak yalnızca bant dışı bir kanal aracılığıyla — hizmet, eşleşen bir 15 dakikalık e-posta doğrulama işaretçisi üretemeyen birlikte imzalama isteklerini reddeder.

6.2 Karar gövdesi (content_type = 0x04)

Bir karar gövdesi, bir VerdictBody değerinin kanonik CBOR kodlamasıdır:

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
}

Kanonik CBOR anahtar sırası:

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

Toplam serileştirilmiş karar CBOR'u 8 KB'yi aşMAMALIDIR (yukarıdaki kayıt defteri satırıyla eşleşir).

Sonuç sıralı türü (enum). Tel baytı niyetten bağımsızdır; dört kategori — Right / Partial / Wrong / Unfalsifiable — karar taşıyan her niyetin sonuç uzayını kapsar. Niyete özgü etiketler (Right için "Bildim" / "Tuttum" / "Yayımlandı" / "Doğrulandı" gibi) izleyici tarafı sunum işidir ve üst qub'un niyetine göre çözümlenir — tel, dilden ve niyetten bağımsız kalır. 1..=4 dışındaki değerler kod çözmede reddedilmek ZORUNDADIR.

Üst bağlantısı. Bir karar qub'u, üst referansını gövdesinde taşıMAZ. Üst qub'un Arweave işlem kimliği, yükleme anında Parent-Tx-Id depolama etiketi olarak yayılır (§7 depolama-etiketi katmanı). Bu, gövdeyi kendi kendine yeten, imzalı bir öz-değerlendirme bildirimi olarak tutar; denetim zinciri ("ne hakkında haklı?") Arweave-etiketi araması üzerinden kurulur.

Kanıt URL güvenliği (normatif). evidence_url mevcut olduğunda, doğrulayıcılar (hazırlama tarafı, tel tarafı, Worker kenarı) şunları uygulamak ZORUNDADIR:

  1. Yalnızca HTTPS. Dize, https:// bayt dizisiyle başlamak ZORUNDADIR. Başka herhangi bir şema — http, ftp, javascript, data, file vb. — reddedilir.
  2. Uzunluk üst sınırı. ≤ 2.048 bayt (tarayıcı URL pratik sınırı).
  3. NFC + düşmanca kod noktası denetimi. title ve reflection ile aynı kural — bidi-override / sıfır genişlik / etiket bloğu / BOM / C0 / C1 kod noktaları reddedilir. Tanım, Rust crate::handle::contains_hostile_text_codepoint ve TS workers/api/src/utils/unicode.ts::isHostileCodepoint ile eşleşir (üçü birlikte ilerletilmek ZORUNDADIR).
  4. Boşluk yok, ASCII denetim karakteri yok. URL'nin herhangi bir yerindeki boşluk / DEL / 0x20 altındaki baytlar reddedilir — bidi kuralının kapsamadığı \n/\t enjeksiyon vektörünü kapatır.
  5. Boş olmayan ana bilgisayar bölümü. https:// ile ilk /, ? veya # arasındaki her şey boş olmayan ZORUNDADIR.

Sunucu tarafında getirme yok. Worker, URL'yi vekille çekMEMELİ, getirMEMELİ ya da önizleMEMELİDİR. Protokol bir dize saklar; sunum, rel="nofollow noopener noreferrer" target="_blank" ile izleyici tarafında gerçekleşir ve bağlantı metniyle birlikte görünür bir ana bilgisayar gösterilir.

Düşünce. İsteğe bağlı, yaratıcının yazdığı düşünce metni ("ne değişti, ne öğrendin"). title ile aynı NFC + düşmanca kod noktası doğrulaması. Boş / yalnızca boşluk içeren girdi, oluşturma anında yok sayılır.

Şema sürümü. v1 yalnızca verdict_version = 0x01'i destekler. Gelecekteki şema güncellemeleri bu baytı artırır ve §12 uyarınca yeni bir protokol sürümüyle birlikte iner.


7. Mühürleme protokolü

Tam mühürleme dizisi. Her adım normatiftir.

 1. User composes plaintext and metadata in ComposeQub.
 2. Validate:
    a. body is non-empty.
    b. body size ≤ max for content_type and user tier (see §6).
    c. unlock_at is in the future.
    d. unlock_at ≤ created_at + 10 years.
    e. content_type is a known, supported value.
 3. Compute body_hash = SHA3-256(body).
 4. Set created_at = current Unix seconds UTC.
 5. Select drand chain. Load chain_genesis_time and chain_period_seconds, and
    compute drand_round = ceil((unlock_at - chain_genesis_time) / chain_period_seconds).
    (Computed here, before qub_id, because drand_round is bound into the qub_id
    preimage — §4.1, V1.2.)
 6. Compute qub_id (see §4.1), folding in drand_round from step 5.
 7. Construct QubEnvelope with all fields.
 8. Serialise QubEnvelope using canonical CBOR → bytes B.
    Assert: serialised output matches canonical profile (§3).
 9. Compute C = tlock_encrypt(B, drand_round, drand_chain_public_key).
10. Construct SealedQub with tlock_ciphertext = C, and matching qub_id, version,
    unlock_at, drand_chain_id, drand_round.
12. Serialise SealedQub using canonical CBOR → SealedQubCbor.
12a. Generate K = 32 random bytes (CSPRNG) and N = 12 random bytes (CSPRNG).
     Compute W = wrap_sealed_qub(SealedQubCbor, qub_id=qub_id, key=K, nonce=N)
     per §13. The bytes uploaded to permanent storage are the OuterWrapper CBOR W,
     never the bare SealedQubCbor. K leaves the device only as the URL
     fragment in step 16.
13. Display seal-time disclosure. User confirms.
14. Validate upload eligibility via the qub upload service (bot-detection, entitlement, rate limits).
15. Submit W (the OuterWrapper bytes) to the qub upload service; the service
    signs and uploads to permanent storage. The service is byte-blind to the inner
    SealedQubCbor and never receives K.
16. Receive arweave_tx_id from the service. Construct delivery URL as
    `<origin>/c/<arweave_tx_id>#<base64url(K)>` (or `<origin>/s/<short_code>#<base64url(K)>`
    when a short code is allocated). Browsers do not transmit URL fragments
    to servers, so K is never observed by qub.social or any storage gateway.

Depolama etiket katmanı (bant dışı). qub yükleme hizmeti, sarmalanmış yüke ek olarak kasıtlı olarak küçük bir depolama işlem etiketi seti ekler. Content-Type=application/octet-stream normatif olarak gereklidir. Referans hizmet ayrıca, yaratıcı bunları yüzeye çıkarmayı seçtiğinde üç isteğe bağlı etiket ekler: Intent (izin listesi doğrulamalı oluşturma niyeti — örneğin quote, reply, commitment), Author (yaratıcının §9.3 açık anahtar parmak izi 64 karakterli küçük harf onaltılık olarak) ve Parent-Tx-Id (yanıt zincirleri için üst qub'un depolama işlem kimliği, 43 karakterli base64url).

Author etiketi qub başına opsiyoneldir: referans yaratıcı uygulaması bunu yalnızca kullanıcı mühürleme anında kamu atıfını açıkça etkinleştirdiğinde ekler. Geçiş kapalıyken — varsayılan — hiçbir Author etiketi yazılmaz ve qub zincirde atfedilmemiştir: kalıcı depolamadaki hiçbir şey yüklemeyi bir yaratıcının kullanıcı adına, e-postasına veya diğer qub'larına bağlamaz. Geçiş açıkken, Author parmak izi §9.5 onay zinciri aracılığıyla yaratıcının seçtiği @handle'a çözümlenir. Yanıt zinciri ilişkileri ve Intent tanımlayıcı değildir. Dış sarmalayıcı (§13), iç gövdeyi şifreli metin korelasyonundan korur — bir hasatçının drand turu yayınlandıktan sonra qub-şekilli yüklemeleri tanımasını ve toplu olarak şifresini çözmesini engeller.

Referans hizmet kasıtlı olarak App-Name, App-Version veya Type etiketlerini eklemez: bu tür herhangi bir tek değerli filtre, bir GraphQL sorgusuna tüm qub corpus'unu döndürür ki bu, sarmalayıcının yalnızca-gövde gizlilik kapsamıyla tutarsızdır.

Uyumlu bir doğrulayıcı, §11 üçüncü taraf doğrulaması için herhangi bir depolama etiketine bağımlı olMAMALIDIR; gövde özeti / qub_id / imza yalnızca iç CBOR'a taahhüt eder, asla etiket setine değil.


8. Açma protokolü

Tam açma dizisi. Her adım normatiftir.

 1. Viewer opens delivery URL. Extract arweave_tx_id from path AND
    K = base64url_decode(fragment) from the URL fragment. If the fragment
    is absent or malformed → display "this URL is missing its decryption
    key" and stop; the viewer MUST NOT contact the storage gateway
    without K, since fetching wrapped bytes the viewer cannot decrypt
    serves no purpose and only leaks the access attempt.
 2. Check denylist. If tx_id is denylisted → display block message. Stop.
 3. Fetch OuterWrapper bytes from permanent storage (with multi-gateway fallback).
 3a. Unwrap: parse the bytes as OuterWrapper (§13), verify the wrapper
    `version` byte is `0x01`, and compute SealedQubCbor =
    unwrap_sealed_qub(OuterWrapper, key=K). Any AEAD authentication
    failure (wrong K, tampered ciphertext, swapped qub_id-as-AAD,
    swapped nonce) → display "this URL's decryption key does not match
    the stored qub" and stop. Authentication failures are
    indistinguishable to the viewer per §13.5.
 4. Parse SealedQubCbor → SealedQub.
 5. Validate: SealedQub.version is known (0x01). Reject unknown versions.
 6. If current time < SealedQub.unlock_at → display countdown. Poll or wait.
 6a. Round-binding check (V1.2). Recompute expected_round =
    ceil((SealedQub.unlock_at - chain_genesis_time) / chain_period_seconds).
    Reject unless SealedQub.drand_round == expected_round AND the round baked
    into the tlock ciphertext stanza (read via the age/tlock header, no signature
    required) == expected_round. The stanza round is the one that actually gates
    decryption; without this check a malicious creator could bind the ciphertext
    to an already-past round while displaying a future countdown, so anyone
    reading the stored bytes could decrypt before unlock_at. Implementations with
    no chain identity (test mocks) skip this check.
 7. Once current time ≥ SealedQub.unlock_at:
    a. Fetch drand round signature for SealedQub.drand_round from drand network.
    b. Compute B = tlock_decrypt(SealedQub.tlock_ciphertext, round_signature).
 8. Parse B → QubEnvelope.
 9. Validate QubEnvelope.version is known.
10. Verify: SHA3-256(QubEnvelope.body) == QubEnvelope.body_hash.
    Fail → integrity error.
11. Verify: QubEnvelope.qub_id == SealedQub.qub_id.
    Fail → integrity error.
12. Verify: QubEnvelope.unlock_at == SealedQub.unlock_at.
    Fail → integrity error.
13. Verify: QubEnvelope.content_type is known and renderable.
    Known values: 0x01 (text), 0x03 (pact). Unknown → display error.
14. If QubEnvelope.sig_alg != 0x00 → verify author signature (see §9.4).
15. If cosigner_pubkey or cosigner_signature present → verify cosigner (see §9.7).
16. Render content using appropriate renderer (see §10 for text, §6 for pact).
17. Construct RevealedQub for display.

9. Yazarlık imzalama

9.1 Gerekçe

qub'lar kalıcı depolamada saklanır. Yazarlık imzaları süresiz olarak sahteciliğe karşı dirençli kalmalıdır, bu nedenle v1.0, güvenliği qub'un kalıcı yaşam süresi içinde bozulabilecek klasik bir şema yerine post-kuantum ML-DSA-65 şemasını (FIPS 204) kullanır.

9.2 Algoritma kayıt defteri

sig_alg Şema Anahtar boyutu İmza boyutu
0x00 İmza yok (imzasız)
0x01 ML-DSA-65 (FIPS 204) 1.952 bayt 3.309 bayt

İzleyiciler bilinmeyen sig_alg değerlerini reddetmek ZORUNDADIR.

9.3 İmzalı ön görüntü inşası

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

signature = Sign(author_secret_key, sig_input)

Etki alanı ayırıcı: "QUB_AUTHOR_SIG_V1" 17 ASCII bayttır: [0x51, 0x55, 0x42, 0x5F, 0x41, 0x55, 0x54, 0x48, 0x4F, 0x52, 0x5F, 0x53, 0x49, 0x47, 0x5F, 0x56, 0x31]. Dolgu yok.

Son bayt: 91. ön görüntü baytı 0x00 OLMALIDIR. Referans uygulama bunu crates/qub-core/src/signing.rs içinde ORG_ID_PRESENT_INDIVIDUAL = 0x00 sabiti olarak sunar; doğrulama için sig_input'u yeniden oluşturan izleyiciler aynı baytı yaymak ZORUNDADIR.

İmza kapsamı — neyi kapsar ve neyi kapsamaz. sig_input, dört zarf alanına taahhüt eder: version, qub_id, body_hash, unlock_at (artı sabit etki alanı ayırıcısı ve org_id_present baytı). Bu dördünden üçü yapısal değişmezlerdir: qub_id'nin kendisi §4.1 ön görüntüsü aracılığıyla version, content_type, created_at, unlock_at, outcome_at, drand_round ve body_hash'ten türetilir, bu nedenle bu alanlarda yapılan herhangi bir değişiklik farklı bir qub_id üretir ve imzayı geçişli olarak geçersiz kılar. Bu nedenle doğrudan kimliği doğrulanmış yüzey şudur:

Alan İmza ile kimliği doğrulanır Nasıl
version sig_input'a doğrudan girdi
qub_id Doğrudan girdi
body_hash Doğrudan girdi
unlock_at Doğrudan girdi
content_type qub_id ön görüntüsü aracılığıyla geçişli olarak
created_at qub_id ön görüntüsü aracılığıyla geçişli olarak
outcome_at qub_id ön görüntüsü aracılığıyla geçişli olarak
drand_round qub_id ön görüntüsü aracılığıyla geçişli olarak (V1.2)
body body_hash = SHA3-256(body) aracılığıyla geçişli olarak
author_pubkey — (örtük) İmzayı doğrulayan anahtar, tanım gereği yazardır
sender_label Yalnızca görüntüleme metni; imza bozulmadan değiştirilebilir
reply_to İş parçacığı işaretçisi; imza bozulmadan değiştirilebilir
cosigner_pubkey / cosigner_signature Aynı sig_input üzerinde bağımsız olarak imzalanır (bkz. §9.7)
drand_chain_id, tlock_ciphertext, visibility Dış SealedQub alanları, zarfın içinde değil — kendi yapısal değişmezleri (tur / zincir tutarlılığı) tarafından kapsanır ancak yazar imzası tarafından kapsanmaz. (drand_round artık qub_id ön görüntüsü aracılığıyla geçişli olarak bağlanır — yukarıya bakın.)

Kimliği doğrulanmamış alanların güvenlik etkileri.

sender_label veya reply_to'yu son kullanıcılara gösteren uygulamalar, kimliği doğrulanmış kimliği (açık anahtar parmak izi, onay) etiket yerine birincil kimlik sinyali olarak yüzeye çıkarmak ZORUNDADIR.

9.4 Doğrulama prosedürü

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 (same formula as §9.3).
6. Verify(author_pubkey, sig_input, author_signature).
7. If verification succeeds → display "signed by [key fingerprint]."
8. If verification fails → display "signature verification failed."

İmza doğrulama en pahalı işlemdir (özellikle ML-DSA-65). Daha ucuz tüm kontroller (özet, qub_id, unlock_at) geçtikten sonra yapılmalıdır (SHOULD).

9.5 Kimlik onayları

Kimlik onayları — author_pubkey'in qub kullanıcı adı, e-posta adresi, sosyal kullanıcı adı veya parola anahtarı kimlik bilgisi gibi insan tarafından tanınabilen kimlik iddialarına eşlenmesi — izleyici tarafında aşamalı bir geliştirmedir ve imza doğrulaması için gerekli değildir. Onayları bir görüntüleme kimliğine çözen izleyiciler şu önceliği uygulamak ZORUNDADIR:

handle > email > social > fingerprint

Parmak izi yedeği, SHA3-256(author_pubkey)'in küçük harf onaltılığıdır; herhangi bir imzalı qub için her zaman kullanılabilir. İzleyiciler bunu görüntüleme için kısaltABİLİR — referans izleyici, qub: ardından ilk ve son dört baytı sunar (qub:<8 hex>…<8 hex>).

Uyumlu bir doğrulayıcı, qub API'sine başvurmadan, kalıcı depolama ve drand'in ötesinde herhangi bir ağ olmadan ve herhangi bir sunucu tarafı araması olmadan §9.4'teki her kontrolü tamamlayabilir. Onay çözümleme, yalnızca imza doğrulaması başarılı olduktan sonra gerçekleştirilen ayrı bir en iyi çaba adımıdır.

9.6 Boyut etkisi

Ed25519 ML-DSA-65
İmza 64 bayt 3.309 bayt
Açık anahtar 32 bayt 1.952 bayt
qub başına toplam 96 bayt 5.261 bayt
Depolama maliyet farkı (~5$/MB ile) ~0,0005$ ~0,026$

500–2.000 bayt'lık bir metin qub için ML-DSA-65 depolanan boyutu kabaca üç katına çıkarır. Mutlak maliyet ihmal edilebilir düzeydedir.

9.7 Birlikte imzalayan doğrulaması (Pakt ikili anlaşmaları)

İkili anlaşmalar için (content_type = 0x03), ikinci bir imza katmanı her iki tarafın da aynı koşullara onay verdiğini kanıtlar.

Zarf alanları:

Her iki alan da birlikte mevcut olmak veya her ikisi de yok olmak ZORUNDADIR. Yalnızca biri mevcutsa, izleyiciler bir bütünlük hatası bildirmek ZORUNDADIR.

Doğrulama prosedürü:

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.
5. Verify(cosigner_pubkey, sig_input, cosigner_signature).
6. Success → display "co-signed by [cosigner fingerprint]."
7. Failure → display "co-signature verification failed."

Özellikler:

E-posta bağlama kapısı (operasyonel). Hazırlanan bir pakt bir Taraf B e-posta iletişimi taşıdığında (§6.1), qub yükleme hizmeti, hem hazırlama kimliği hem de bu iletişimin normalleştirilmiş e-posta özeti ile eşleşen kısa ömürlü bir e-posta doğrulama işaretçisi mevcut olmadıkça birlikte imzalama isteğini reddetmek ZORUNDADIR. İşaretçi, sihirli bağlantı belirteci bir staging_id taşıdığında ve doğrulanmış adres SHA-256(normalise_email(party_b.contact)) ile eşleştiğinde /api/v1/auth/verify tarafından yazılır — burada normalise_email(addr) yerel bölümün büyük/küçük harfini korur ve yalnızca etki alanı bölümünü küçük harfe çevirir (RFC 5321 §2.3.11 uyarınca) ve buradaki SHA-256, NIST FIPS 180-4 özetidir (§4 türetmelerinde kullanılan SHA3-256'dan farklı) — ve verildikten 900 saniye (15 dakika) sonra sona erer. Bu operasyonel bir kimlik taklidine karşı kapıdır, zincir üzerindeki qub kanıtının bir parçası DEĞİLDİR — §11'i yeniden oynayan üçüncü taraf bir doğrulayıcının yalnızca kalıcı depolama ve drand'a ihtiyacı vardır, herhangi bir sunucu tarafı araması olmadan. İşaretçi yalnızca sunucu tarafında bulunur ve asla imzalanmış gövdenin bir parçası değildir.

Boyut etkisi (ML-DSA-65 yazar + birlikte imzalayan):

Bileşen Boyut
Yazar imzası 3.309 bayt
Yazar açık anahtarı 1.952 bayt
Birlikte imzalayan imzası 3.309 bayt
Birlikte imzalayan açık anahtarı 1.952 bayt
Toplam kripto ek yükü 10.522 bayt
Depolama maliyet farkı ~0,05$

10. Markdown sunumu ve temizleme

Bu bölüm güvenlik açısından kritiktir. İzleyici, metin qub'larını (content_type = 0x01) kısıtlı bir Markdown alt kümesi kullanarak sunar.

10.1 İzin verilen öğeler

10.2 Yasaklanan öğeler

Öğe İşleme
Ham HTML (<div>, <script>, vb.) Tamamen çıkarılır. HTML'den hiçbiri geçmez.
Görseller (![alt](url)) Çıkarılır. Görsel söz dizimi çıktıdan kaldırılır.
Bağlantılar ([text](url)) URL görünür düz metin olarak sunulur. Otomatik bağlantılanmaz. Açık kullanıcı eylemi olmadan tıklanabilir değildir.
Tehlikeli URL şemaları javascript:, data:, vbscript:, file: — çıkarılır.
iframe'ler, gömülenler, nesneler Çıkarılır.
HTML varlıkları Yalnızca güvenliyse görüntüleme karakterlerine kod çözülür.

10.3 Uygulama

Uygulamalar bir kara liste değil, katı bir izin listesi ayrıştırıcısı kullanmak ZORUNDADIR. Önerilen yaklaşım:

  1. Markdown'ı pulldown-cmark (veya eşdeğeri) kullanarak ayrıştırın.
  2. AST'de gezinin ve izin listesinde olmayan herhangi bir düğümü atın (§10.1).
  3. Bağlantı düğümleri için: URL'yi tıklanabilir bir <a> öğesi olarak değil, görünür metin olarak yayın.
  4. Filtrelenmiş AST'yi tipli bir ara temsile dönüştürün (örneğin, yalnızca güvenli varyantları olan bir MarkdownNode enum'u). Bu IR'de ham HTML yapısal olarak temsil edilemez.
  5. Tipli IR'den hedef görünüm katmanına (örneğin, reaktif görünüm bileşenleri, DOM düğümleri) sunum yapın. Hiçbir noktada HTML dize birleştirme veya innerHTML kullanılmaz.

Kara liste yaklaşımları kırılgandır çünkü yeni Markdown uzantıları veya ayrıştırıcı tuhaflıkları filtrelenmemiş öğeler getirebilir. Tipli AST yaklaşımı, XSS'yi yapısal olarak imkansız hale getirir — keyfi HTML taşıyabilecek bir varyant yoktur.

10.4 Boyut ve yapı sınırları


11. Üçüncü taraf doğrulaması

Herhangi bir üçüncü taraf, qub işbirliği olmadan bir kamu qub'unu doğrulayabilir. Doğrulama prosedürü:

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

Doğrulamanın kanıtladıkları:

Kanıt Ne kurar
Taahhüt Şifreli metin depolama blok zaman damgasına kadar var olmuştur.
Bütünlük Düz metin gövdesi taahhüt edilen özetle eşleşir ve değiştirilmemiştir.
Zamanlama İçerik, seçilen açılma zamanına karşılık gelen drand turuna kadar okunamazdı (tlock ve drand güvenlik varsayımlarına tabidir).

Doğrulamanın kanıtlaMADIKLARI:

Kanıtlanamayan Neden
Yazarlık sender_label dekoratiftir. sig_alg0x01 olmadan, bu içeriği herhangi biri mühürlemiş olabilir.
Niyet qub içeriği ve zamanlamayı kanıtlar, yaratıcının öznel olarak ne demek istediğini değil.
Olay öncesi zamanlama Depolama blok dahil edilmesi gerçek yüklemeden dakikalarca gecikebilir. Taahhüt zaman damgası blok zamanıdır, kullanıcının "mühürle"ye bastığı an değildir.

12. Sürümleme

12.1 Protokol sürümü

Hem SealedQub hem de QubEnvelope içindeki version alanı (u8), birincil protokol sürümünü tanımlar.

12.2 Sürüm geçmişi

Sürüm Değer Açıklama
v1 0x01 Kamu metin qub'ları (content_type 0x01), pakt ikili anlaşmaları (0x03, structured/v1 şeması, ML-DSA-65 yazar + birlikte imzalayan), tlock, SHA3-256

12.3 İleriye dönük uyumluluk

Bilinmeyen isteğe bağlı CBOR harita anahtarlarına (§3.2 kanonik sırasında olmayan anahtarlar) sahip bir QubEnvelope ile karşılaşan bir v1 izleyicisi, bu anahtarları yok saymalı ve bilinen alanları kullanarak doğrulamaya devam etmelidir (SHOULD). Bu, büyük sürüm yükseltmesi gerektirmeden gelecekteki küçük eklemelere (örneğin yeni meta veriler) izin verir.

sig_alg = 0x01 (ML-DSA-65) ile karşılaşan ancak ML-DSA-65 doğrulama desteğinden yoksun bir v1 izleyicisi, qub'u tamamen reddetmek yerine "imza mevcut ancak doğrulanamaz" notu ile qub içeriğini görüntülemelidir (SHOULD). Bugünkü referans uygulama, 0x00 ve 0x01 dışındaki her sig_alg değerini reddeder çünkü v1 kayıt defterinde başka geçerli algoritma yoktur — üçüncü bir algoritma kaydedilene kadar katı reddetme ve yumuşak hata gözlemsel olarak aynıdır. Yukarıdaki yumuşak hata davranışı, §9.2 yeni bir giriş kabul ettikten sonra yük taşır hale gelir ve referans izleyici o noktada yumuşak hataya güncellenecektir.

12.4 Dış sarmalayıcı sürümü

§13'te açıklanan OuterWrapper, SealedQub.version ve QubEnvelope.version'dan bağımsız olarak kendi version baytını taşır. İki sürüm alanı ayrı ayrı evrimleşir: gelecekteki post-kuantum-güvenli bir simetrik değiştirme, iç protokol sürümüne dokunmadan sarmalayıcı baytını yükseltir ve gelecekteki bir protokol katmanı eklemesi (örneğin yeni bir zarf alanı), sarmalayıcı baytına dokunmadan iç sürümü yükseltir.

OUTER_WRAPPER_VERSION_* Değer Algoritma Durum
OUTER_WRAPPER_VERSION_1 0x01 12-baytlık nonce, 16-baytlık kimlik doğrulama etiketi, qub_id'ye bağlı AAD ile AES-256-GCM v1 varsayılan
0x020xFF Ayrılmış Gelecek

İzleyiciler bilinmeyen sarmalayıcı sürümlerini açık bir hata ile reddetmek ZORUNDADIR. Protokol, somut bir geçiş itici gücü ortaya çıkana kadar (örneğin farklı bir AEAD'yi destekleyen NIST kılavuzu) sarmalayıcı sürüm alanını kasıtlı olarak dar tutar; bir 0x02 yuvası, algoritmayı tanıtan aynı revizyonda tahsis edilecektir.


13. Dış şifreleme sarmalayıcısı

13.1 Gerekçe

Protokol katmanları (QubEnvelope → tlock → SealedQub) mühürlü bir qub'u zamana kilitli yapar: gövde unlock_at'a ve drand tur imzası yayınlanana kadar okunamaz. Ancak açılmadan sonra, tur imzası kamuya açıktır ve SealedQub'un kanonik CBOR şekli tanınabilirdir, bu nedenle kalıcı depolama işlemlerini indeksleyen bir hasatçı, tüm qub corpus'unu toplu olarak şifresini çözebilir.

Dış şifreleme sarmalayıcısı, kanonik SealedQubCbor ile kalıcı depolamaya yazılan baytlar arasına ek bir simetrik AEAD katmanı yerleştirerek bu kanalı kapatır. 256-bitlik anahtar K, yalnızca teslim URL'sinin URL parçasında ve kullanıcı cihazlarında bulunur; tarayıcılar URL parçalarını sunuculara iletmez, bu nedenle qub.social, her depolama ağ geçidi ve her ikisinin önündeki her CDN, K'ye gözlemsel olarak kördür. Bu nedenle kalıcı depolamadaki her qub, düz metni yaratıcının paylaşmayı seçtiği URL olmadan kurtarılamayan opak bir şifreli metindir.

Net etki:

13.2 Katmanlama

plaintext body                       ← QubEnvelope.body (§2.2)
  ↓ canonical CBOR (§3)
envelope CBOR
  ↓ tlock encrypt to drand round (§7 step 10)
tlock_ciphertext (inside SealedQub) (§2.3)
  ↓ canonical CBOR (§3)
SealedQubCbor bytes                  ← inner wire artifact
  ↓ AES-256-GCM(K, nonce, AAD=qub_id) (§7 step 12a, this section)
OuterWrapper CBOR bytes              ← uploaded to permanent storage (§7 step 15)

Protokol katmanındaki mühürleme ve açma (§7, §8) sarmalayıcı sınırının altında değişmez; sarmalayıcı seal()'ın çağrı yerinde takılır ve unlock()'ın çağrı yerinde çıkarılır.

13.3 OuterWrapper veri yapısı

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

Alan değişmezleri.

CBOR kodlaması. §3 uyarınca kanonik CBOR, aynı anahtar sıralama kuralıyla (artan kodlanmış bayt uzunluğuna göre sıralı, sonra sözlüksel olarak). Dört anahtar şudur:

Anahtar Kodlanmış baytlar Sıra
nonce 6 1
qub_id 7 2
version 8 3
ciphertext 11 4

Bu nedenle OuterWrapper CBOR'unun ilk baytı, 4-girişli bir harita için kesin uzunluklu harita başlığıdır (0xA4).

13.4 qub_id'ye AAD bağlama

Sarmalayıcı, qub_id'yi AEAD ek kimliği doğrulanmış veri olarak bağlar. Bu, üç sınıf saldırıya karşı yük taşıyan yapısal savunmadır:

Saldırı Savunma
Şifreli metni sarmalayıcıdaki farklı bir qub_id alanı altında taşımak AAD uyuşmazlığı → AEAD kimlik doğrulaması başarısız olur
qub A'nın URL parçasını qub B'nin depolama baytları ile karıştırmak AAD uyuşmazlığı → AEAD kimlik doğrulaması başarısız olur
Yüklemeden sonra sarmalayıcının qub_id alanını kurcalamak AAD uyuşmazlığı → AEAD kimlik doğrulaması başarısız olur

qub_id'yi sarmalayıcı düz metninde taşımak, numaralandırma bağışıklığını anlamlı ölçüde zayıflatmaz — qub_id'nin kendisi §4.1 ön görüntüsünün özetten kurtarılamayan ön görüntüsüz bir SHA3-256 özetidir ve sarmalayıcı baytlarını zaten toplayan bir numaralandırıcı, görünür qub_id'den yüklemenin varlığından çıkarabileceğinden daha fazla bir şey öğrenmez.

13.5 Sarmalama ve sarmalama açma algoritmaları

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

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

Hata modu çöküşü. Yanlış K, yanlış nonce, AAD uyuşmazlığı ve kurcalanmış şifreli metin, hepsi aynı DECRYPT_FAILED hatasını üretir. Bu, kasıtlı bir AEAD özelliğidir: hata modunu ayırt etmek, uzaktan bir saldırganın hatalı biçimlendirilmiş sarmalayıcılar göndererek ve yanıtı zamanlayarak araştırabileceği bir yan kanal oluşturur. Referans uygulamalar tüm AEAD hatalarını tek bir hata şekline daraltmak ZORUNDADIR.

13.6 Anahtar materyali ve dağıtım

Sarmalama anahtarı K, bir CSPRNG tarafından qub başına üretilen 256-bitlik tek tip rastgele bir değerdir. Referans uygulamalar bunu şuradan alır:

Dağıtım: K, URL-güvenli base64 (RFC 4648 §5, dolgu yok) olarak kodlanmak ve teslim URL'sine parça bileşeni olarak eklenmek ZORUNDADIR:

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

Parça, uyumlu bir tarayıcı tarafından hiçbir sunucuya iletilmez. Tam teslim URL'sini — parça dahil — kullanıcının cihazının ötesinde kalıcı kılan kurtarma kanalları (sunucu tarafı geçmiş dizini, opt-in e-posta otomatik gönderme), varsayılan kripto-parçalama duruşuna karşı açık bir takastır ve açık kullanıcı onayına kapılı olmak ZORUNDADIR.

Parça kaybı. Bir kullanıcı URL parçasını kaybeder ve kurtarma kanalı yoksa, qub okunamaz. Bu, tasarımın yük taşıyan takasıdır ve mühürleme anında kullanıcıya açıklanmak ZORUNDADIR. MVP, mühürleme anı açıklamasını açık "bu URL'yi kaydedin" metni ve opt-in yapan kullanıcılar için doğrulanmış-e-posta kurtarma kanalı ile güçlendirir.

13.7 Bu bölümün kapsamı dışında

13.8 Kamu qub'ları (sarmalayıcı atlanması)

Dış sarmalayıcı, teslim katmanında isteğe bağlıdır. Bir yaratıcı bir qub'u kamu olarak mühürleyebilir; bu durumda kanonik SealedQubCbor, OuterWrapper katmanı ve K anahtarı olmadan kalıcı depolamaya doğrudan yazılır:

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

Bir kamu qub'u zamana kilitlidir ancak bağlantı kontrollü değildir: drand turu yayınlanana kadar okunamaz durumda kalır (tlock katmanı değişmez), ancak açılmadan sonra arweave_tx_id'ye sahip olan herkes onun şifresini çözebilir — hiçbir URL parçası gerekmez, çünkü bir K yoktur. Bu, sunucunun yürütmesi gereken yüzeyler için kasıtlı bir takastır: açılma bildirimi e-postaları, üçüncü taraf gömme öğeleri ve daha zengin açılma sonrası SEO'nun tümü, sunucunun asla elinde tutmadığı bir gizli değer olmadan çalışan bir bağlantıya ihtiyaç duyar (§13.6).

Bir üreticinin hesaba katmak ZORUNDA olduğu sonuçlar:

Özel (sarmalanmış) varsayılan olarak kalır; kamu, açık bir qub-başına yaratıcı seçimidir.


14. Test vektörleri

14.1 qub_id türetmesi

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

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

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

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

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

Uygulamalar bu girdi için aynı body_hash ve qub_id değerlerini üretmek ZORUNDADIR. Bu test vektörü yazılan ilk birim testi olmalıdır (SHOULD). Yukarıdaki kanonik değerler referans uygulama tarafından hesaplanmıştır ve bit-bit eşleşmek ZORUNDADIR. Tarihsel ön görüntü düzenleri (lansman öncesi — bu değerlere bağlı canlı qub yoktu): 92 baytlık V1.0 qub_id şuydu: 3d9fc2390eab043d38a1669ed3b71be76f9eefe872b9569ab1aaa027b88392b0; 100 baytlık V1.1 qub_id (outcome_at_or_zero katlandıktan sonra) şuydu: b0d032898ad629795150fdcb3f84e518f59ed05b7a2a82bc24ebdb87f52144ed. V1.2, drand_round'u katlar ve etki alanı ayırıcısını QUB_ID_V2'ye yükseltir.

14.2 Açılma turu eşleştirmesi

Input:
  unlock_at           = 1735689600
  chain_genesis_time  = 1595431050
  chain_period_seconds = 30

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

drand_round = 4675285

14.3 Kanonik CBOR gidiş-dönüşü

Uygulamalar, tüm geçerli girdiler için serialize(parse(serialize(qub))) == serialize(qub) olduğunu doğrulamak ZORUNDADIR. Bu tek bir vektör değil, bir özellik testidir.

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)

Kanonik CBOR baytları ve SHA3-256 body_hash, referans uygulama tarafından hesaplanır. Uygulamalar bu girdi için bayt-aynı CBOR üretmek ZORUNDADIR.

Uygulamalar ayrıca, tüm geçerli PactTerms girdileri için serialize(parse(serialize(pact))) == serialize(pact) olduğunu doğrulamak ZORUNDADIR (özellik testi).

14.5 Dış sarmalayıcı diller arası vektörler

Dış sarmalayıcı (§13), crates/qub-core/tests/vectors/wrapper_v1.json konumunda ayrı bir kanonik fikstüre sahiptir. Her vaka, opak onaltılık girdiler olarak bir (key, nonce, qub_id, sealed_cbor) demetini sabitler ve belirli bir expected_wrapper_hex çıktısını onaylar. Her iki referans uygulama da aynı JSON dosyasını tüketir:

Fikstür şu anda üç vakayı sabitler:

Vaka Kapsam
basic-text-public En küçük gerçekçi SealedQub şekli; isteğe bağlı alan yok. v1.0-tipik bir qub için kanonik sarmalayıcı şeklini kurar.
with-recipient-pubkey recipient_pubkey ayarlı SealedQub (Faz 2 yolu). Farklı iç CBOR anahtar seti, farklı qub_id.
longer-body ~4 KiB gövde — hem iç zarfta hem de dış şifreli metinde çok baytlı CBOR uzunluk öneklerini sınar.

Uygulamalar, kaydedilen girdiler için bayt-aynı expected_wrapper_hex üretmek ZORUNDADIR. Fikstürü yeniden oluşturmak QUB_REGEN_VECTORS=1 cargo test -p qub-core --test wrapper_vectors gerektirir ve kasıtlı biçim değişiklikleri için ayrılmıştır.


15. Kripto profil yönetişimi (Gelecek)

Bu bölüm v1 için bilgilendiricidir ve qub'un kriptografik ilkellerinden herhangi birine ikinci bir algoritma girdiği ilk anda normatif hale gelir.

15.1 Mevcut duruş

Protokol v1, ilkel başına tam olarak bir algoritmaya bağlanır:

Doğrulayıcılar şu anda ilkel başına anahtar ve imza uzunluklarını sabit kodlar. Aktarım biçimi tarafından çeviklik yüzeyi sunulmaz.

15.2 Hedeflenen şekil

Protokole ikinci bir algoritma girdiğinde, doğrulayıcı, ilkel başına izin verilen değerlerin tam setini — sig_alg'ler, drand zincirleri, sarmalayıcı sürümleri, içerik türleri — listeleyen adlandırılmış bir CryptoProfile (örneğin ExqubV1) için yapılandırılacaktır. Profil doğrulama zamanında sabitlenir, asla bant içinde müzakere edilmez. Aktif profilin dışındaki herhangi bir değer reddedilir.

Bu, ML-DSA-87 eklemenin veya Ed25519'u etkinleştirmenin mevcut doğrulayıcı yapılandırmalarını geriye dönük olarak zayıflatamamasını garanti eder: bir v1 doğrulayıcısı, bir v2 profili yayınlandıktan sonra bile bir v1 doğrulayıcısı olarak kalır.

15.3 Tetikleyici koşullar

§15'i şunlardan herhangi biri önerildiğinde normatif duruma yükseltin:

O zamana kadar §15, gelecekteki PR'ların müzakere yüzeyini sıfırdan yeniden tartışmak yerine bilinen bir hedefe karşı inmesi için geçiş şeklini sabitleyen bir yer tutucudur.