Spesifikasi Protokol qub
qub ialah protokol untuk komitmen temporal kriptografi: satu sistem untuk memeterai kata-kata kepada tarikh akan datang dan membuktikan, apabila tarikh itu tiba, apa sebenarnya yang telah dikatakan dan bila.
Tiga primitif menjadikannya berfungsi. drand ialah suar kerawakan ternyahpusat — tarikh pendedahan boleh dikuatkuasakan oleh fizik, bukan oleh niat baik mana-mana pihak. Storan awam kekal ialah simpanan awam tahan-usik — tiada pihak boleh menyunting atau memadam qub setelah ia dimeterai. ML-DSA-65 ialah tandatangan digital pasca-kuantum — setiap qub diikat kepada satu pasangan kunci yang rahsianya tidak pernah meninggalkan peranti pengarang.
Bersama-sama primitif ini menghasilkan satu kenyataan yang terkunci waktu, kalis usik, dan boleh dikaitkan — satu resit yang nilainya bertambah selari dengan kemampuan dunia untuk memalsukan masa lalu.
Sebahagian dokumen yang tinggal ialah spesifikasi normatif yang diperlukan untuk implementasi yang saling kendali.
Spesifikasi Protokol qub
| Medan | Nilai |
|---|---|
| Versi | 1.0 (versi protokol 0x01, versi pembungkus luar 0x01) |
| Tarikh | 2026-05-01 |
| Status | Draf |
| Disemak sehingga | 2026-05-01 |
Dokumen ini ialah spesifikasi protokol normatif untuk sistem komitmen berwaktu qub. Ia mentakrifkan struktur data, peraturan pensirian, formula derivasi, dan prosedur pengesahan yang diperlukan untuk implementasi yang saling kendali.
Skop: lapisan protokol secara sengaja bersifat neutral-bahasa — badan qub ialah bait teks biasa / markdown / perjanjian yang legap, dan pemaparan mengikut lokal ialah tanggungjawab pembaca (aplikasi web qub.social, iframe <qub-embed>, klien MCP, dsb.).
1. Notasi dan Konvensi
| Notasi | Maksud |
|---|---|
u8, u64, i64 |
Integer tanpa tanda/bertanda dengan lebar bit yang ditetapkan |
[u8; N] |
Tatasusunan bait panjang tetap sebanyak N bait |
Vec<u8> |
Tatasusunan bait panjang berubah |
Option<T> |
Nilai jenis T, atau tiada |
String |
Rentetan teks UTF-8, dinormalkan NFC |
| ` | |
SHA3-256(x) |
Cincang NIST SHA3-256 bagi rentetan bait x (FIPS 202) |
ceil(x) |
Fungsi siling: integer terkecil ≥ x |
| CBOR | Concise Binary Object Representation (RFC 8949) |
| big-endian | Bait paling signifikan didahulukan |
Semua integer dalam pembinaan praimej dikodkan sebagai tatasusunan bait lebar tetap big-endian (i64 → 8 bait, u8 → 1 bait) melainkan dinyatakan sebaliknya.
Semua cap masa ialah saat Unix dalam UTC.
2. Struktur Data
2.1 ComposeQub (Keadaan Dalam-Memori Pencipta)
Tidak disirikan kepada CBOR. Tidak ditulis ke storan kekal. Tempatan pada aplikasi pencipta.
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 (Muatan Ternyahsulit)
Disirikan menggunakan CBOR berkanun (§3). Disulitkan di dalam SealedQub. Inilah struktur yang membuktikan integriti kandungan selepas penyahsulitan.
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
}
Asas (qub teks tanpa tandatangan): version = 0x01, content_type = 0x01, sig_alg = 0x00, semua medan Option tiada.
Konfigurasi v1 yang lain: content_type = 0x03 (badan perjanjian, lihat §6.1); sig_alg = 0x01 (ML-DSA-65) dengan author_signature dan author_pubkey hadir (lihat §9.3); cosigner_pubkey dan cosigner_signature hadir bersama-sama untuk perjanjian yang ditandatangani bersama (lihat §9.7); reply_to ditetapkan kepada qub_id qub induk untuk qub rantai balas (lihat §9.3 untuk implikasi skop tandatangan).
2.3 SealedQub (Format Wayar Berkanun)
Disirikan menggunakan CBOR berkanun (§3). Ditulis ke storan kekal. Inilah artifak atas-rantai.
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 (Keadaan Aplikasi Pembaca)
Tidak disirikan kepada CBOR. Tempatan pada aplikasi pembaca. Dibina selepas penyahsulitan dan pengesahan berjaya.
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 — dibawa ke hadapan daripada QubEnvelope.outcome_at / SealedQub.outcome_at; memacu blok pemerhatian-keputusan halaman pendedahan (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. Profil CBOR Berkanun
Semua pensirian SealedQub dan QubEnvelope MUST mematuhi profil ini. Dua implementasi yang diberikan struktur logik yang sama MUST menghasilkan bait yang serupa.
3.1 Peraturan Pengekodan
| Peraturan | Spesifikasi |
|---|---|
| Piawai | RFC 8949 §4.2.1 (Core Deterministic Encoding Requirements) |
| Susunan kunci peta | Diisih mengikut panjang bait terkod dahulu (yang lebih pendek didahulukan), kemudian secara leksikografi (bait demi bait untuk pengekodan panjang yang sama) |
| Pengekodan integer | Bentuk terpendek: 0–23 dalam bait awal; 24–255 dalam 2 bait; 256–65535 dalam 3 bait; dsb. |
| Pengekodan panjang | Panjang tentu sahaja. Tiada tatasusunan, peta, rentetan bait, atau rentetan teks panjang tak tentu (maklumat tambahan = 31 adalah dilarang). |
| Tag | Tiada tag CBOR (jenis utama 6 adalah dilarang). |
| Titik apungan | Tiada apungan (nilai jenis utama 7 0xF9–0xFB adalah dilarang). |
| Rentetan teks | Dikod UTF-8, dinormalkan NFC (Unicode Normalization Form C). |
| Rentetan bait | Bait mentah. Tiada pengekodan base64 pada lapisan CBOR. |
| Kunci pendua | Tolak dengan ralat. Penghurai MUST NOT menerima kunci peta pendua secara senyap. |
| Kunci tidak dikenali | Tolak dengan ralat. Penghurai MUST NOT bertolak ansur dengan kunci peta di luar set kunci berkanun jenis tersebut — dua rentetan bait berkanun yang berbeza tidak boleh sekali-kali menyahkod kepada nilai yang sama (encode(decode(x)) == x), dan bagi muatan yang ditandatangani, kunci tambahan akan menjadi kandungan tersembunyi yang dikomit oleh kedua-dua tandatangan. Evolusi skema berjalan melalui version, tidak sekali-kali melalui kunci tambahan. |
| Nilai mudah | Hanya true (0xF5), false (0xF4), dan null (0xF6) dibenarkan. |
| Medan pilihan | Medan pilihan yang tiada diabaikan daripada peta CBOR sepenuhnya (tidak dikodkan sebagai null). Medan pilihan yang hadir dimasukkan mengikut susunan kunci yang diisih. |
3.2 Susunan Kunci Berkanun yang Disahkan
Susunan kunci ini bersifat normatif. Implementasi MUST mengeluarkan kunci dalam susunan yang tepat ini. Penegasan nyahpepijat SHOULD mengesahkan susunan dalam binaan bukan-keluaran.
QubEnvelope (versi 0x01, tanpa tandatangan, semua medan pilihan tiada):
"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)
Derivasi susunan kunci QubEnvelope: setiap kunci ialah rentetan teks CBOR. Panjang terkod = 1 bait pengepala + panjang rentetan (untuk rentetan di bawah 24 bait). Diisih mengikut jumlah panjang terkod dahulu, kemudian secara leksikografi bagi kunci panjang yang sama.
SealedQub (versi 0x01, awam, tiada penerima):
"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 (badan perjanjian, 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 (baris tatasusunan terms):
"key" (4 encoded bytes)
"value" (6 encoded bytes)
PartyIdentifier (peta party_a / party_b):
"label" (6 encoded bytes)
"contact" (8 encoded bytes) ← only if present
3.3 Rujukan Pengekodan Bait
| Jenis | Pengekodan CBOR | Contoh |
|---|---|---|
| Cincang SHA3-256 (32 bait) | 0x58 0x20 + 32 bait |
body_hash, qub_id |
| Cap masa (i64) | Jenis utama 0 (positif) atau 1 (negatif), pengekodan terpendek | saat Unix |
| Versi (u8, nilai 1) | 0x01 (bait tunggal) |
|
| Jenis kandungan (u8, nilai 1) | 0x01 (bait tunggal) |
|
| sig_alg (u8, nilai 0) | 0x00 (bait tunggal) |
|
| Tandatangan ML-DSA-65 (3,309 bait) | 0x59 0x0C 0xED + 3,309 bait |
author_signature, cosigner_signature |
| Kunci awam ML-DSA-65 (1,952 bait) | 0x59 0x07 0xA0 + 1,952 bait |
author_pubkey, cosigner_pubkey |
4. Derivasi Normatif
4.1 qub_id
qub_id mengenal pasti qub secara unik dan mengikat QubEnvelope kepada SealedQub. Ia diterbitkan secara deterministik daripada kandungan envelope.
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
Pengekodan pemisah domain: Rentetan "QUB_ID_V2" ialah 9 bait ASCII. Satu bait pelapik 0x00 ditambah untuk mencapai 10 bait bagi penjajaran. Implementasi MUST menggunakan tepat 10 bait ini: [0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00].
Pengekodan outcome_at: V1.1 melanjutkan praimej daripada 92 kepada 100 bait untuk melipat medan outcome_at pilihan ke dalam ikatan. outcome_at yang tiada dikod sebagai 8 bait sifar; pengesah protokol menolak outcome_at <= 0 di mana-mana sahaja, jadi sentinel ini tidak boleh berlanggar dengan nilai yang sah. Lihat §3.2 (format wayar) dan tasks/verdict-uplift-plan.md dalam pokok untuk mekanik verdik yang mendorong medan ini.
Pengekodan drand_round: V1.2 melanjutkan praimej daripada 100 kepada 108 bait untuk melipat drand_round (pusingan drand sasaran, §4.3) ke dalam ikatan, dan menaikkan pemisah domain kepada QUB_ID_V2. Ini mengikat pusingan timelock ke dalam identiti qub: pintu masuk tidak boleh mengikat semula siferteks kepada pusingan yang berbeza (cth., yang sudah berlalu) daripada apa yang disiratkan oleh unlock_at yang dipaparkan. Prosedur buka kunci (§8) selanjutnya mengesahkan bahawa pusingan yang dibakar ke dalam stanza siferteks tlock sepadan dengan unlock_round(unlock_at), jadi masa buka kunci yang dipaparkan terbukti ialah pusingan yang mengawal penyahsulitan.
Sifat:
- Mengubah mana-mana medan dalam QubEnvelope (badan, cap masa, jenis kandungan, versi) menghasilkan qub_id yang berbeza.
- qub_id dikira sebelum penyulitan. Kedua-dua QubEnvelope dan SealedQub membawa qub_id yang sama. Pembaca mengesahkan ia sepadan selepas penyahsulitan.
- qub_id tidak bergantung pada
sender_label,author_signature, atauauthor_pubkey. Ini bermakna kandungan yang sama yang dimeterai pada masa yang sama menghasilkan qub_id yang sama tanpa mengira siapa yang menandatanganinya. - Mengubah
titleSealedQub (dengan segala-galanya tetap) mengubahqub_idmelaluititle_hash. Oleh itu, pintu masuk tidak boleh menukar tajuk teks biasa yang dipaparkan pada hitung mundur tanpa membatalkan identiti qub. - Mengubah
outcome_atSealedQub (dengan segala-galanya tetap) mengubahqub_idmelalui praimej. Pintu masuk tidak boleh menukar tarikh verdik-pada pra-pendedahan yang dipaparkan pada hitung mundur tanpa membatalkan identiti qub. - Mengubah
drand_round(dengan segala-galanya tetap) mengubahqub_idmelalui praimej. Pintu masuk tidak boleh mengikat semula siferteks timelock kepada pusingan yang berbeza tanpa membatalkan identiti qub; digabungkan dengan semakan stanza-pusingan masa-buka-kunci §8,unlock_atyang dipaparkan ialah pusingan yang sebenarnya mengawal penyahsulitan.
4.2 body_hash
body_hash = SHA3-256(body)
Di mana body ialah muatan kandungan Vec<u8> mentah. Untuk qub teks, ini ialah badan qub yang dikod UTF-8.
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
Di mana title ialah tajuk teks biasa pilihan yang dipaparkan pada hitung mundur pembaca sebelum pendedahan (lihat §3.2). Penormalan NFC dijalankan pada masa pencincangan supaya cernaan stabil merentas urutan titik kod yang setara secara visual. Sentinel sifar-semua dikhaskan untuk kes tiada; rentetan kosong ditolak pada sempadan CBOR berkanun sebagai pengekodan bukan-berkanun bagi "tiada" (pengekodan berkanun mengabaikan medan sepenuhnya).
4.3 Pemetaan Pusingan-Buka-Kunci
drand_round = ceil((unlock_at - chain_genesis_time) / chain_period_seconds)
| Parameter | Sumber | Contoh |
|---|---|---|
unlock_at |
Saat Unix UTC yang dipilih pengguna | 1735689600 (2025-01-01 00:00:00 UTC) |
chain_genesis_time |
maklumat rantai drand (genesis_time) |
1595431050 |
chain_period_seconds |
maklumat rantai drand (period) |
30 |
Operasi ceil() memilih pusingan drand pertama yang masa pendedahannya ≥ unlock_at. Ini memastikan qub tidak menjadi boleh dinyahsulit sebelum masa buka kunci yang dipilih.
Kes pinggir: jika (unlock_at - chain_genesis_time) boleh dibahagikan tepat dengan chain_period_seconds, hasilnya ialah pusingan tepat itu — qub buka kunci tepat pada masa pendedahan pusingan itu.
Pengesahan: unlock_at MUST berada pada masa hadapan pada masa pemeteraian. unlock_at MUST NOT lebih daripada 10 tahun daripada created_at (untuk mengehadkan risiko kebergantungan drand jangka panjang; UI SHOULD memberi amaran untuk tarikh buka kunci melebihi 2 tahun).
5. Jenis Baharu Format Wayar
Jenis baharu format wayar menyediakan keselamatan masa-kompilasi terhadap mengelirukan bait CBOR dengan JSON, teks biasa mentah, atau pengekodan bait lain.
| Jenis | Mengandungi | Dihasilkan Oleh | Digunakan Oleh |
|---|---|---|---|
SealedQubCbor |
CBOR berkanun bagi SealedQub | serialize_sealed_qub() |
Muat naik storan kekal, ambilan pembaca |
QubEnvelopeCbor |
CBOR berkanun bagi QubEnvelope | serialize_qub_envelope() |
Input penyulitan tlock, output penyahsulitan tlock |
5.1 Peraturan Pembinaan
// 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 Pengesahan pada Pembinaan
from_encoded() SHOULD mengesahkan bahawa input bermula dengan pengepala peta CBOR yang sah. Pengesahan struktur penuh berlaku pada masa hurai, bukan masa pembinaan, untuk mengelakkan hurai-dua-kali.
6. Pendaftaran Jenis Kandungan
| Nilai | Jenis | Saiz Badan Maks | Nota |
|---|---|---|---|
0x00 |
Dikhaskan (tidak sah) | — | MUST NOT digunakan |
0x01 |
Teks biasa (UTF-8, Markdown terhad) | 50 KB berbayar / 10 KB percuma | Lihat §10 untuk peraturan pemaparan. Pemisahan percuma / berbayar dikuatkuasakan oleh perkhidmatan muat naik; siling keras lapisan protokol ialah 50 KB. |
0x02 |
Dikhaskan (masa depan) | — | Diperuntukkan untuk jenis kandungan masa depan; tidak sah dalam v1. Pembaca MUST menolak mengikut peraturan di bawah. |
0x03 |
Perjanjian (perjanjian dwihala, badan CBOR) | 100 KB | Badan ialah CBOR berkanun PactTerms (§6.1). Tandatangan bersama mengikut §9.7. |
0x04 |
Keputusan (penilaian-diri pencipta, badan CBOR) | 8 KB | Badan ialah CBOR berkanun VerdictBody (§6.2). Dikeluarkan hanya oleh hasrat sisi-sistem verdict. Hubungan induk berada pada tag Arweave Parent-Tx-Id, bukan pada badan. Lihat verdict-uplift-plan §3.4. |
Pembaca MUST menolak jenis kandungan yang tidak dikenali dengan ralat yang jelas kelihatan kepada pengguna. Pembaca MUST NOT cuba memaparkan jenis yang tidak dikenali sebagai teks.
6.1 Badan Perjanjian (content_type = 0x03)
Badan perjanjian ialah pengekodan CBOR berkanun bagi nilai PactTerms:
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)> }
Susunan kunci CBOR berkanun untuk ketiga-tiga peta diberikan dalam §3.2. Jumlah CBOR perjanjian yang disirikan MUST NOT melebihi 100 KB (sepadan dengan §6).
Pembeza skema. Baris pertama dalam terms untuk perjanjian structured/v1 MUST ialah { key: "pact_schema", value: "structured/v1" }. Baris tanpa penanda ini ialah perjanjian "tersuai" dan tidak menerima pengesahan berstruktur atau pemaparan sedar-skema.
Slot pengakuan yang dibekukan. Perjanjian structured/v1 membawa tepat empat baris pengakuan di bawah kunci berikut:
"initiator_standard_terms"
"initiator_capacity_terms"
"counterparty_standard_terms"
"counterparty_capacity_terms"
Nilai value bagi setiap satu ialah salah satu daripada lapan rentetan Inggeris dibekukan yang dipilih oleh pasangan (role, kind), di mana role ∈ { seller, buyer, provider, client } dan kind ∈ { standard, capacity }. Rentetan itu sendiri ialah data protokol normatif — tandatangan ML-DSA-65 kedua-dua pihak komited kepada bait yang tepat melalui body_hash. Ia TIDAK dilokalkan; badan yang ditandatangani bersifat neutral-bahasa. Apa-apa perubahan kata-kata memerlukan versi skema baharu (structured/v2).
Lapan rentetan, carian (acknowledgement_for(role, kind)), dan rasional bagi setiap satu disematkan oleh implementasi rujukan. Implementasi yang mematuhi MUST mengeluarkan nilai pengakuan yang serupa bait-demi-bait; ujian body-cincangan SHA3-256 lekapan-emas yang meliputi keempat-empat kombinasi peranan menangkap apa-apa hanyutan.
Susunan paparan pembaca. Rentetan pengakuan mengandungi frasa seperti "described above", yang menganggap baris penerangan / skop dipaparkan sebelum pengakuan. Pembaca MUST memaparkan tatasusunan terms dalam susunan CBOR; menyusun semula merosakkan semantik prosa.
Hubungan Pihak Lawan. Apabila contact Pihak B ialah alamat e-mel yang sah, perkhidmatan muat naik qub menghantar e-mel jemputan semakan / tandatangan bersama secara automatik pada masa pementasan dan mengikat tandatangan bersama akhirnya kepada pengesahan alamat yang sama itu (§9.7). Perjanjian yang contact Pihak B-nya tiada masih boleh ditandatangani bersama, tetapi hanya melalui saluran luar-jalur — perkhidmatan menolak permintaan tandatangan bersama yang tidak boleh menghasilkan penanda pengesahan-e-mel 15 minit yang sepadan.
6.2 Badan Keputusan (content_type = 0x04)
Badan keputusan ialah pengekodan CBOR berkanun bagi nilai VerdictBody:
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
}
Susunan kunci CBOR berkanun:
"outcome" (8 encoded bytes)
"reflection" (11 encoded bytes) ← only if present
"evidence_url" (13 encoded bytes) ← only if present
"verdict_version" (16 encoded bytes)
Jumlah CBOR keputusan yang disirikan MUST NOT melebihi 8 KB (sepadan dengan baris pendaftaran di atas).
Enum keputusan. Bait wayar bersifat hasrat-neutral; keempat-empat kategori Right / Partial / Wrong / Unfalsifiable meliputi ruang keputusan setiap hasrat yang membawa keputusan. Label setiap-hasrat ("Tepat sasaran" / "Saya menunaikannya" / "Dikeluarkan" / "Disahkan" untuk Right, dsb.) ialah kebimbangan pemaparan sisi-pembaca yang diselesaikan terhadap hasrat qub induk — wayar kekal neutral-bahasa dan neutral-hasrat. Nilai di luar 1..=4 MUST ditolak pada penyahkodan.
Pautan induk. Sebuah qub keputusan TIDAK membawa rujukan induk dalam badannya. Id transaksi Arweave qub induk dikeluarkan sebagai tag storan Parent-Tx-Id pada masa muat naik (§7 lapisan tag storan). Ini mengekalkan badan sebagai pernyataan penilaian-diri yang ditandatangani dan serba lengkap; rantai audit ("betul tentang apa?") ditubuhkan melalui carian tag Arweave.
Keselamatan URL bukti (normatif). Apabila evidence_url hadir, pengesah (sisi-karang, sisi-wayar, edge Worker) MUST menguatkuasakan:
- HTTPS sahaja. Rentetan MUST bermula dengan urutan bait
https://. Apa-apa skema lain —http,ftp,javascript,data,file, dsb. — ditolak. - Had panjang. ≤ 2,048 bait (had praktikal URL pelayar).
- Pemeriksaan NFC + kod-titik bermusuhan. Peraturan yang sama seperti
titledanreflection— kod-titik bidi-override / lebar-sifar / blok-tag / BOM / C0 / C1 ditolak. Definisi sepadan dengan Rustcrate::handle::contains_hostile_text_codepointdan TSworkers/api/src/utils/unicode.ts::isHostileCodepoint(kekalkan selari). - Tiada ruang putih, tiada kawalan ASCII. Ruang putih / DEL / bait di bawah
0x20di mana-mana dalam URL ditolak — menutup vektor suntikan\n/\tyang tidak diliputi peraturan bidi. - Segmen hos tidak kosong. Semua yang berada di antara
https://dan/,?, atau#yang pertama MUST tidak kosong.
Tiada pengambilan sisi-pelayan. Worker MUST NOT memproksi, mengambil, atau pratonton URL. Protokol menyimpan rentetan; pemaparan berlaku sisi-pembaca dengan rel="nofollow noopener noreferrer" target="_blank" dan hos yang kelihatan dipaparkan bersebelahan teks pautan.
Refleksi. Teks refleksi tulisan-pencipta pilihan ("apa yang berubah, apa yang anda pelajari"). Pengesahan NFC + kod-titik bermusuhan yang sama seperti title. Input kosong / hanya ruang putih menguncup kepada tiada pada masa pembinaan.
Versi skema. v1 menyokong verdict_version = 0x01 sahaja. Semakan skema masa depan menaikkan bait ini dan tiba bersama versi protokol baharu mengikut §12.
7. Protokol Meterai
Urutan pemeteraian lengkap. Setiap langkah bersifat normatif.
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.
Lapisan tag storan (luar-jalur). Perkhidmatan muat naik qub melampirkan satu set tag transaksi storan yang sengaja kecil di sebelah muatan terbalut. Content-Type=application/octet-stream diperlukan secara normatif. Perkhidmatan rujukan juga melampirkan tiga tag pilihan apabila pencipta memilih untuk memaparkannya: Intent (niat penggubahan disahkan-senarai dibenarkan — cth., quote, reply, commitment), Author (cap jari kunci awam §9.3 pencipta sebagai heks huruf kecil 64-aksara), dan Parent-Tx-Id (ID transaksi storan qub induk untuk rantai balas, base64url 43-aksara).
Tag Author adalah pilih-masuk setiap qub: aplikasi pencipta rujukan melampirkannya hanya apabila pengguna mendayakan atribusi awam secara eksplisit pada masa pemeteraian. Apabila togol mati — lalai — tiada tag Author ditulis dan qub tidak dikaitkan pada rantai: tiada apa-apa dalam storan kekal yang menghubungkan muat naik kepada pemegang, e-mel, atau qub lain pencipta. Apabila togol hidup, cap jari Author menyelesaikan kepada @handle yang dipilih pencipta melalui rantai pengesahan §9.5. Hubungan rantai-balas dan Intent tidak mengenal pasti. Pembungkus luar (§13) melindungi badan dalaman daripada korelasi siferteks — menghalang penuai daripada mengenali dan menyahsulit secara pukal muat naik berbentuk qub selepas pusingan drand mereka diterbitkan.
Perkhidmatan rujukan sengaja TIDAK melampirkan tag App-Name, App-Version, atau Type: apa-apa penapis bernilai-tunggal sebegini akan mengembalikan keseluruhan korpus qub kepada pertanyaan GraphQL, yang tidak konsisten dengan skop kerahsiaan badan-sahaja pembungkus.
Pengesah yang mematuhi MUST NOT bergantung pada apa-apa tag storan untuk pengesahan pihak-ketiga §11; cincang badan / qub_id / tandatangan komited hanya kepada CBOR dalaman, tidak pernah kepada set tag.
8. Protokol Buka Kunci
Urutan buka kunci lengkap. Setiap langkah bersifat normatif.
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. Tandatangan Pengarang
9.1 Rasional
qub disimpan dalam storan kekal. Tandatangan pengarang mesti kekal tidak boleh dipalsukan selama-lamanya, sebab itulah v1.0 menggunakan skema ML-DSA-65 pasca-kuantum (FIPS 204) berbanding skema klasik yang keselamatannya boleh merosot dalam hayat kekal qub.
9.2 Pendaftaran Algoritma
sig_alg |
Skema | Saiz Kunci | Saiz Tandatangan |
|---|---|---|---|
0x00 |
Tiada tandatangan (tanpa tandatangan) | — | — |
0x01 |
ML-DSA-65 (FIPS 204) | 1,952 bait | 3,309 bait |
Pembaca MUST menolak nilai sig_alg yang tidak dikenali.
9.3 Pembinaan Praimej Bertandatangan
Dua versi praimej pernah wujud. Semua tandatangan MUST menggunakan V2, dan pengesah MUST menerima V2 sahaja. Praimej V1 warisan (didokumenkan di bawah untuk rujukan sejarah) diterima sebagai sandaran pengesahan-sahaja semasa migrasi V2; sandaran itu telah ditamatkan dan tandatangan V1-sahaja kini ditolak.
V2 (semasa — dihasilkan oleh semua penandatanganan pengarang baharu, dan oleh kedua-dua tandatangan aliran pementasan / tandatangan bersama perjanjian):
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 mengikut konvensyen penunjuk-ketiadaan yang sama seperti title_hash (§4.2.1): 32 bait sifar bukan output SHA3-256 yang sah, jadi "ketiadaan" tidak akan pernah berlanggar dengan label yang hadir. Semua medan mempunyai lebar tetap, jadi praimej tidak taksa tanpa awalan panjang.
V1 (warisan — DITAMATKAN; tidak lagi dihasilkan dan tidak lagi diterima semasa pengesahan):
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
Praimej V1 meninggalkan sender_label dan reply_to. Ia diterima sebagai sandaran pengesahan-sahaja semasa migrasi ke V2; sandaran itu sejak itu telah ditamatkan — pengesah MUST menerima praimej V2 sahaja. Takrif ini dikekalkan di sini untuk rujukan sejarah dan untuk menerangkan pemisah domain di bawah. Tandatangan yang hanya disahkan terhadap V1 MUST dianggap sebagai kegagalan pengesahan.
Pemisah domain: "QUB_AUTHOR_SIG_V1" / "QUB_AUTHOR_SIG_V2" ialah 17 bait ASCII setiap satu ([0x51, 0x55, 0x42, 0x5F, 0x41, 0x55, 0x54, 0x48, 0x4F, 0x52, 0x5F, 0x53, 0x49, 0x47, 0x5F, 0x56, 0x31/0x32]). Tiada pelapik. Pemisah yang berbeza memisahkan-domain kedua-dua binaan, jadi tandatangan atas satu praimej tidak akan pernah disahkan sebagai yang satu lagi.
Bait org_id_present: bait yang mengikuti unlock_at MUST ialah 0x00. Implementasi rujukan mendedahkan ini sebagai pemalar ORG_ID_PRESENT_INDIVIDUAL = 0x00 dalam crates/qub-core/src/signing.rs; pembaca yang membina semula sig_input untuk pengesahan MUST mengeluarkan bait yang sama.
Skop tandatangan — apa yang diliputi dan apa yang tidak. sig_input V2 komited secara langsung kepada version, qub_id, body_hash, unlock_at, sender_label, dan reply_to (tambah pemisah domain tetap dan bait org_id_present). qub_id itu sendiri diterbitkan daripada version, content_type, created_at, unlock_at, outcome_at, drand_round, dan body_hash melalui praimej §4.1, jadi apa-apa perubahan kepada medan-medan itu menghasilkan qub_id yang berbeza dan membatalkan tandatangan secara transitif. Permukaan yang disahkan dengan itu ialah:
| Medan | Disahkan oleh tandatangan | Bagaimana |
|---|---|---|
version |
✓ | Input langsung kepada sig_input |
qub_id |
✓ | Input langsung |
body_hash |
✓ | Input langsung |
unlock_at |
✓ | Input langsung |
sender_label |
✓ | Input langsung melalui sender_label_hash (praimej V2 — satu-satunya bentuk yang diterima) |
reply_to |
✓ | Input langsung melalui reply_to_or_zero (praimej V2 — satu-satunya bentuk yang diterima) |
content_type |
✓ | Secara transitif, melalui praimej qub_id |
created_at |
✓ | Secara transitif, melalui praimej qub_id |
outcome_at |
✓ | Secara transitif, melalui praimej qub_id |
drand_round |
✓ | Secara transitif, melalui praimej qub_id (V1.2) |
body |
✓ | Secara transitif, melalui body_hash = SHA3-256(body) |
author_pubkey |
— (tersirat) | Kunci yang mengesahkan tandatangan ialah pengarang, secara takrif |
cosigner_pubkey / cosigner_signature |
— | Ditandatangani secara bebas pada sig_input yang sama (lihat §9.7) |
drand_chain_id, tlock_ciphertext, visibility |
— | Medan SealedQub luaran, bukan di dalam envelope — diliputi oleh invarian struktur mereka sendiri (konsistensi pusingan / rantai) tetapi bukan oleh tandatangan pengarang. (drand_round kini diikat secara transitif melalui praimej qub_id — lihat di atas.) |
Mengapa V2 ialah satu-satunya praimej yang diterima.
- Di bawah praimej V1 yang ditamatkan, pihak yang mempunyai akses tulis kepada bait yang disimpan boleh menukar
sender_label("Alice" → "Mallory") atau menetapkan semula indukreply_to— dan menyulit semula selepas-pusingan — tanpa membatalkan tandatangan pengarang, kerana tiada satu pun medan tersebut berada dalam praimej yang ditandatangani. V2 meliputi kedua-duanya, jadi apa-apa perubahan kepada mana-mana medan mengubah pengesahan kepada "gagal". Oleh sebab pengesah kini menerima V2 sahaja, penukaran ini ditutup untuk setiap tandatangan: tandatangan yang tidak mengikat kedua-dua medan (iaitu hanya disahkan terhadap V1) ditolak terus dan bukannya diturunkan taraf kepadanya. author_pubkeydi dalam envelope kekal sebagai sauh identiti sebenar — pembaca MUST menerbitkan identiti paparan daripadaauthor_pubkey(melalui lapisan pengesahan §9.5) bukannya mempercayaisender_label.
Implementasi yang memaparkan sender_label atau reply_to kepada pengguna akhir MUST mempamerkan identiti yang disahkan (cap jari kunci awam, pengesahan) sebagai isyarat identiti utama, bukan label.
9.4 Prosedur Pengesahan
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."
Pengesahan tandatangan ialah operasi yang paling mahal (terutamanya ML-DSA-65). Ia SHOULD dilakukan selepas semua pemeriksaan yang lebih murah (cincang, qub_id, unlock_at) telah lulus.
9.5 Pengesahan Identiti
Pengesahan identiti — pemetaan author_pubkey kepada tuntutan identiti yang boleh dikenali manusia seperti pemegang qub, alamat e-mel, pemegang sosial, atau kelayakan passkey — ialah peningkatan progresif sisi-pembaca dan tidak diperlukan untuk pengesahan tandatangan. Pembaca yang menyelesaikan pengesahan kepada identiti paparan MUST menggunakan kekananan:
handle > email > social > fingerprint
Sandaran cap jari ialah heks huruf kecil bagi SHA3-256(author_pubkey); ia sentiasa tersedia untuk mana-mana qub bertandatangan. Pembaca MAY meringkaskannya untuk paparan — pembaca rujukan memaparkan qub: diikuti oleh empat bait pertama dan terakhir (qub:<8 hex>…<8 hex>).
Pengesah yang mematuhi boleh melengkapkan setiap pemeriksaan dalam §9.4 tanpa menghubungi API qub, tanpa apa-apa rangkaian melebihi storan kekal dan drand, dan tanpa apa-apa carian sisi-pelayan. Penyelesaian pengesahan ialah langkah terbaik-cuba yang berasingan yang dilakukan hanya selepas pengesahan tandatangan berjaya.
9.6 Kesan Saiz
| Ed25519 | ML-DSA-65 | |
|---|---|---|
| Tandatangan | 64 bait | 3,309 bait |
| Kunci awam | 32 bait | 1,952 bait |
| Jumlah setiap qub | 96 bait | 5,261 bait |
| Delta kos storan (pada ~$5/MB) | ~$0.0005 | ~$0.026 |
Untuk qub teks 500–2,000 bait, ML-DSA-65 kira-kira menggandakan tiga kali ganda saiz yang disimpan. Kos mutlak adalah boleh diabaikan.
9.7 Pengesahan Penandatangan Bersama (Perjanjian Dwihala)
Untuk perjanjian dwihala (content_type = 0x03), lapisan tandatangan kedua membuktikan kedua-dua pihak bersetuju dengan terma yang sama.
Medan envelope:
cosigner_pubkey: Kunci awam ML-DSA-65 penandatangan-balas (Pihak B).cosigner_signature: Tandatangan atassig_inputyang sama seperti pengarang (§9.3).
Kedua-dua medan MUST hadir bersama-sama atau kedua-duanya tiada. Jika tepat satu hadir, pembaca MUST melaporkan ralat integriti.
Prosedur pengesahan:
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."
Sifat:
- Penandatangan bersama menandatangani
sig_inputyang sama seperti pengarang — kedua-dua pihak komited kepadaqub_id,body_hash, danunlock_atyang sama (dan, di bawah V2,sender_label_hashdanreply_to_or_zeroyang sama). - Untuk membolehkan penandatangan-balas membina semula praimej V2 tanpa akses kepada bait envelope mentah, perkhidmatan pementasan menguatkuasakan pada masa pementasan bahawa
sender_labelenvelope perjanjian sama denganpact_terms.party_a.labeldan bahawareply_totiada. Kedua-duanya berlaku untuk setiap perjanjian klien-rujukan; envelope yang melanggar ditolak semasa pementasan. - Derivasi
qub_id(§4.1) TIDAK termasuk medan penandatangan bersama. Menambah penandatangan bersama kepada envelope sedia ada tidak mengubahqub_id. - Perjanjian boleh ditandatangani-pengarang sahaja (komitmen sebelah), penandatangan bersama sahaja (luar biasa), atau kedua-duanya (bukti dwihala penuh).
Pintu pengikatan-e-mel (operasi). Apabila perjanjian yang dipentaskan membawa hubungan e-mel Pihak B (§6.1), perkhidmatan muat naik qub MUST menolak permintaan tandatangan bersama melainkan penanda pengesahan-e-mel jangka pendek wujud yang sepadan dengan kedua-dua id pementasan dan cincang e-mel ternormal hubungan tersebut. Penanda itu ditulis oleh /api/v1/auth/verify apabila token pautan-ajaib membawa staging_id dan alamat yang disahkan sepadan dengan SHA-256(normalise_email(party_b.contact)) — di mana normalise_email(addr) mengekalkan kes bahagian-tempatan dan menurunkan huruf kecil hanya bahagian domain (mengikut RFC 5321 §2.3.11), dan SHA-256 di sini ialah cincang NIST FIPS 180-4 (berbeza daripada SHA3-256 yang digunakan dalam derivasi §4) — dan tamat tempoh 900 saat (15 minit) selepas dikeluarkan. Ini ialah pintu anti-penyamaran operasi, BUKAN sebahagian daripada bukti qub atas-rantai — pengesah pihak-ketiga yang memainkan semula §11 hanya memerlukan storan kekal dan drand, tanpa apa-apa carian sisi-pelayan. Penanda wujud sisi-pelayan sahaja dan tidak pernah menjadi sebahagian daripada badan yang ditandatangani.
Kesan saiz (pengarang ML-DSA-65 + penandatangan bersama):
| Komponen | Saiz |
|---|---|
| Tandatangan pengarang | 3,309 bait |
| Kunci awam pengarang | 1,952 bait |
| Tandatangan penandatangan bersama | 3,309 bait |
| Kunci awam penandatangan bersama | 1,952 bait |
| Jumlah overhed kripto | 10,522 bait |
| Delta kos storan | ~$0.05 |
10. Pemaparan dan Sanitasi Markdown
Bahagian ini kritikal keselamatan. Pembaca memaparkan qub teks (content_type = 0x01) menggunakan subset Markdown yang terhad.
10.1 Elemen Dibenarkan
- Pengepala:
#hingga####(tiada#####atau######) - Penekanan: tebal (
**), italik (*), garis-coret (~~) - Senarai: bertertib (
1.) dan tidak bertertib (-,*) - Petikan blok (
>) - Kod: rentang sebaris (```) dan blok berpagar (`````)
- Pemisah mendatar (
---) - Pemecah baris (dua ruang belakang atau baris kosong)
- Perenggan
10.2 Elemen Dilarang
| Elemen | Pengendalian |
|---|---|
HTML mentah (<div>, <script>, dsb.) |
Dibuang sepenuhnya. Tiada HTML melepasi. |
Imej () |
Dibuang. Sintaks imej dialih keluar daripada output. |
Pautan ([text](url)) |
URL dipaparkan sebagai teks biasa yang kelihatan. Tidak auto-pautkan. Tidak boleh diklik tanpa tindakan pengguna yang eksplisit. |
| Skema URL berbahaya | javascript:, data:, vbscript:, file: — dibuang. |
| Iframe, embed, objek | Dibuang. |
| Entiti HTML | Dinyahkod kepada aksara paparan hanya jika selamat. |
10.3 Implementasi
Implementasi MUST menggunakan penghurai senarai dibenarkan ketat, bukan senarai disekat. Pendekatan yang disyorkan:
- Hurai Markdown menggunakan
pulldown-cmark(atau setara). - Susuri AST dan buang mana-mana nod yang tidak dalam senarai dibenarkan (§10.1).
- Untuk nod pautan: keluarkan URL sebagai teks yang kelihatan, bukan sebagai elemen
<a>yang boleh diklik. - Tukarkan AST yang ditapis kepada perwakilan perantaraan bertaip (cth., enum
MarkdownNodedengan hanya varian selamat). HTML mentah tidak boleh diwakili secara struktur dalam IR ini. - Paparkan daripada IR bertaip kepada lapisan pandangan sasaran (cth., komponen pandangan reaktif, nod DOM). Tiada penyambungan rentetan HTML atau
innerHTMLpada bila-bila masa.
Pendekatan senarai disekat adalah rapuh kerana sambungan Markdown baharu atau keanehan penghurai boleh memperkenalkan elemen yang tidak ditapis. Pendekatan AST bertaip menjadikan XSS mustahil secara struktur — tiada varian yang boleh membawa HTML sewenang-wenangnya.
10.4 Had Saiz dan Struktur
- Kedalaman pengepala maksimum yang dipaparkan:
####(H4).#####dan lebih dalam dipaparkan sebagai teks tebal. - Tiada had bilangan perenggan (had saiz badan dalam §6 ialah kekangan).
- Blok kod berpagar: tiada penonjolan sintaks dalam MVP. Dipaparkan sebagai teks praformat monospace.
11. Pengesahan Pihak Ketiga
Mana-mana pihak ketiga boleh mengesahkan qub awam tanpa kerjasama qub. Prosedur pengesahan:
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.
Apa yang dibuktikan oleh pengesahan:
| Bukti | Apa yang ia tetapkan |
|---|---|
| Komitmen | Siferteks wujud menjelang cap masa blok storan. |
| Integriti | Badan teks biasa sepadan dengan cincang yang dikomited dan tidak diubah. |
| Pemasaan | Kandungan tidak boleh dibaca sehingga pusingan drand, yang sepadan dengan masa buka kunci yang dipilih (tertakluk kepada andaian keselamatan tlock dan drand). |
Apa yang TIDAK dibuktikan oleh pengesahan:
| Bukan-bukti | Mengapa |
|---|---|
| Pengarangan | sender_label bersifat hiasan. Tanpa sig_alg ≥ 0x01, sesiapa sahaja boleh meterai kandungan ini. |
| Niat | qub membuktikan kandungan dan pemasaan, bukan apa yang dimaksudkan pencipta secara subjektif. |
| Pemasaan pra-peristiwa | Kemasukan blok storan mungkin lewat muat naik sebenar dengan beberapa minit. Cap masa komitmen ialah masa blok, bukan saat pengguna menekan "meterai." |
12. Versi
12.1 Versi Protokol
Medan version (u8) dalam kedua-dua SealedQub dan QubEnvelope mengenal pasti versi protokol utama.
- Pembaca MUST menolak versi utama yang tidak dikenali dengan ralat yang jelas.
- Dalam versi utama yang dikenali, penyahkod MUST menolak kunci peta yang tidak dikenali (§3.1) — evolusi skema berlaku dengan memperkenalkan
versionbaharu, bukan dengan menambah kunci yang akan dilangkau oleh penyahkod sedia ada. (Semakan terdahulu spesifikasi ini membenarkan bertolak ansur dengan medan pilihan yang tidak dikenali; klausa itu ditarik balik — ia menjadikanencode(decode(x))tidak lagi satu-ke-satu dan membuka vektor kandungan tersembunyi yang ditandatangani pada muatan perjanjian.) - Jenis kandungan (
content_type) dan skema tandatangan (sig_alg) bersifat versi-berpintu: nilai baharu hanya boleh diperkenalkan bersama versi protokol baharu atau kemas kini pendaftaran yang eksplisit.
12.2 Sejarah Versi
| Versi | Nilai | Penerangan |
|---|---|---|
| v1 | 0x01 |
qub teks awam (content_type 0x01), badan perjanjian dwihala (0x03, skema structured/v1, pengarang + penandatangan bersama ML-DSA-65), tlock, SHA3-256 |
12.3 Keserasian Ke Hadapan
Pembaca v1 yang menjumpai QubEnvelope dengan kunci peta CBOR yang tidak dikenali (kunci yang tiada dalam susunan berkanun §3.2) MUST menolaknya dengan ralat nyahkod (§3.1). Keserasian ke hadapan bergantung pada medan version, bukan pada tolak ansur kunci: penambahan pada masa hadapan — walaupun metadata kecil — dihantar di bawah nilai version baharu, yang ditolak oleh pembaca v1 dengan ralat "protokol lebih baharu" yang jelas, bukannya secara senyap menggugurkan kandungan yang dikomit oleh tandatangan.
Pembaca v1 yang menjumpai sig_alg = 0x01 (ML-DSA-65) tetapi kekurangan sokongan pengesahan ML-DSA-65 SHOULD memaparkan kandungan qub dengan notis "tandatangan hadir tetapi tidak boleh disahkan", bukannya menolak qub sepenuhnya. Implementasi rujukan hari ini menolak setiap nilai sig_alg selain daripada 0x00 dan 0x01 kerana pendaftaran v1 tidak mengandungi algoritma sah lain — penolakan ketat dan gagal-lembut adalah serupa dari segi pemerhatian sehingga algoritma ketiga didaftarkan. Tingkah laku gagal-lembut di atas menjadi memikul-beban sebaik sahaja §9.2 menerima entri baharu, dan pembaca rujukan akan dikemas kini kepada gagal-lembut pada ketika itu.
12.4 Versi Pembungkus Luar
OuterWrapper yang diterangkan dalam §13 membawa bait version-nya sendiri, bebas daripada SealedQub.version dan QubEnvelope.version. Kedua-dua ruang versi berkembang secara berasingan: penggantian simetrik selamat-pasca-kuantum pada masa hadapan menolak bait pembungkus tanpa menyentuh versi protokol dalaman, dan penambahan lapisan protokol pada masa hadapan (cth., medan envelope baharu) menolak versi dalaman tanpa menyentuh bait pembungkus.
OUTER_WRAPPER_VERSION_* |
Nilai | Algoritma | Status |
|---|---|---|---|
OUTER_WRAPPER_VERSION_1 |
0x01 |
AES-256-GCM dengan nonce 12-bait, tag pengesahan 16-bait, AAD terikat kepada qub_id |
lalai v1 |
| — | 0x02–0xFF |
Dikhaskan | Masa Hadapan |
Pembaca MUST menolak versi pembungkus yang tidak dikenali dengan ralat yang jelas. Protokol sengaja mengekalkan ruang versi pembungkus yang sempit sehingga pemacu migrasi konkrit muncul (cth., panduan NIST yang mengutamakan AEAD yang berbeza); slot 0x02 akan diperuntukkan dalam semakan yang sama yang memperkenalkan algoritma.
13. Pembungkus Penyulitan Luar
13.1 Rasional
Lapisan protokol (QubEnvelope → tlock → SealedQub) menjadikan qub yang dimeterai terkunci-waktu: badan tidak boleh dibaca sehingga unlock_at dan tandatangan pusingan drand telah diterbitkan. Walau bagaimanapun, selepas buka kunci, tandatangan pusingan adalah awam dan bentuk CBOR berkanun SealedQub boleh dikenali, jadi penuai yang mengindeks transaksi storan kekal boleh menyahsulit korpus qub keseluruhan secara pukal.
Pembungkus penyulitan luar menutup saluran itu dengan menyelitkan lapisan AEAD simetrik tambahan antara SealedQubCbor berkanun dan bait yang ditulis ke storan kekal. Kunci 256-bit K wujud hanya dalam fragmen URL pautan penghantaran dan pada peranti pengguna; pelayar tidak menghantar fragmen URL kepada pelayan, jadi qub.social, setiap pintu masuk storan, dan setiap CDN di hadapan kedua-duanya adalah buta dari segi pemerhatian terhadap K. Oleh itu, setiap qub dalam storan kekal ialah siferteks legap yang teks biasanya tidak boleh dipulihkan tanpa URL yang dipilih pencipta untuk dikongsi.
Kesan bersih:
- Imuniti penghitungan secara lalai. Bait terbungkus dalam storan kekal tidak boleh dibezakan bait-demi-bait daripada siferteks sewenang-wenangnya. Strategi penuai "pertanyaan-GraphQL untuk muat naik berbentuk-qub, nyahsulit pukal dengan tandatangan drand awam" tidak berakhir dengan teks biasa.
- Postur kerahsiaan kripto-pemusnahan. qub.social secara harfiah tidak boleh menyahsulit korpusnya sendiri. Sepina mencapai siferteks, bukan teks biasa.
- Tangga kerahsiaan dua-tahap. Lalai = akses dikawal-pautan (bahagian ini). qub peribadi yang disulitkan-penerima (ciri Fasa-2 yang dikhaskan, belum dispesifikasikan) berlapis di atas sebagai tahap kedua.
13.2 Berlapis
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)
Meterai dan buka kunci pada lapisan protokol (§7, §8) tidak berubah di bawah sempadan pembungkus; pembungkus dilekatkan pada laman panggilan seal() dan ditanggalkan pada laman panggilan unlock().
13.3 Struktur Data OuterWrapper
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
}
Invarian medan.
versionMUST bersamaan0x01untuk bait pembungkus v1.0.qub_idMUST bersamaan medanqub_idSealedQub yang dipulihkan selepas penyahbungkusan. Langkah penyahbungkusan tidak menguatkuasakan ini secara langsung (pengikatan AEAD AAD menjadikan usikan tahap-bait mustahil), tetapi lapisan buka kunci menyemak hubungan secara transitif: jika pencipta membungkusSealedQubCboryangqub_iddalamannya tidak sepadan denganqub_idpembungkus, §8 langkah 11 gagal.nonceMUST ialah 96 bit (12 bait), dihasilkan baharu oleh CSPRNG untuk setiap operasi pembungkusan. Menggunakan semula nonce di bawah kunci yang sama membenarkan serangan guna-semula-nonce AEAD yang memulihkan teks biasa; pengeluar MUST melayan pasangan (key,nonce) sebagai sekali-guna.ciphertextialah output AES-256-GCM: bait siferteks digabungkan dengan tag pengesahan 16-bait.ciphertext.len() == SealedQubCbor.len() + 16tepat.
Pengekodan CBOR. CBOR berkanun mengikut §3, dengan peraturan susunan-kunci yang sama (diisih mengikut panjang bait terkod menaik, kemudian secara leksikografi). Empat kunci tersebut ialah:
| Kunci | Bait terkod | Susunan |
|---|---|---|
nonce |
6 | 1 |
qub_id |
7 | 2 |
version |
8 | 3 |
ciphertext |
11 | 4 |
Bait pertama OuterWrapper CBOR oleh itu ialah pengepala peta panjang-tentu untuk peta 4-entri (0xA4).
13.4 Pengikatan AAD kepada qub_id
Pembungkus mengikat qub_id sebagai data pengesahan tambahan AEAD. Ini ialah pertahanan struktur yang memikul beban terhadap tiga kelas serangan:
| Serangan | Pertahanan |
|---|---|
Alih siferteks di bawah medan qub_id yang berbeza dalam pembungkus |
Ketidakpadanan AAD → pengesahan AEAD gagal |
| Campurkan fragmen URL qub A dengan bait storan kekal qub B | Ketidakpadanan AAD → pengesahan AEAD gagal |
Usik medan qub_id pembungkus selepas muat naik |
Ketidakpadanan AAD → pengesahan AEAD gagal |
Membawa qub_id dalam teks biasa pembungkus tidak melemahkan imuniti penghitungan dengan ketara — qub_id itu sendiri ialah cincang SHA3-256 bagi praimej §4.1 tanpa praimej boleh-pulih daripada cernaan, dan penghitung yang telah menuai bait pembungkus tidak belajar apa-apa daripada qub_id yang kelihatan yang tidak boleh disimpulkan daripada kewujudan muat naik itu sendiri.
13.5 Algoritma Bungkus dan Nyahbungkus
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
Keruntuhan mod-kegagalan. K yang salah, nonce yang salah, ketidakpadanan AAD, dan siferteks yang diusik semuanya menghasilkan ralat DECRYPT_FAILED yang sama. Ini ialah sifat AEAD yang disengajakan: membezakan mod kegagalan akan mencipta saluran sampingan yang penyerang jauh boleh siasat dengan menghantar pembungkus cacat dan mengetik tindak balas. Implementasi rujukan MUST meruntuhkan semua kegagalan AEAD kepada satu bentuk ralat.
13.6 Bahan Kunci dan Pengedaran
Kunci pembungkusan K ialah nilai rawak seragam 256-bit yang dihasilkan setiap-qub oleh CSPRNG. Implementasi rujukan mendapatkannya daripada:
- Pencipta WASM:
getrandom(WebCrypto di bawah belakangwasm_js). - Pemanggil API meterai sisi-pelayan: CSPRNG tempatannya; pemanggil membekalkan dan mengekalkan
Ksebagaiwrapper_key_b64url. Worker menggunakanKdalam memori untuk pembungkus tetapi MUST NOT mengekalkannya. Ini membolehkan cubaan semula idempoten memulihkan respons yang disunting menggunakan keupayaan yang dikekalkan pemanggil dan bukannya bergantung pada rahsia sekali-guna yang dijana pelayan.
Pengedaran: K MUST dikodkan sebagai base64 URL-selamat (RFC 4648 §5, tanpa pelapik) dan ditambah kepada pautan penghantaran sebagai komponen fragmen:
delivery_url = <origin>/c/<arweave_tx_id>#<base64url(K)>
Fragmen tidak pernah dihantar kepada mana-mana pelayan oleh pelayar yang mematuhi. Saluran pemulihan (indeks sejarah sisi-pelayan, hantar-auto e-mel pilih-masuk) yang mengekalkan pautan penghantaran penuh — termasuk fragmen — di luar peranti pengguna ialah pertukaran eksplisit terhadap postur kripto-pemusnahan lalai dan MUST dipintu pada persetujuan eksplisit pengguna.
Kehilangan fragmen. Jika pengguna kehilangan fragmen URL dan tiada saluran pemulihan, qub tidak boleh dibaca. Inilah pertukaran memikul-beban reka bentuk dan MUST didedahkan kepada pengguna pada masa pemeteraian. MVP mengukuhkan pendedahan masa-pemeteraian dengan salinan eksplisit "simpan URL ini" dan saluran pemulihan e-mel disahkan untuk pengguna yang pilih-masuk.
13.7 Di Luar Skop bagi Bahagian Ini
- Tandatangan pengarang (§9) tidak berubah: tandatangan dikira di dalam
QubEnvelopedalaman dan dipulihkan selepas nyahbungkus → nyahsulit tlock → hurai CBOR. - qub peribadi yang disulitkan-penerima (ciri Fasa-2 yang dikhaskan, belum dispesifikasikan) digubah di atas pembungkus ini sebagai tahap kerahsiaan kedua; kedua-dua tahap boleh aktif serentak.
- Perjanjian (§6, content_type
0x03) dibungkus dengan tepat seperti qub teks; pembungkus adalah buta-bait kepada jenis kandungan dalaman.
13.8 qub awam (peninggalan pembungkus)
Pembungkus luar bersifat pilihan pada lapisan penghantaran. Seorang pencipta boleh memeterai qub sebagai awam, dan dalam kes itu SealedQubCbor berkanun ditulis ke storan kekal secara langsung, tanpa lapisan OuterWrapper dan tanpa kunci K:
SealedQubCbor bytes ──(public)──▶ uploaded to permanent storage as-is
SealedQubCbor bytes ──(private)─▶ AES-256-GCM(K, …) ▶ OuterWrapper ▶ uploaded
Sebuah qub awam terkunci-waktu tetapi tidak berpintu-pautan: ia kekal tidak boleh dibaca sehingga pusingan drandnya diterbitkan (lapisan tlock tidak berubah), tetapi selepas buka kunci sesiapa sahaja yang mempunyai arweave_tx_id boleh menyahsulitnya — tiada fragmen URL diperlukan, kerana tiada K. Inilah pertukaran yang disengajakan untuk permukaan yang pelayan mesti pacu: e-mel pemberitahuan-pendedahan, sematan pihak-ketiga, dan SEO pasca-pendedahan yang lebih kaya semuanya memerlukan pautan yang berfungsi tanpa rahsia yang pelayan tidak pernah pegang (§13.6).
Akibat yang pengeluar MUST ambil kira:
- Tiada imuniti penghitungan. qub awam mengetepikan sifat imuniti-penghitungan §13.1 secara reka bentuk. Perkhidmatan muat naik rujukan mencap tag storan-kekal
Visibility: publicpadanya (dan hanya padanya) supaya ia sengaja boleh ditemui; qub peribadi tidak membawa tag sedemikian dan mengekalkan ketakbolehbezaan baitnya. - Tajuk teks biasa terdedah pada masa pemeteraian. Medan
title§3.2 ialah teks biasa di dalamSealedQubCbor. Di bawah pembungkus ia tersembunyi sehingga pembaca membekalkanK; tanpa pembungkus ia boleh dibaca seluruh dunia pada storan kekal dari saat muat naik, sebelum buka kunci. Aplikasi pencipta yang mematuhi MUST mendedahkan ini pada masa pemeteraian. - Pengesanan bersifat struktur. Pembaca/sematan yang mematuhi membezakan kedua-dua bentuk melalui hurai: bait yang dihurai sebagai
OuterWrappermengambil laluan nyahbungkus-dengan-K; bait yang dihurai sebagaiSealedQubCbortelanjang diterima secara langsung. Tiada bendera wayar diperlukan, danqub_idtidak mengikat keterlihatan — kandungan yang sama adalah serupa bait-demi-bait pada lapisanSealedQubsama ada dimeterai awam atau peribadi.
Peribadi (terbungkus) kekal sebagai lalai; awam ialah pilihan pencipta setiap-qub yang eksplisit.
14. Vektor Ujian
14.1 Derivasi qub_id
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
Implementasi MUST menghasilkan nilai body_hash dan qub_id yang serupa untuk input ini. Vektor ujian ini SHOULD menjadi ujian unit pertama yang ditulis. Nilai berkanun di atas dikira oleh implementasi rujukan dan MUST sepadan bit-demi-bit. Susun atur praimej sejarah (pra-pelancaran — tiada qub langsung bergantung pada nilai ini): qub_id V1.0 92-bait ialah 3d9fc2390eab043d38a1669ed3b71be76f9eefe872b9569ab1aaa027b88392b0; qub_id V1.1 100-bait (selepas melipat outcome_at_or_zero) ialah b0d032898ad629795150fdcb3f84e518f59ed05b7a2a82bc24ebdb87f52144ed. V1.2 melipat drand_round ke dalam dan menaikkan pemisah domain kepada QUB_ID_V2.
14.2 Pemetaan Pusingan-Buka-Kunci
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 Pergi-Balik CBOR Berkanun
Implementasi MUST mengesahkan bahawa serialize(parse(serialize(qub))) == serialize(qub) untuk semua input yang sah. Ini ialah ujian sifat, bukan vektor tunggal.
14.4 CBOR PactTerms (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)
Bait CBOR berkanun dan body_hash SHA3-256 dikira oleh implementasi rujukan. Implementasi MUST menghasilkan CBOR yang serupa bait-demi-bait untuk input ini.
Implementasi juga MUST mengesahkan bahawa serialize(parse(serialize(pact))) == serialize(pact) untuk semua input PactTerms yang sah (ujian sifat).
14.5 Vektor Silang-Bahasa Pembungkus Luar
Pembungkus luar (§13) mempunyai lekapan berkanun yang berasingan di crates/qub-core/tests/vectors/wrapper_v1.json. Setiap kes menetapkan tupel (key, nonce, qub_id, sealed_cbor) sebagai input heks legap dan menegaskan output expected_wrapper_hex tertentu. Kedua-dua implementasi rujukan menggunakan fail JSON yang sama:
- 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).
Lekapan tersebut kini menyemat tiga kes:
| Kes | Liputan |
|---|---|
basic-text-public |
Bentuk SealedQub realistik terkecil; tiada medan pilihan. Menetapkan bentuk pembungkus berkanun untuk qub tipikal-v1.0. |
with-recipient-pubkey |
SealedQub dengan recipient_pubkey ditetapkan (laluan Fasa 2). Set kunci CBOR dalaman yang berbeza, qub_id yang berbeza. |
longer-body |
Badan ~4 KiB — melatih awalan panjang CBOR pelbagai bait dalam kedua-dua envelope dalaman dan siferteks luar. |
Implementasi MUST menghasilkan expected_wrapper_hex yang serupa bait-demi-bait untuk input yang direkodkan. Penjanaan semula lekapan memerlukan QUB_REGEN_VECTORS=1 cargo test -p qub-core --test wrapper_vectors dan dikhaskan untuk perubahan format yang disengajakan.
15. Tadbir Urus Profil Kripto (Masa Hadapan)
Bahagian ini bersifat informatif untuk v1 dan menjadi normatif kali pertama algoritma kedua memasuki mana-mana primitif kriptografi qub.
15.1 Postur Semasa
Protokol v1 mengikat tepat satu algoritma setiap primitif:
- Tandatangan: ML-DSA-65 (
sig_alg = 0x01; kunci awam 1952-bait, tandatangan 3309-bait) dan tidak bertandatangan (sig_alg = 0x00). Pendaftaran §9.2 tidak menakrifkan nilai lain; pengesah v1 MUST menolak setiapsig_algdi luar{0x00, 0x01}. Entri Ed25519 masa depan dijangka (§15.3) tetapi tidak diperuntukkan dalam v1. - Kunci masa: quicknet drand sahaja — cincang rantai, kunci awam, masa genesis, dan tempoh ialah parameter rangkaian tetap yang dibawa oleh rujukan
DrandTimelockProvider::quicknet()(crates/qub-core/src/tlock.rs) danconfig/drand-endpoints.json. - Pembungkus luar: AES-256-GCM v1 sahaja (§13).
Pengesah kini mengekodkan-keras panjang kunci dan tandatangan setiap primitif. Tiada permukaan kelincahan didedahkan oleh format wayar.
15.2 Bentuk yang Dimaksudkan
Apabila algoritma kedua memasuki protokol, pengesah akan dikonfigurasi untuk CryptoProfile bernama (cth., ExqubV1) yang menyenaraikan set tepat nilai dibenarkan setiap primitif — sig_alg, rantai drand, versi pembungkus, jenis kandungan. Profil ditetapkan pada masa pengesahan, tidak pernah dirunding dalam-jalur. Apa-apa nilai di luar profil aktif ditolak.
Ini menjamin bahawa menambah ML-DSA-87 atau mengaktifkan Ed25519 tidak boleh secara retroaktif melemahkan konfigurasi pengesah sedia ada: pengesah v1 kekal sebagai pengesah v1 walaupun selepas profil v2 diterbitkan.
15.3 Syarat Pencetus
Naikkan §15 kepada status normatif apabila mana-mana yang berikut dicadangkan:
- Bait
sig_algkedua (pengaktifan Ed25519, ML-DSA-87, atau apa-apa entri baharu dalam pendaftaran §9). - Rantai drand kedua dalam penggunaan pengeluaran.
- Versi pembungkus-luar kedua.
Sehingga itu §15 ialah pemegang tempat yang menetapkan bentuk migrasi supaya PR masa hadapan mendarat terhadap sasaran yang diketahui dan bukannya bertikai semula permukaan rundingan dari awal.
16. Log Ketelusan dan Tahap Ketahanan (Reka Bentuk — semakan selesai)
Status. Bahagian ini ialah spesifikasi reka bentuk. Format wayar, pencincangan, dan model amanah di bawah bersifat normatif untuk implementasi, tetapi belum ada kod log-ketelusan dihantar lagi. Semakan luaran W5 telah selesai: §16.15 merekodkan keputusan yang telah diselesaikan dan kekangan pelancaran yang mengikat yang terhasil daripadanya. Implementasi boleh diteruskan di bawah kekangan tersebut. §16 kekal berpandangan ke hadapan dalam erti yang sama seperti §15 — ia menetapkan sasaran supaya implementasi mendarat terhadap reka bentuk yang telah diputuskan dan bukannya menerbitkan semula model amanah dalam semakan kod. Ia bersifat tambahan secara ketat — setiap qub sedia ada mengekalkan transaksi Arweave individunya dan tiada perubahan pada format wayar
SealedQub/QubEnvelope.
16.1 Rasional dan Tahap Ketahanan
Hari ini ketahanan qub dan komitmen temporalnya kedua-duanya bersandar pada satu transaksi Arweave setiap-qub (§11). Ini menggandingkan kependaman meterai kepada kemuktamadan Arweave, menjadikan muat naik setiap-qub sebagai had kos produk (ARWEAVE_DAILY_CEILING), dan tidak memberikan susunan yang boleh dikesan-usik merentas qub. Log ketelusan menambah dua lapisan di bawah dan sekeliling tahap tunggal itu:
| Tahap | Nama | Jaminan | Bila |
|---|---|---|---|
| T1 | Akuan segerak R2-dahulu | Lantai ketahanan — bait dimeterai ditulis ke storan tahan lasak sebelum meterai kembali (< 300 ms p95). |
Setiap qub, secara segerak (§16.10). |
| T2 | Kemasukan log-ketelusan berkelompok | Komitmen tambah-sahaja sejagat, boleh-dikesan-usik + susunan menyeluruh, dilabuhkan ke Arweave. | Setiap qub, ditangguh + berkelompok (§16.5–16.7). |
| T3 | Kekekalan Arweave setiap-qub | Transaksi Arweave individu untuk qub. | Jualan tokok berbayar, dan sandaran ketakbersediaan-Arweave (§16.8). |
T2 menjadikan Arweave setiap-qub sebagai pilihan (T3) dan bukannya satu-satunya laluan ketahanan. ARWEAVE_DAILY_CEILING ditarik balik sebagai had produk dan diturunkan kepada pemutus litar pada dompet labuh khusus sahaja (§16.7); meterai pengguna tidak pernah ditolak kerana melebihinya.
Kejujuran ketahanan (diselesaikan — §16.15 Q6). Ketahanan tidak berundur: tulisan R2 T1 bersifat segerak dan tulis-sekali, jadi qub tahap-percuma yang tidak membeli T3 adalah tahan lasak sepenuhnya pada saat meterai kembali. Apa yang menjadi kasar ialah masa komitmen had-atas yang boleh dibuktikan: untuk qub percuma ia menjadi masa blok labuh dan bukannya masa blok transaksi setiap-qub. Pada volum rendah — keadaan pelancaran-awal dan luar-puncak yang realistik — kadens harian penuh ialah lantai tipikal, bukan tepi yang jarang. Oleh itu rangka produk ialah had atas dengan tiada kependaman berangka yang dikomited — "dimeterai dan tahan lasak sekarang; cap masa awam bebas ditambah pada labuh log seterusnya (biasanya harian)" — dan bukti komitmen jam-tepat ialah sifat T3 berbayar, didedahkan pada permukaan perbandingan-tahap dan dalam syarat (§16.11, §16.15 Q6). Sebarang had masa ialah SLO dalaman sahaja, tidak pernah SLA yang dipasarkan.
16.2 Struktur LogLeaf (dua bentuk yang dikomited)
Entri log ialah LogLeaf, dikodkan sebagai CBOR berkanun tulisan-tangan di bawah profil §3.1 (panjang-tentu, tiada tag, tiada apungan, integer bentuk-terpendek, teks NFC, medan pilihan ditinggalkan apabila tiada, kunci diisih mengikut panjang bait terkod menaik kemudian secara bait). Pengawal berkanun parse → enkod-semula → banding §3.1 dikenakan pada laluan enkod sebelum pencincangan (bukan hanya pada nyahkod), supaya dua implementasi tidak boleh berselisih tentang bait daun melalui perbezaan lebar-integer atau susunan-kunci. Semua integer ialah u8 / u64 / i64; semua cernaan ialah rentetan bait 32-bait (bstr[32]). Id transaksi Arweave yang disimpan ialah cernaan SHA-256 32-bait mentah yang dibawa sebagai bstr[32], tidak pernah rentetan teks base64url (sepadan dengan §3.3).
Daun mempunyai dua bentuk yang dipilih oleh bait kind, kerana pada laluan muat naik lalai Worker bersifat buta-bait: POST /api/v1/upload menerima hanya qub_id dan unlock_at sebagai penegasan klien yang tidak dipercayai — body_hash, drand_round, created_at, dan drand_chain_version semuanya dimeterai di dalam pembungkus luar §13, yang kuncinya Worker tidak pernah pegang. Hanya laluan meterai-pelayan (POST /api/v1/seal) yang menerbitkan body_hash / drand_round daripada teks biasa. Satu bentuk daun yang membawa body_hash + drand_round oleh itu akan mengkomit nilai yang operator tidak pernah sahkan bagi majoriti qub sebenar. Pembahagian ini memastikan setiap nilai yang dikomited kekal jujur:
| Kunci | Pjg. terkod | Jenis | Kehadiran | Maksud |
|---|---|---|---|---|
seq |
4 | u64 |
wajib | Indeks daun global berasaskan-0; kedudukan yang dikomit oleh bukti kemasukan. |
kind |
5 | u8 |
wajib | 0x01 disahkan (meterai-pelayan) atau 0x02 ditegaskan (meterai-klien / muat naik buta-bait). |
ref |
4 | bstr[32] |
wajib | Id rujukan daun. Disahkan → qub_id mentah. Ditegaskan → id terbutakan SHA3-256(qub_id ‖ log_blind_secret) (§16.2.1). |
chash |
6 | bstr[32] |
wajib | Alamat kandungan SHA3-256(stored_bytes) — satu-satunya ikatan kandungan yang Worker sentiasa boleh kira dengan jujur, pada kedua-dua laluan. |
unlock_at |
10 | i64 |
wajib | Disalin (disahkan) atau ditegaskan (ditegaskan); disahkan > 0 sebelum ia memasuki daun. |
received_at |
12 | i64 |
wajib | Jam-dinding Worker pada akuan-R2. Bukan-keterangan (ditegaskan-operator; §16.6). Hadir untuk perihalan-diri, tidak pernah satu bukti. Disahkan > 0. |
body_hash |
10 | bstr[32] |
kind=0x01 sahaja |
Ditinggalkan pada 0x02 — Worker tidak memilikinya di bawah §13. |
drand_round |
12 | u64 |
kind=0x01 sahaja |
Ditinggalkan pada 0x02. |
Daun kind=0x02 sengaja tidak mengkomit body_hash mahupun drand_round: ia menyaksikan komitmen dan susunan siferteks legap pada alamat-kandungan chash, mendakwa qub_id dan unlock_at — bukan teks biasa atau pusingannya. Kaki teks-biasa/pusingan untuk qub ditegaskan datang daripada pengesahan berkas-.qub §11 sedia ada, bukan daripada log (§16.11). drand_chain_version tiada dalam daun (ia di dalam pembungkus pada laluan lalai); kebutiran rantai terletak pada labuh (§16.7). Disiplin pengekod: tolak ref atau chash yang semuanya-sifar, dan tolak unlock_at / received_at bukan-positif, mencerminkan pengawal sentinel outcome_at > 0 dalam cbor.rs.
16.2.1 Pembutaan qub-peribadi
Log tidak boleh menjadi orakel penghitungan yang pembungkus luar §13 wujud untuk mencegahnya (§13.1). Untuk qub peribadi (terbungkus) daun asserted mengkomit pengecam terbutakan SHA3-256(qub_id ‖ log_blind_secret), di mana log_blind_secret ialah rahsia yang dipegang-pelayan, dan meninggalkan body_hash. Pihak ketiga tidak boleh mengikat daun sedemikian kepada qub_id tertentu; pemegang qub, yang mempunyai URL penghantaran dan oleh itu qub_id, boleh mengira semula pembutaan untuk mengesahkan kemasukan mereka sendiri. qub awam (sudah boleh dihitung, sudah membawa tag Arweave Visibility: public mengikut §13.8) mengkomit qub_id mentah. Inilah satu tempat di mana kebolehsahihan berdiri-sendiri sengaja mengalah kepada invarian kerahsiaan yang memikul-beban; ikatan berdiri-sendiri untuk qub peribadi ialah chash (§16.9).
Jagaan log_blind_secret (diselesaikan — §16.15 Q4). Pembutaan melindungi ketakbolehpautan daun, bukan kerahsiaan teks biasa (pembungkus §13 memegangnya secara bebas). Pada kompromi log_blind_secret, untuk mana-mana qub_id yang penyerang sudah pegang atau boleh bina semula (setiap qub yang berkas/URLnya ia ada, ditambah mana-mana qub_id entropi-rendah atau awam) ia mengira semula ref daun dalam satu cincang dan memautkannya — ini ialah pemautan langsung populasi yang diketahui, bukan paksaan-kasar terhadap ruang yang tidak diketahui. Kelaskan log_blind_secret sebagai rahsia gred-korelasi/Sybil dalam tahap jagaan yang sama seperti rahsia pelayan lain, dan putar ke hadapan sahaja (putaran membutakan-semula daun masa hadapan; ia tidak boleh menyahpautkan secara retroaktif daun yang sudah dilabuhkan).
16.3 Pencincangan Daun dan Nod
Pencincangan terpisah-domain RFC 6962 §2.1 dengan SHA-256 digantikan oleh 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
Bait awalan domain 0x02 (rantai entri, §16.4) dan 0x03 (cincang STH, §16.6) dikhaskan dan bercerai daripada ini. Ia adalah bait tunggal dan oleh itu tidak boleh berlanggar dengan pemisah domain ASCII 10-bait sedia ada (QUB_ID_V2, dll.). Pokok ialah pokok kiri-penuh tidak seimbang RFC 6962 (setiap pisahan dalaman pada kuasa-dua terbesar yang lebih kecil secara ketat daripada bilangan daun subpokok), yang membenarkan bukti kemasukan dan ketekalan berkongsi satu algoritma laluan-audit. Spesifikasi rujukan membawa pseudokod derivasi kiri/kanan eksplisit dan menyemat vektor ujian bukan-kuasa-dua (5-daun) supaya kes naik-pangkat tepi-kanan — yang disembunyikan oleh vektor 4-daun — dilatih.
16.4 Pengrantaian Cincang (dalaman)
LogDO menyelenggara rantai entri dalaman untuk ketekalan-nahas sahaja. Ia tidak pernah diterbitkan dan tidak pernah menghadap-pengesah:
entry_chain[seq] = SHA3-256(0x02 || entry_chain[seq-1] || leaf_hash[seq])
entry_chain[-1] = SHA3-256("QUB_TLOG_GENESIS_V1")
Autoriti tambah-sahaja yang diterbitkan ialah akar Merkle kumulatif + labuhnya (§16.5–16.6), tidak pernah susunan mentah di mana operator kebetulan menyajikan daun: rantai mengira semula untuk apa-apa susunan yang disajikan, jadi hanya akar yang dilabuhkan menyemat kedudukan berkanun.
16.5 Pokok Merkle Kumulatif dan Pengelompokan
Terdapat satu pokok RFC 6962 yang sentiasa membesar ke atas semua daun dalam susunan seq — bukan pokok terasing setiap-kelompok. (Pembinaan setiap-kelompok berantai-daun-bawaan ditolak: ia bukan hubungan awalan yang benar, jadi "bukti ketekalan"nya tidak munasabah.) Pokok kumulatif memberikan bukti ketekalan RFC 9162 yang tulen dan membenarkan satu labuh terkini membuktikan kemasukan untuk mana-mana qub yang lebih lama.
Objek Tahan Lasak LogDO ialah penulis tunggal (blockConcurrencyWhile, mencerminkan QuotaDO / EntitlementDO) — menambah kepada log dikongsi ialah baca-ubah-tulis pada keadaan dikongsi dan oleh itu MUST melalui DO, tidak pernah KV. Ia menyimpan-cache sempadan tepi-kanan pokok (O(log n) cincang) supaya menutup satu kelompok ialah O(batch). Satu kelompok ialah set daun yang dilabuhkan bersama; pencetusnya boleh dikonfigurasi, tidak dibekukan-protokol: kemajuan tree_size sekurang-kurangnya LOG_BATCH_MAX_LEAVES (lalai 4096), atau usia mencapai kadens labuh, atau pembilasan paksa apabila meterai T3 berbayar mendarat. root_i ialah Cincang Pokok Merkle kumulatif ke atas daun 0 .. tree_size_i.
16.6 Kepala Pokok Bertandatangan melalui Labuh Arweave
Transaksi labuh Arweave ialah Kepala Pokok Bertandatangan dan menggantikan tandatangan operator untuk kepala pokok itu sendiri: labuh harian tidak memerlukan kunci qub kerana owner tx Arweave ialah tandatangannya. Tesis benteng kekal — substrat tidak berubah, bukan rahsia yang dipegang-qub, yang memikul-beban untuk akar yang dilabuhkan.
Terdapat tepat satu kunci tandatangan qub panas dalam reka bentuk, dan ia disemat: kunci resit setiap-meterai (§16.10). Kunci awamnya dikomit dalam LogProfile (diedarkan dengan pengesah) dan ditandatangani-silang oleh anchor_owner, supaya pengesah mengesahkan resit terhadap akar disemat yang sama seperti labuh. Inilah penyelesaian §16.15 Q2 — kunci resit tidak-disemat, boleh-diputar-operator akan boleh-disangkal (operator boleh menafikan kunci itu miliknya), yang akan membatalkan nilai akauntabiliti resit terhadap penyerang peringkat-operator yang resit itu wujud untuk menghalangnya. Jadi: qub memegang tiada kunci tandatangan-log yang tidak-disemat; kunci resit disemat dan ditandatangani-silang oleh anchor_owner.
SignedTreeHead ialah CBOR berkanun (kunci mengikut panjang terkod): size:u64, root:bstr[32], batch:u64, prev:bstr[32] (sth_hash sebelumnya; genesis = 32 bait sifar), log_id:bstr[32], first_seq:u64, anchored_at:i64. Cincangnya ialah sth_hash = SHA3-256(0x03 || canonical_cbor(SignedTreeHead)).
Akar amanah disemat. log_id = SHA3-256("QUB_TLOG_V1" || anchor_owner_address). Pengesah yang mematuhi MUST memerlukan anchor_tx.owner == LogProfile.anchor_owner, di mana anchor_owner (dan kunci awam kunci-resit) dibakar ke dalam qub_core sebagai LogProfile — bersama-sama pemalar quicknet yang sudah ada dalam DrandTimelockProvider::quicknet() — dan diedarkan dengan binari pengesah. Pengesah juga MUST mengesahkan pengikatan tx Arweave data → tx_id secara setempat dan bukannya mempercayai respons /raw/ pintu masuk. Ini menutup lubang ekuivokasi dompet-jahat: "dilabuhkan pada Arweave" tidak bermakna sehingga pengesah menyemat dompet yang mana.
Putaran ialah lanjutan tadbir urus §15, bukan guna-semula (diselesaikan — §16.15 Q3). Permukaan profil §15.2 kini hanya menyenaraikan sig_algs / rantai drand / versi pembungkus / jenis kandungan, dan pencetus §15.3 tidak menyenaraikan satu pun daripada ini — LogProfile / anchor_owner belum lagi dalam permukaan §15. Tadbir urus putaran oleh itu MUST dibina: §15.3 dilanjutkan (di bawah) untuk menambah pencetus LogProfile, dan satu putaran ialah kenaikan LogProfile bertandatangan yang dihantar dalam kemas kini pengesah. Putaran dirancang membawa tandatangan-silang keluar → masuk; putaran dipacu-kompromi tidak boleh (kunci keluar tidak dipercayai/tidak tersedia tepat ketika itu) dan berbalik kepada kenaikan yang ditadbir-§15, dengan semakan cabang labuh-sebelumnya (di bawah) membatasi kerosakan dalam tempoh peralihan.
Tetingkap ekuivokasi (parameter amanah kelas-pertama). Sebuah daun tahan-ekuivokasi hanya sebaik labuh penaungnya disahkan Arweave. Tetingkap ialah received_at → pengesahan labuh (≤ kadens + kemuktamadan Arweave). Dalamnya satu-satunya jaminan ialah resit meterai disemat (§16.10) dan integriti operasi qub. Tiga artifak akauntabiliti menjadikan ini jujur dan bukannya melambai-tangan (model saksi ialah penyelesaian §16.15 Q2):
- Resit meterai bertandatangan disemat — analog SCT yang dikembalikan dalam respons muat naik (§16.10), ditandatangani oleh kunci resit disemat, ditandatangani-silang oleh
anchor_owner. Sebuah daun yang digugurkan sebelum labuhnya meninggalkan mangsa satu resit tak-boleh-sangkal untuk diterbitkan, menutup lubang peninggalan-senyap. - Metodologi pemantau diterbitkan + jalan rantai-sebelumnya — rantai
prevlabuh dijalani kepala→genesis; satu cabang (dua labuh pada satusizedenganrootberbeza, atauprevyang patah) ialah bukti boleh-terbit tentang salah laku. Pengesanan ekuivokasi ialah komitmen operasi yang dinyatakan, bukan andaian senyap. - Kepala diterbit-sendiri berkembar — setiap kepala baharu
{sth_hash, tree_size}disiarkan ke repositori GitHub awam, tambah-sahaja yang dimiliki-qub yang khusus (kaki penerbitan-sendiri boleh-dikesan-usik yang memikul-beban), dengan siaran sosial sebagai sokongan usaha-terbaik sahaja. Penyiaran yang gagal MUST mengejut (page) (bukan gagal-senyap).
Had kejujuran (kekangan mengikat). Kerana qub mengawal kedua-dua permukaan penyiaran, ini diterbit-sendiri, bukan disaksi secara bebas. Tiada permukaan produk, pemasaran, atau perundangan boleh mendakwa log "disaksi secara bebas"; dakwaan yang dibenarkan ialah bahawa ekuivokasi boleh dikesan dan meninggalkan resit yang tak-boleh-sangkal. Saksi pihak-ketiga bebas yang benar ditangguh kepada kenaikan tadbir urus §15 masa hadapan.
received_at ditegaskan-operator dan tiada dakwaan boleh bersandar padanya — ia tidak pernah dikemukakan sebagai bukti atau sebagai sokongan pertikaian pada mana-mana permukaan produk / perundangan / API / pemaparan-bukti. Masa blok labuh Arweave T ialah satu-satunya cap masa tanpa-amanah (had atas pada "dilog menjelang"). Sebarang semakan kewarasan pemantau pada received_at MUST membanding terhadap T, bukan terhadap medan STH anchored_at yang dikawal-operator; semakan sedemikian ialah pengawal terhadap pepijat jam operator jujur sahaja, bukan kawalan akauntabiliti terhadap operator jahat (§16.15 Q5).
16.7 Format Transaksi Labuh dan Kadens
AnchorBundle ialah badan transaksi Arweave CBOR-berkanun, ditulis melalui pembungkus §16.8: ver:u8, sth:bstr (bait SignedTreeHead berkanun), prev_anchor:bstr (id tx labuh sebelumnya bait mentah; ditinggalkan pada genesis), chain_hash:tstr (rantai drand yang berkuat kuasa — quicknet), dan aliran daun-CBOR kelompok dalam susunan seq supaya labuh adalah berdiri-sendiri: satu pemantau menerbitkan-semula root daripada badan dengan sifar kebergantungan qub. (Jika aliran daun menjadi besar pada volum tinggi, semakan masa hadapan boleh mengkomit hanya julat daun melalui rujukan; dicatat, tidak diterima pakai dalam v1.)
Tag Arweave sengaja boleh-dihitung — log bertujuan untuk ditemui, tidak seperti qub peribadi: App-Name: qub-tlog, Anchor-Format: 1, Log-Id: <hex>, Batch: <n>, Tree-Size: <n>, Root: <hex>, Prev-Anchor: <tx>, Content-Type: application/cbor. Tag ialah petunjuk tidak dipercayai; badan CBOR ialah satu-satunya autoriti.
Kadens: harian secara lalai, ditinjau semula dengan volum (pencetus saiz memendekkan-auto kadens berkesan di bawah beban); satu meterai T3 berbayar memaksa labuh supaya pelanggan yang membayar tidak pernah menunggu sehari. Dompet labuh bersifat khusus dan halaju-rendah, terpisah daripada dompet muat naik — ia MUST menjadi JWKnya sendiri (kunci tersendiri, bukan peranan logik pada dompet muat naik) supaya kompromi dompet-muat-naik tidak boleh memalsukan labuh — dengan bajet transaksi-labuh setiap-hari yang keras (ARWEAVE_DAILY_CEILING yang diturunkan). Postur jagaan dinyatakan dengan jelas: kunci panas berskop-sempit dengan pemutus litar ketat dan baki rendah, bukan "sejuk" — sebuah dompet yang menandatangani-auto setiap hari tidak boleh sejuk, dan spesifikasi tidak berpura-pura sebaliknya.
16.8 Pembungkus ANS-104
Pengekod DataItem ANS-104 dan penandatangan deep-hash dalaman, lebih kurang 300 baris, Web Crypto sahaja, sifar kebergantungan npm (kedua-dua SDK Turbo gagal pintu rantai-bekalan npm ci --ignore-scripts). Susun atur bait DataItem:
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
Pencincangan ialah deepHash Arweave — cernaan SHA-384 rekursif (keperluan wayar Arweave, crypto.subtle.digest("SHA-384")) ke atas ["dataitem", "1", sig_type, owner, target, anchor, encoded_tags, data] — kemudian RSA-PSS ke atas deep hash dengan JWK dompet melalui crypto.subtle; id = base64url(SHA-256(signature)). SHA-384 di sini dikuarantinkan sebagai primitif wayar-Arweave-sahaja, tidak pernah primitif amanah qub (§15 merekodkan pagarnya; pencincangan amanah qub ialah SHA3-256 di seluruhnya).
Satu laluan kod menyajikan tiga pengguna: kekekalan setiap-qub T3 berbayar, sandaran ketakbersediaan-Arweave (baris-gilir DataItem, kembalikan akuan R2-dahulu tanpa mengira — ini menutup jalan-buntu 503 ARWEAVE_UNAVAILABLE semasa), dan penulisan AnchorBundle. Skema tandatangan (diselesaikan — §16.15 Q8): v1 menandatangani dengan RSA-PSS (jenis tandatangan 1) menggunakan semula mekanisme JWK dompet Arweave sedia ada (sifar jagaan kunci baharu tahan-lama, menyajikan tesis "satu rahsia kurang"); Ed25519 ditangguh kepada laluan migrasi-PQ §15.
Deep hash buatan-tangan ialah kod berisiko-tertinggi, berliputan-semula jadi-terendah dalam W5, jadi pemintuannya tidak boleh dirunding (§16.15 Q8):
- Lekapan silang-bahasa
tlog_v1.json(Rust + TS, corakwrapper_v1.json§14.5) meliputi deep-hash, bait DataItem + id, cincang daun, akar 5-daun + laluan audit, satu cincang STH, satu bukti kemasukan, dan satu bukti ketekalan — dalam kedua-dua arah tandatangan dan pengesahan (arah pengesahan penting kerana semakan tx → tx_id setempat §16.6 menarik deep hash ke dalam setiap pengesah berdiri-sendiri, bukan hanya penulis). - Satu pusing-balik antaroperasi sekali sahaja melalui pembungkus ANS-104 rujukan, diguna sebagai data ujian statik sahaja — tidak pernah kebergantungan masa-jalan npm (postur Web-Crypto-sahaja / tiada-skrip-pemasangan kekal).
- Laluan deep-hash + RSA-PSS MUST pusing-balik melalui primitif
crypto.subtleyang sama yang digunakan pengeluaran, supaya pengekod dalaman serasi-bait. - Satu pemantau penerimaan pasca-bungkus berterusan mengesahkan setiap labuh / sandaran DataItem benar-benar mencapai penerimaan Arweave, dengan penggera + pemutus litar — kerana deep hash juga menyajikan baris-gilir sandaran ketakbersediaan-Arweave, jadi regresi senyap akan mengisi baris-gilir itu dengan item yang ditolak-rangkaian semasa gangguan tepat yang ia wujud untuk meliputinya.
16.9 Bukti Kemasukan dan Ketekalan
Kedua-duanya ialah RFC 9162, SHA3-256, disajikan sebagai CBOR berkanun.
InclusionProof — GET /api/v1/qub/:tx_id/proof: ver:u8, leaf:bstr (CBOR daun tepat — pengesah mengira semula leaf_hash sendiri dan tidak pernah mempercayai cincang yang dibekalkan), 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. Satu senarai kunci tak-taksa tunggal, disemat oleh vektor ujian.
Pengesahan berdiri-sendiri (tiada pelayan qub, melanjutkan §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).
Storan penyajian-bukti MUST berkunci-koordinat (diselesaikan — §16.15 Q7, prasyarat penghalang). Penjanaan bukti daun-sejuk adalah neutral-kebenaran hanya jika bahan audit R2 ialah storan nod-Merkle berterusan yang berkunci pada koordinat pokok mutlak (level, index) — bukan delta nod setiap-batch. Dengan storan berkunci-koordinat, mana-mana laluan audit (leaf i, size N) ialah set O(log N) R2 GET langsung dengan tiada pengiraan semula merentas sempadan kelompok; dengan storan berkunci-kelompok ia tidak, dan inilah jurang susun-atur-storan yang ditutup oleh penyelesaian ini. Badan daun juga boleh dialamatkan-kandungan oleh seq. Satu vektor ujian W5 MUST membuktikan daun sejuk era-genesis terhadap akar yang jauh-kemudian menggunakan hanya R2 + Arweave dengan storan LogDO dipadam, supaya dakwaan keselamatan-tuntutan-semula dalam §16.13 disokong dan bukannya ditegaskan. O(log N) R2 GET berjujukan tergolong hanya pada titik akhir bukti tak segerak — tidak pernah pada laluan panas meterai (§16.10) atau cron setiap-detik.
16.10 Susunan Akuan R2-Dahulu
Urutan POST /api/v1/upload menjadi:
- Pintu separuh-hadapan (auth, pengesahan, kunci serpihan kebolehkalbutan) — tidak berubah.
- Secara segerak
await QUB_CACHE.put(qub-cache/<tx_id>, wrappedBytes)— lantai ketahanan; juga menutup perlumbaan pra-cache W1 (dahulunyactx.waitUntilselepas penyerahan Arweave). - Secara segerak
await LogDO.append(leaf)— satu RPC DO dalam-colo; penulis tunggal menetapkanseq, melanjutkan rantai entri, dan mengemas kini sempadan. (RMW pada keadaan dikongsi → DO, tidak pernah KV.) RPCappendmelakukan hanya itu — kerja tutup-kelompok MerkleO(batch)berjalan di luar RPC ini pada penggera LogDO, atau p95appendmelonjak setiap meterai ke-LOG_BATCH_MAX_LEAVES. - Kembalikan akuan sekarang — dengan resit meterai (ditandatangani oleh kunci resit disemat, §16.6) dan
{ tx_id, log_seq, anchor_status: "pending" }. Fan-out Arweave berbilang-saat dialih keluar daripada laluan kritikal. - Satu
ctx.waitUntilmembariskan kerja yang ditangguh: penyerahan Arweave setiap-qub (kini usaha-terbaik / berbayar; pada kegagalan ia menghala ke baris-gilir sandaran pembungkus dan bukannya 503 kepada pengguna) ditambah tulisan meta-sementara sedia ada. Tutup kelompok dan pelabuhan berjalan secara bebas daripada penggera LogDO dan cron labuh harian. Tiadactx.waitUntildi dalam gelung; kunci serpihan kebolehkalbutan sedia ada dikekalkan.
Bajet kependaman (diselesaikan — §16.15 Q7). Sasaran < 300 ms p95 ialah pintu pelancaran yang diukur, bukan andaian. Laluan kritikal yang jujur ialah baca KV separuh-hadapan + satu R2 PUT + dua Objek Tahan Lasak bersiri — debit kuota-meterai QuotaDO sedia ada dan append LogDO baharu — jadi bajet mesti mengambil kira dua pusing-pergi DO dalam-colo, bukan satu. Hantar penggera kependaman LogDO yang mencerminkan kepunyaan QuotaDO, dan layan regresi p95 sebagai penghalang keluaran.
16.11 Model Amanah — dakwaan tepat, berskop mengikut jenis daun
Untuk kind=0x01 (disahkan): "Kandungan ini — badan yang sepadan dengan body_hash, dikenal pasti oleh qub_id — dikomit ke log tambah-sahaja qub pada kedudukan seq dan wujud tidak lewat daripada masa blok Arweave T; ia tidak boleh dibaca secara kriptografi sehingga pusingan drand R = unlock_round(unlock_at)." Inilah triple penuh {ikatan pusingan tlock + kemasukan Merkle + akar dilabuhkan}.
Untuk kind=0x02 (ditegaskan, lalai): "Siferteks legap dengan alamat-kandungan chash, mendakwa qub_id dan unlock_at, dikomit ke log tambah-sahaja pada kedudukan seq dan wujud tidak lewat daripada masa blok Arweave T." Kaki pusingan dan badan dibekalkan oleh pengesahan berkas-.qub §11 sedia ada (qub_core::unlock), bukan oleh log; apa yang log tambah berbanding transaksi setiap-qub kosong ialah susunan boleh-dikesan-usik, masa komitmen had-atas tanpa-amanah, dan rintangan ekuivokasi.
Kedua-dua dakwaan mengecualikan, mengikut §11: pengarangan tanpa sig_alg ≥ 0x01, niat, dan pemasaan sub-kebutiran-labuh. Tiada satu pun membenarkan apa-apa dakwaan bersandar pada received_at.
Siling dakwaan (kekangan pelancaran mengikat — diselesaikan §16.15 Q1). Untuk qub percuma / lalai (kind=0x02), dakwaan kind=0x02 berskop di atas ialah siling pada apa yang mana-mana permukaan produk, pemasaran, syarat, atau pemaparan-bukti boleh menegaskan. Tiada permukaan boleh menyatakan atau membayangkan bahawa log membuktikan kandungan atau pusingan buka-kunci qub lalai — log membuktikan susunan + masa komitmen had-atas tanpa-amanah bagi siferteks legap. Bukti kandungan dan pusingan datang secara eksklusif daripada pengesahan berkas-.qub §11 sedia ada, yang bebas-log. Inilah penghalang pelancaran keras pada salinan, bukan keutamaan gaya; inilah penyelesaian yang memastikan laluan lalai buta-bait kekal jujur.
16.12 Versi dan Penyelarasan W3
Tiada kenaikan wayar SealedQub dan oleh itu tiada kenaikan versi-protokol (§12.1): log ialah sampingan yang mengkomit kepada medan dan bait sedia ada, jadi ia tidak memasuki sejarah versi-protokol §12.2. drand_chain_version pilihan W3 tidak disentuh dan kekal sebagai satu-satunya medan SealedQub pilihan. Sebaliknya log memperkenalkan ruang versinya sendiri yang bebas — LOG_VERSION_1, ANCHOR_FORMAT_1, InclusionProof.ver — mencerminkan kebebasan versi-pembungkus §12.4 (pembungkus membawa bait versi yang bebas daripada versi protokol, dan versi log mengikut pemisahan yang sama).
Penghantaran bukti diambil secara lalai, dengan tumpang-ikut pilihan. Satu bukti tidak boleh wujud pada masa meterai (labuh belum ditulis lagi), jadi berkas .qub masa-meterai kekal bebas-bukti. Pengesah W7 mengambil GET …/proof sekali, atau dalam mod luar-talian sepenuhnya membina semula bukti daripada AnchorBundle awam melalui pertanyaan Arweave pada Log-Id. Berkas .qub (W7) mengkhaskan ahli inclusion_proof pilihan — tiada pada meterai, diisi oleh eksport-semula pasca-labuh untuk arkib sejuk — mengikut corak "pilihan, ditinggalkan secara lalai, tambahan" yang sama seperti drand_chain_version W3.
16.13 Pengekalan
Tetingkap pengekalan untuk ekor terbuka LogDO, substrat penyajian-bukti R2, pembilang pemutus-litar labuh, dan baris-gilir sandaran pembungkus dispesifikasikan dalam docs/DATA-RETENTION.md. Prinsip: storan setiap-entri panas log (LogDO) boleh dituntut-semula pasca-labuh; bahan auditnya — storan nod-Merkle berkunci-koordinat (level, index) + badan daun beralamat-seq (§16.9) + labuh Arweave — adalah kekal. Menuntut-semula daun sejuk daripada DO tidak pernah membatalkan bukti yang dikeluarkan, kerana bukti diselesaikan terhadap storan nod R2 kekal itu dan labuh Arweave, bukan DO (dan vektor ujian DO-dipadam §16.9 membuktikannya).
16.14 Vektor Ujian
W5 menghantar lekapan silang-bahasa tlog_v1.json (§16.8) ditambah vektor terkerja: satu daun kind=0x01 dan satu kind=0x02 → leaf_hash; akar kumulatif 5-daun; satu bukti kemasukan; satu bukti ketekalan; satu AnchorBundle; dan satu id DataItem. Ini terletak bersama vektor pembungkus-luar §14.5 dan dilatih oleh kedua-dua implementasi Rust (qub-core) dan TypeScript (Worker).
16.15 Keputusan Semakan (W5 — diselesaikan)
Semakan luaran W5 (laluan reka bentuk adversari + tandatangan-akhir pemilik) telah selesai. Setiap keputusan di bawah telah dimuktamadkan dan dicerminkan dalam teks §16 di atas; kekangan pelancaran mengikat dinyatakan semula di akhir. Implementasi boleh diteruskan di bawahnya.
- Kejujuran daun laluan-lalai (
kind=0x02) — DISELESAIKAN. Hantar pembahagian dua-jenis-daun seperti dispesifikasikan:kind=0x02tidak mengkomitbody_hashmahupundrand_round. Tiada medan*_body_hashpada laluan buta-bait (ia akan menjadi isyarat "disahkan" palsu yang paling boleh-baca untuk pengintegrasi dan kemudahan yang §11 sudah sediakan daripada berkas). Jangan memerlukan meterai-pelayan untuk qub yang dilog-saksi (itu akan memaksa teks biasa melalui Worker dan memusnahkan benteng kripto-pemusnahan). Sebarang pintasan perihalan-diri tergolong dalam berkas.qub/ sampul bukti sebagai medan dikira-semula-pengesah, tidak pernah medan daun. Siling dakwaan disahkan-pemilik: §16.11. - Akauntabiliti ekuivokasi / peninggalan — DISELESAIKAN. Kunci resit-meterai disemat dalam
LogProfile+ ditandatangani-silanganchor_owner(menutup percanggahan "tiada kunci tandatangan" sebelumnya; §16.6). Model saksi pelancaran: resit disemat + metodologi pemantau + jalan rantai-sebelumnya + kepala diterbit-sendiri berkembar (repo GitHub awam dimiliki-qub, sosial usaha-terbaik), dipasarkan sebagai boleh-dikesan + diresit, tidak pernah disaksi secara bebas. Saksi pihak-ketiga benar ditangguh kepada kenaikan tadbir urus §15. - Akar amanah anchor-owner disemat + putaran — DISELESAIKAN. Terima pakai pin
LogProfile(§16.6); pengesah menyemakanchor_tx.owner == anchor_ownerdan mengesahkan pengikatan tx data → tx_id secara setempat. Tadbir urus putaran ialah lanjutan untuk dibina §15 (pencetus §15.3 ditambah), bukan guna-semula; putaran dirancang menandatangani-silang, putaran dipacu-kompromi berbalik kepada kenaikan §15 dengan semakan cabang membatasi kerosakan. - Pembutaan daun qub-peribadi — DISELESAIKAN. Kekalkan pembutaan untuk qub peribadi (
ref = SHA3-256(qub_id ‖ log_blind_secret)),qub_idmentah untuk qub awam (sudah §16.2.1),chashsebagai ikatan berdiri-sendiri.log_blind_secretialah rahsia gred-korelasi/Sybil, putar-ke-hadapan sahaja (§16.2.1). received_at— DISELESAIKAN. Kekalkan ia dalam daun, dikomit tetapi secara eksplisit bukan-keterangan; tidak pernah dikemukakan sebagai bukti atau sokongan pertikaian pada mana-mana permukaan. Sebarang semakan kewarasan pemantau membanding terhadap masa blok ArweaveT, bukananchored_atyang dikawal-operator (§16.6).- Pemasaan boleh-bukti tahap-percuma — DISELESAIKAN (tandatangan-akhir pemilik). Ketahanan tidak berundur; hanya masa komitmen had-atas yang boleh dibuktikan menjadi kasar kepada masa blok labuh. Salinan tahap-percuma tidak menggunakan SLA berangka ("…ditambah pada labuh log seterusnya, biasanya harian"); bukti jam-tepat ialah sifat T3 berbayar, didedahkan pada permukaan perbandingan-tahap + dalam syarat (§16.1).
- Pokok kumulatif pada Workers — DISELESAIKAN. Pokok RFC 9162 kumulatif tunggal + LogDO penulis-tunggal bercache-sempadan (ruang lega selesa berbanding siling DO ~1k tulisan/saat; tangguh persharian Merkle-bagi-akar-shard sehingga hampir kepadanya). Prasyarat penghalang: storan nod R2 berkunci-koordinat
(level, index)+ vektor ujian daun-sejuk DO-dipadam (§16.9);< 300 msialah pintu pelancaran yang diukur ke atas dua DO bersiri (§16.10). - Skema tandatangan ANS-104 + deep-hash — DISELESAIKAN. RSA-PSS (jenis sig 1, menggunakan semula JWK dompet-labuh khusus); Ed25519 ditangguh kepada laluan PQ §15. Deep hash SHA-384 buatan-tangan dipintu pada lekapan silang-impl kedua-arah, semakan antaroperasi pembungkus-rujukan statik-sahaja, pusing-balik
crypto.subtledikongsi, dan pemantau penerimaan-Arweave pasca-bungkus (§16.8).
Kekangan pelancaran mengikat (bawa ke dalam implementasi + semakan produk/perundangan):
- Siling dakwaan (Q1/Q6). Tiada permukaan boleh berkata log membuktikan kandungan atau pusingan buka-kunci qub lalai; dakwaan yang dibenarkan ialah disusun, secara boleh-dikesan-usik, dengan masa komitmen had-atas tanpa-amanah. Salinan cap masa tahap-percuma tidak membawa kependaman berangka; bukti jam-tepat ialah T3 berbayar sahaja.
- Kejujuran saksi (Q2). Pasarkan ekuivokasi sebagai boleh-dikesan + diresit, tidak pernah disaksi secara bebas.
- Kunci resit + labuh (Q2/Q8). Kunci resit disemat + ditandatangani-silang; dompet labuh ialah JWKnya sendiri yang berbeza daripada dompet muat naik.
- Pintu deep-hash (Q8). Tiada labuh atau tx T3 dihantar sehingga lekapan kedua-arah + semakan antaroperasi lulus; pemantau penerimaan mengejut pada kegagalan.
- Prasyarat storan (Q7). Storan nod berkunci-koordinat + vektor daun-sejuk DO-dipadam ialah prasyarat untuk jaminan "tuntutan-semula tidak pernah membatalkan bukti".