qub Protocol Specification
Ang qub ay isang protokol para sa mga kriptograpikong pangako na nakatali sa oras: isang sistemang nagsesellyo ng mga salita patungo sa isang hinaharap na petsa at, sa pagdating ng petsang iyon, nagpapatunay nang eksakto kung ano ang sinabi at kailan.
Tatlong primitibo ang nagpapagana nito. drand ay isang desentralisadong randomness beacon — ang petsa ng paghahayag ay ipinapatupad ng pisika, hindi ng kabutihang-loob ng sinumang panig. Ang permanenteng pampublikong storage ay isang tamper-proof na pampublikong imbakan — walang sinumang panig ang maaaring mag-edit o magtanggal ng isang qub kapag naiselyo na ito. ML-DSA-65 ay isang post-quantum na digital na lagda — bawat qub ay nakatali sa isang key pair na ang lihim ay hindi umaalis sa device ng may-akda.
Magkasama, ang mga primitibong ito ay gumagawa ng isang pahayag na naka-time-lock, may-katibayan-ng-pakikialam, at maiuugnay sa may-akda — isang resibo na ang halaga ay lumalago habang umuunlad ang kakayahan ng mundong likhain muli ang nakaraan.
Ang natitirang bahagi ng dokumentong ito ay ang normatibong pagtutukoy na kinakailangan para sa magkakaugnay na implementasyon.
qub Protocol Specification
| Field | Value |
|---|---|
| Version | 1.0 (protocol version 0x01, outer wrapper version 0x01) |
| Date | 2026-05-01 |
| Status | Draft |
| Reviewed through | 2026-05-01 |
Ang dokumentong ito ay ang normatibong tukoy ng protokol para sa sistema ng nakatakdang-oras na pangako ng qub. Tinutukoy nito ang mga istruktura ng datos, mga panuntunan sa serialisation, mga pormula ng derivation, at mga pamamaraan ng pagberipika na kinakailangan para sa magkakaugnay na implementasyon.
Saklaw: ang protocol layer ay sadyang neutral sa wika — ang katawan ng qub ay opaque plaintext / markdown / pact bytes, at ang locale-aware na pag-render ay responsibilidad ng mambabasa (qub.social web app, <qub-embed> iframe, MCP clients, atbp.).
1. Notasyon at mga Kumbensiyon
| Notation | Meaning |
|---|---|
u8, u64, i64 |
Unsigned/signed integers of specified bit width |
[u8; N] |
Fixed-length byte array of N bytes |
Vec<u8> |
Variable-length byte array |
Option<T> |
Value of type T, or absent |
String |
UTF-8 text string, NFC normalised |
| ` | |
SHA3-256(x) |
NIST SHA3-256 hash of byte string x (FIPS 202) |
ceil(x) |
Ceiling function: smallest integer ≥ x |
| CBOR | Concise Binary Object Representation (RFC 8949) |
| big-endian | Most significant byte first |
Lahat ng integer sa preimage constructions ay naka-encode bilang big-endian fixed-width byte arrays (i64 → 8 bytes, u8 → 1 byte) maliban kung iba ang itinakda.
Lahat ng timestamp ay Unix seconds sa UTC.
2. Mga Istruktura ng Datos
2.1 ComposeQub (Estado ng Tagalikha sa Memorya)
Hindi ini-serialize sa CBOR. Hindi isinusulat sa permanenteng storage. Lokal lamang sa app ng tagalikha.
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 (Na-decrypt na Payload)
Naka-serialize gamit ang canonical CBOR (§3). Naka-encrypt sa loob ng SealedQub. Ito ang istrukturang nagpapatunay ng integridad ng nilalaman pagkatapos ng pag-decrypt.
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 — kapag nagbigay ng hatol ang realidad (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
}
Baseline (hindi naka-lagdang text qub): version = 0x01, content_type = 0x01, sig_alg = 0x00, lahat ng Option fields ay wala.
Iba pang v1 configurations: content_type = 0x03 (pact body, tingnan §6.1); sig_alg = 0x01 (ML-DSA-65) na may author_signature at author_pubkey (tingnan §9.3); cosigner_pubkey at cosigner_signature ay magkasama para sa mga kasunduang may kasamang lagda (tingnan §9.7); reply_to itinakda sa qub_id ng magulang na qub para sa mga reply-chain qubs (tingnan §9.3 para sa mga implikasyon sa saklaw ng lagda).
2.3 SealedQub (Canonical Wire Format)
Naka-serialize gamit ang canonical CBOR (§3). Isinusulat sa permanenteng storage. Ito ang on-chain artifact.
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 — inilalantad sa verdict-watch CTA
// bago ang paghahayag; sumasalamin sa QubEnvelope.outcome_at;
// nakatali sa qub_id sa pamamagitan ng §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 (Estado ng Aplikasyon ng Mambabasa)
Hindi ini-serialize sa CBOR. Lokal lamang sa app ng mambabasa. Binubuo pagkatapos ng matagumpay na pag-decrypt at pagberipika.
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 — dinadala mula sa QubEnvelope.outcome_at / SealedQub.outcome_at; nagpapatakbo sa verdict-watch block ng reveal page (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. Canonical CBOR Profile
Lahat ng SealedQub at QubEnvelope serialisation ay MUST sumunod sa profile na ito. Dalawang implementasyon na binigyan ng parehong lohikal na istruktura ay MUST gumawa ng magkaparehong bytes.
3.1 Mga Panuntunan sa Pag-encode
| Rule | Specification |
|---|---|
| Standard | RFC 8949 §4.2.1 (Core Deterministic Encoding Requirements) |
| Map key ordering | Sorted by encoded byte length first (shorter before longer), then lexicographically (byte-by-byte for same-length encodings) |
| Integer encoding | Shortest form: 0–23 in initial byte; 24–255 in 2 bytes; 256–65535 in 3 bytes; etc. |
| Length encoding | Definite lengths only. No indefinite-length arrays, maps, byte strings, or text strings (additional info = 31 is forbidden). |
| Tags | No CBOR tags (major type 6 is forbidden). |
| Floating-point | No floats (major types 7 values 0xF9–0xFB are forbidden). |
| Text strings | UTF-8 encoded, NFC normalised (Unicode Normalization Form C). |
| Byte strings | Raw bytes. No base64 encoding at the CBOR layer. |
| Duplicate keys | Reject with error. Parsers MUST NOT silently accept duplicate map keys. |
| Simple values | Only true (0xF5), false (0xF4), and null (0xF6) are permitted. |
| Optional fields | Absent optional fields are omitted from the CBOR map entirely (not encoded as null). Present optional fields are included in sorted key order. |
3.2 Verified Canonical Key Orders
Ang mga key order na ito ay normatibo. Ang mga implementasyon ay MUST mag-emit ng mga key sa eksaktong pagkakasunod na ito. Ang mga debug assertion ay SHOULD magberipika ng pagkakasunod sa mga non-release build.
QubEnvelope (version 0x01, unsigned, all optional fields absent):
"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)
Derivation ng key order ng QubEnvelope: bawat key ay isang CBOR text string. Encoded length = 1 byte header + string length (para sa mga string na mas mababa sa 24 bytes). Pagsunod-sunurin ayon sa kabuuang encoded length muna, pagkatapos ay lexicographically para sa mga key na may parehong haba.
SealedQub (version 0x01, public, no recipient):
"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 (pact body, 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 (row of the terms array):
"key" (4 encoded bytes)
"value" (6 encoded bytes)
PartyIdentifier (party_a / party_b map):
"label" (6 encoded bytes)
"contact" (8 encoded bytes) ← only if present
3.3 Sanggunian ng Byte Encoding
| Type | CBOR encoding | Example |
|---|---|---|
| SHA3-256 hash (32 bytes) | 0x58 0x20 + 32 bytes |
body_hash, qub_id |
| Timestamps (i64) | Major type 0 (positive) or 1 (negative), shortest encoding | Unix seconds |
| Version (u8, value 1) | 0x01 (single byte) |
|
| Content type (u8, value 1) | 0x01 (single byte) |
|
| sig_alg (u8, value 0) | 0x00 (single byte) |
|
| ML-DSA-65 signature (3,309 bytes) | 0x59 0x0C 0xED + 3,309 bytes |
author_signature, cosigner_signature |
| ML-DSA-65 public key (1,952 bytes) | 0x59 0x07 0xA0 + 1,952 bytes |
author_pubkey, cosigner_pubkey |
4. Mga Normatibong Derivation
4.1 qub_id
Ang qub_id ay natatanging tumutukoy sa isang qub at nagbubuklod sa QubEnvelope sa SealedQub. Ito ay deterministikong nakuha mula sa nilalaman ng 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 kapag wala ang outcome_at)
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
Encoding ng domain separator: Ang string na "QUB_ID_V2" ay 9 ASCII bytes. Isang 0x00 padding byte ang idinaragdag upang maabot ang 10 bytes para sa pagkakahanay. Ang mga implementasyon ay MUST gumamit ng eksaktong 10 bytes na ito: [0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00].
Encoding ng outcome_at: Pinalawak ng V1.1 ang preimage mula 92 patungong 100 bytes upang isama ang opsyonal na outcome_at field sa pagkakatali. Ang walang outcome_at ay naka-encode bilang 8 zero bytes; tinatanggihan ng mga protocol validator ang outcome_at <= 0 saanman, kaya ang sentinel na ito ay hindi maaaring mag-collide sa isang lehitimong halaga. Tingnan ang §3.2 (wire format) at ang in-tree na tasks/verdict-uplift-plan.md para sa verdict mechanic na nag-uudyok sa field na ito.
Encoding ng drand_round: Pinalawak ng V1.2 ang preimage mula 100 patungong 108 bytes upang isama ang drand_round (ang target drand round, §4.3) sa pagkakatali, at itinaas ang domain separator patungong QUB_ID_V2. Ito ang nag-uugnay ng timelock round sa pagkakakilanlan ng qub: hindi maaaring muling itali ng isang gateway ang ciphertext sa ibang round (hal. lumipas na) kaysa sa ipinahihiwatig ng ipinapakitang unlock_at. Bukod pa rito, bineberipika ng unlock procedure (§8) na ang round na nakabaon sa tlock ciphertext stanza ay tumutugma sa unlock_round(unlock_at), kaya ang ipinapakitang unlock time ay napapatunayang ang round na nagbabantay sa pag-decrypt.
Mga katangian:
- Ang pagbabago ng anumang field sa QubEnvelope (body, timestamps, content type, version) ay gumagawa ng ibang qub_id.
- Ang qub_id ay kinakalkula bago ang pag-encrypt. Parehong QubEnvelope at SealedQub ay may parehong qub_id. Pinapatotohanan ng mambabasa na sila ay tugma pagkatapos ng pag-decrypt.
- Ang qub_id ay hindi nakadepende sa
sender_label,author_signature, oauthor_pubkey. Ibig sabihin nito, ang parehong nilalamang isinelyo sa parehong oras ay gumagawa ng parehong qub_id anuman ang lumagda nito. - Ang pagbabago ng
titleng SealedQub (na ang lahat ng iba ay nakapirmi) ay nagpapabago ngqub_idsa pamamagitan ngtitle_hash. Hindi nga makapagpapalit ang gateway ng plaintext title na ipinapakita sa countdown nang hindi pinawawalang-bisa ang pagkakakilanlan ng qub. - Ang pagbabago ng SealedQub
outcome_at(na ang lahat ng iba ay nakapirmi) ay nagpapabago ngqub_idsa pamamagitan ng preimage. Hindi maaaring palitan ng isang gateway ang pre-reveal verdict-on date na ipinapakita sa countdown nang hindi pinawawalang-bisa ang pagkakakilanlan ng qub. - Ang pagbabago ng
drand_round(na ang lahat ng iba ay nakapirmi) ay nagpapabago ngqub_idsa pamamagitan ng preimage. Hindi maaaring muling itali ng isang gateway ang timelock ciphertext sa ibang round nang hindi pinawawalang-bisa ang pagkakakilanlan ng qub; kasama ang §8 unlock-time stanza-round check, ang ipinapakitangunlock_atay ang round na talagang nagbabantay sa pag-decrypt.
4.2 body_hash
body_hash = SHA3-256(body)
Kung saan ang body ay ang raw Vec<u8> content payload. Para sa mga text qub, ito ang UTF-8 encoded na katawan ng qub.
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
Kung saan ang title ay ang opsyonal na plaintext title na ipinapakita sa countdown ng mambabasa bago ang paghahayag (tingnan §3.2). Ang NFC normalisation ay tumatakbo sa oras ng pag-hash upang ang digest ay stable sa mga visually-equivalent code-point sequence. Ang all-zeros sentinel ay nakalaan para sa kasong wala; ang isang walang lamang string ay tinatanggihan sa canonical CBOR boundary bilang non-canonical encoding ng "wala" (ang canonical encoding ay tinatanggal ang field nang buo).
4.3 Pagmapa ng Unlock-Round
drand_round = ceil((unlock_at - chain_genesis_time) / chain_period_seconds)
| Parameter | Source | Example |
|---|---|---|
unlock_at |
User-chosen Unix seconds UTC | 1735689600 (2025-01-01 00:00:00 UTC) |
chain_genesis_time |
drand chain info (genesis_time) |
1595431050 |
chain_period_seconds |
drand chain info (period) |
30 |
Pinipili ng ceil() na operasyon ang unang drand round na ang reveal time ay ≥ unlock_at. Sinisiguro nito na ang qub ay hindi nagiging decryptable bago ang napiling unlock time.
Edge case: kung ang (unlock_at - chain_genesis_time) ay tumpak na nahahati ng chain_period_seconds, ang resulta ay ang eksaktong round na iyon — ang qub ay nag-aalis ng selyo nang tumpak sa reveal time ng round na iyon.
Pagberipika: ang unlock_at ay MUST sa hinaharap sa oras ng pag-selyo. Ang unlock_at ay MUST NOT na hihigit sa 10 taon mula created_at (upang limitahan ang panganib ng long-horizon drand dependency; ang UI ay SHOULD magbabala para sa mga petsa ng paghahayag na lampas sa 2 taon).
5. Mga Newtype ng Wire Format
Ang mga newtype ng wire format ay nagbibigay ng compile-time na kaligtasan laban sa paglilito ng CBOR bytes sa JSON, raw plaintext, o iba pang byte encodings.
| Type | Contains | Produced By | Consumed By |
|---|---|---|---|
SealedQubCbor |
Canonical CBOR of SealedQub | serialize_sealed_qub() |
Permanent-storage upload, viewer fetch |
QubEnvelopeCbor |
Canonical CBOR of QubEnvelope | serialize_qub_envelope() |
tlock encrypt input, tlock decrypt output |
5.1 Mga Panuntunan sa Construction
// 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 Pagberipika sa Construction
Ang from_encoded() ay SHOULD magberipika na ang input ay nagsisimula sa isang valid na CBOR map header. Ang buong structural validation ay nangyayari sa parse time, hindi construction time, upang maiwasan ang double-parsing.
6. Rehistro ng Content Type
| Value | Type | Max Body Size | Notes |
|---|---|---|---|
0x00 |
Reserved (invalid) | — | MUST NOT be used |
0x01 |
Plain text (UTF-8, restricted Markdown) | 50 KB paid / 10 KB free | See §10 for rendering rules. The free / paid split is enforced by the upload service; the protocol-layer hard ceiling is 50 KB. |
0x02 |
Reserved (future) | — | Allocated for a future content type; not valid in v1. Viewers MUST reject per the rule below. |
0x03 |
Pact (bilateral agreement, CBOR body) | 100 KB | Body is canonical CBOR PactTerms (§6.1). Cosigner signing per §9.7. |
0x04 |
Verdict (self-grading ng tagalikha, CBOR body) | 8 KB | Ang body ay canonical CBOR VerdictBody (§6.2). Inilalabas lamang ng system-side na verdict intent. Ang relasyong magulang ay nasa Arweave tag na Parent-Tx-Id, hindi sa body. Tingnan ang verdict-uplift-plan §3.4. |
Ang mga mambabasa ay MUST tanggihan ang mga hindi kilalang content type na may malinaw na error na nakikita ng gumagamit. Ang mga mambabasa ay MUST NOT subukang i-render ang mga hindi kilalang uri bilang text.
6.1 Katawan ng Kasunduan (content_type = 0x03)
Ang katawan ng kasunduan ay ang canonical CBOR encoding ng isang PactTerms na halaga:
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)> }
Ang mga canonical CBOR key order para sa lahat ng tatlong map ay nasa §3.2. Ang kabuuang naka-serialize na pact CBOR ay MUST NOT lumampas sa 100 KB (tumutugma sa §6).
Tagapag-iba ng schema. Ang unang row sa terms para sa isang structured/v1 na kasunduan ay MUST { key: "pact_schema", value: "structured/v1" }. Ang mga row na walang ganitong marker ay "custom" na kasunduan at hindi tumatanggap ng structured validation o schema-aware rendering.
Frozen acknowledgement slots. Ang structured/v1 na kasunduan ay nagdadala ng eksaktong apat na acknowledgement row sa ilalim ng mga key na ito:
"initiator_standard_terms"
"initiator_capacity_terms"
"counterparty_standard_terms"
"counterparty_capacity_terms"
Ang value para sa bawat isa ay isa sa walong frozen English string na pinili ng (role, kind) pair, kung saan role ∈ { seller, buyer, provider, client } at kind ∈ { standard, capacity }. Ang mga string mismo ay normatibong datos ng protokol — parehong ML-DSA-65 signatures ng dalawang panig ay nangangako sa eksaktong bytes sa pamamagitan ng body_hash. HINDI sila isinasalin; ang naka-lagdang katawan ay neutral sa wika. Ang anumang pagbabago sa pananalita ay nangangailangan ng bagong bersyon ng schema (structured/v2).
Ang walong string, ang kanilang lookup (acknowledgement_for(role, kind)), at ang katwiran para sa bawat isa ay nakatakda ng reference implementation. Ang mga sumusunod na implementasyon ay MUST mag-emit ng byte-identical na acknowledgement values; ang golden-fixture SHA3-256 body-hash tests na sumasaklaw sa lahat ng apat na kombinasyon ng role ay nakahuli ng anumang pag-anod.
Pagkakasunod-sunod ng pagpapakita sa mambabasa. Ang mga acknowledgement string ay naglalaman ng mga parirala tulad ng "described above", na ipinagpapalagay na ang mga description / scope row ay nag-render nang nauna sa mga acknowledgement. Ang mga mambabasa ay MUST mag-render ng terms array sa pagkakasunod ng CBOR; ang pag-aayos muli ay sumisira sa semantiko ng prosa.
Pakikipag-ugnayan sa kabilang panig. Kapag ang contact ni Party B ay isang valid na email address, ang qub upload service ay awtomatikong nagpapadala ng review / co-sign invite email sa stage time at nagbubuklod sa magiging co-sign sa pagberipika ng parehong address na iyon (§9.7). Ang mga kasunduan na ang Party B contact ay wala ay maaari pa ring magkaroon ng kasamang lagda, ngunit sa pamamagitan lamang ng out-of-band channel — tinatanggihan ng serbisyo ang mga co-sign request na hindi makakagawa ng tumutugmang 15-minutong email-verification marker.
6.2 Katawan ng Hatol (content_type = 0x04)
Ang katawan ng hatol ay ang canonical CBOR encoding ng isang VerdictBody na halaga:
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; "ano ang nagbago, ano ang natutunan mo"
evidence_url: Option<String>, // ≤ 2,048 bytes; HTTPS lamang; walang key kapag tinanggal
}
Canonical CBOR key order:
"outcome" (8 encoded bytes)
"reflection" (11 encoded bytes) ← kung nasa loob lamang
"evidence_url" (13 encoded bytes) ← kung nasa loob lamang
"verdict_version" (16 encoded bytes)
Ang kabuuang naka-serialize na verdict CBOR ay MUST NOT lumampas sa 8 KB (tumutugma sa registry row sa itaas).
Outcome enum. Ang wire byte ay neutral sa intent; ang apat na bucket na Right / Partial / Wrong / Unfalsifiable ay sumasaklaw sa buong outcome space ng bawat verdict-bearing intent. Ang mga per-intent label ("Tama ang hula" / "Tinupad ko" / "Naipadala" / "Pinatunayan" para sa Right, atbp.) ay isang viewer-side rendering concern na sinasagot laban sa intent ng magulang na qub — ang wire ay nananatiling neutral sa wika at intent. Ang mga value na nasa labas ng 1..=4 ay MUST tanggihan sa decode.
Pag-uugnay sa magulang. Ang isang verdict qub ay HINDI nagdadala ng reference sa magulang sa loob ng kanyang body. Ang Arweave transaction id ng magulang na qub ay inilalabas bilang Parent-Tx-Id storage tag sa upload time (§7 storage-tag layer). Pinapanatili nito ang body bilang isang self-contained na nakalagdang pahayag ng self-assessment; ang audit chain ("tama tungkol saan?") ay itinatatag sa pamamagitan ng Arweave-tag lookup.
Kaligtasan ng evidence URL (normatibo). Kapag mayroong evidence_url, ang mga validator (compose-side, wire-side, Worker edge) ay MUST ipatupad ang sumusunod:
- HTTPS lamang. Ang string ay MUST magsimula sa byte sequence na
https://. Anumang ibang scheme —http,ftp,javascript,data,file, atbp. — ay tinatanggihan. - Limitasyon ng haba. ≤ 2,048 bytes (praktikal na limitasyon ng browser URL).
- NFC + hostile-codepoint check. Parehong tuntunin sa
titleatreflection— ang mga bidi-override / zero-width / tag-block / BOM / C0 / C1 codepoint ay tinatanggihan. Ang depinisyon ay tumutugma sa Rustcrate::handle::contains_hostile_text_codepointat sa TSworkers/api/src/utils/unicode.ts::isHostileCodepoint(panatilihing magkasabay). - Walang whitespace, walang ASCII controls. Ang whitespace / DEL / sub-
0x20bytes saanman sa URL ay tinatanggihan — pinipigilan ang\n/\tinjection vector na hindi sakop ng tuntunin ng bidi. - Hindi walang-laman ang host segment. Lahat sa pagitan ng
https://at ng unang/,?, o#ay MUST hindi walang laman.
Walang server-side fetching. Ang Worker ay MUST NOT mag-proxy, mag-fetch, o mag-preview ng URL. Ang protocol ay nag-iimbak ng isang string; ang rendering ay nangyayari viewer-side gamit ang rel="nofollow noopener noreferrer" target="_blank" at isang nakikitang host na ipinapakita kasama ng link text.
Repleksyon. Opsyonal na repleksyon na isinulat ng tagalikha ("ano ang nagbago, ano ang natutunan mo"). Parehong NFC + hostile-codepoint validation tulad ng title. Ang walang laman / whitespace-only input ay isasara sa absent sa construction time.
Bersyon ng schema. Ang v1 ay sumusuporta sa verdict_version = 0x01 lamang. Ang mga panghinaharap na schema revision ay magtataas ng byte na ito at darating kasabay ng bagong protocol version ayon sa §12.
7. Protokol ng Pag-selyo
Ang kumpletong sequence ng pag-selyo. Bawat hakbang ay normatibo.
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.
Layer ng storage tag (out-of-band). Ang qub upload service ay nag-aattach ng sadyang maliit na set ng storage transaction tags kasama ang wrapped payload. Ang Content-Type=application/octet-stream ay normatibong kinakailangan. Ang reference service ay karagdagang nag-aattach ng tatlong opsyonal na tag kapag pinili ng tagalikha na ilantad ang mga ito: Intent (allowlist-validated compose intent — hal., quote, reply, commitment), Author (pubkey fingerprint ng tagalikha §9.3 bilang 64-char lowercase hex), at Parent-Tx-Id (storage transaction id ng magulang na qub para sa reply chains, 43-char base64url).
Ang Author tag ay opt-in bawat qub: ina-attach lamang ito ng reference creator app kapag tahasang pinagana ng gumagamit ang pampublikong attribution sa oras ng pag-selyo. Kapag naka-off ang toggle — ang default — walang Author tag na isinulat at ang qub ay walang attribution sa chain: walang anumang bagay sa permanenteng storage ang nag-uugnay sa upload sa handle, email, o ibang qub ng tagalikha. Kapag naka-on ang toggle, ang Author fingerprint ay nareresolba sa napiling @handle ng tagalikha sa pamamagitan ng §9.5 attestation chain. Ang mga reply-chain relationship at Intent ay hindi nagpapakilala. Ang outer wrapper (§13) ay nagpoprotekta sa panloob na katawan mula sa ciphertext correlation — pinipigilan ang isang harvester mula sa pagkilala at bulk-decryption ng mga qub-shaped upload pagkatapos i-publish ang kanilang drand round.
Ang reference service ay sinasadyang HINDI nag-aattach ng App-Name, App-Version, o Type tags: ang anumang ganitong single-value filter ay magbabalik ng buong qub corpus sa isang GraphQL query, na hindi tugma sa body-only confidentiality scope ng wrapper.
Ang isang sumusunod na verifier ay MUST NOT umaasa sa anumang storage tag para sa §11 third-party verification; ang body hash / qub_id / signature ay nangangako lamang sa panloob na CBOR, hindi sa tag set.
8. Protokol ng Paghahayag
Ang kumpletong sequence ng paghahayag. Bawat hakbang ay normatibo.
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. Paglagda ng Pagiging May-akda
9.1 Katwiran
Ang mga qub ay permanenteng naka-imbak sa permanenteng storage. Ang mga lagda ng pagiging may-akda ay dapat manatiling hindi maaaring pekein nang walang hangganan, kaya naman ang v1.0 ay gumagamit ng post-quantum ML-DSA-65 scheme (FIPS 204) sa halip na isang klasikal na scheme na ang seguridad ay maaaring humina sa loob ng permanenteng buhay ng qub.
9.2 Rehistro ng Algorithm
sig_alg |
Scheme | Key Size | Signature Size |
|---|---|---|---|
0x00 |
No signature (unsigned) | — | — |
0x01 |
ML-DSA-65 (FIPS 204) | 1,952 bytes | 3,309 bytes |
Ang mga mambabasa ay MUST tanggihan ang mga hindi kilalang sig_alg values.
9.3 Konstruksiyon ng Naka-lagdang Preimage
sig_input = SHA3-256(
"QUB_AUTHOR_SIG_V1" || // domain separator (17 bytes)
version || // u8 (1 byte)
qub_id || // [u8; 32] (32 bytes)
body_hash || // [u8; 32] (32 bytes)
unlock_at || // i64 big-endian (8 bytes)
0x00 // u8 (1 byte): MUST be 0x00 in v1.0
)
// Total preimage: 91 bytes → 32-byte hash
signature = Sign(author_secret_key, sig_input)
Domain separator: "QUB_AUTHOR_SIG_V1" ay 17 ASCII bytes: [0x51, 0x55, 0x42, 0x5F, 0x41, 0x55, 0x54, 0x48, 0x4F, 0x52, 0x5F, 0x53, 0x49, 0x47, 0x5F, 0x56, 0x31]. Walang padding.
Trailing byte: ang ika-91 preimage byte ay MUST 0x00. Ang reference implementation ay naglalantad nito bilang ang constant na ORG_ID_PRESENT_INDIVIDUAL = 0x00 sa crates/qub-core/src/signing.rs; ang mga mambabasang nagbubuo muli ng sig_input para sa pagberipika ay MUST mag-emit ng parehong byte.
Saklaw ng lagda — kung ano ang sakop at hindi. Ang sig_input ay nangangako sa apat na envelope field: version, qub_id, body_hash, unlock_at (kasama ang nakapirming domain separator at org_id_present byte). Tatlo sa apat na iyon ay structural invariants: ang qub_id mismo ay nakuha mula sa version, content_type, created_at, unlock_at, outcome_at, drand_round, at body_hash sa pamamagitan ng §4.1 preimage, kaya ang anumang pagbabago sa mga field na iyon ay gumagawa ng ibang qub_id at transitively na nagpapawalang-bisa sa lagda. Ang direktang pinapatotohanang ibabaw ay samakatuwid:
| Field | Authenticated by signature | How |
|---|---|---|
version |
✓ | Direct input to sig_input |
qub_id |
✓ | Direct input |
body_hash |
✓ | Direct input |
unlock_at |
✓ | Direct input |
content_type |
✓ | Transitively, via qub_id preimage |
created_at |
✓ | Transitively, via qub_id preimage |
outcome_at |
✓ | Transitively, via qub_id preimage |
drand_round |
✓ | Transitively, via qub_id preimage (V1.2) |
body |
✓ | Transitively, via body_hash = SHA3-256(body) |
author_pubkey |
— (implicit) | Key that verified the signature is the author, by definition |
sender_label |
✗ | Display-only text; mutable without signature breakage |
reply_to |
✗ | Threading pointer; mutable without signature breakage |
cosigner_pubkey / cosigner_signature |
— | Independently signed over the same sig_input (see §9.7) |
drand_chain_id, tlock_ciphertext, visibility |
— | Outer SealedQub fields, not inside the envelope — covered by their own structural invariants (round / chain consistency) but not by the author signature. (drand_round is now bound transitively via the qub_id preimage — see above.) |
Mga implikasyon ng seguridad ng mga hindi pinatotohanang field.
- Ang isang panig na may write access sa mga naka-imbak na bytes ay maaaring magpalit ng
sender_label("Alice" → "Mallory") nang hindi pinawawalang-bisa ang lagda ng may-akda. Angauthor_pubkeysa loob ng envelope ay nananatiling tunay na anchor ng pagkakakilanlan — ang mga mambabasa ay MUST kumuha ng display identity mula saauthor_pubkey(sa pamamagitan ng §9.5 attestation layer) sa halip na magtiwala sasender_label. - Ang isang
reply_tofield ay maaari ring i-edit pagkatapos ng paglagda. Dahil angqub_iday content-addressed, hindi maaaring iturok ng umaatake angreply_tosa isang hindi umiiral na target, ngunit maaari nilang tahimik na i-re-parent ang reply sa ibang umiiral na qub.
Ang mga implementasyon na nagpapakita ng sender_label o reply_to sa mga end user ay MUST ilantad ang authenticated identity (pubkey fingerprint, attestation) bilang pangunahing signal ng pagkakakilanlan, hindi ang label.
9.4 Pamamaraan ng Pagberipika
1. Read sig_alg from QubEnvelope.
2. If sig_alg == 0x00 → unsigned. No verification. Display "unsigned qub."
3. If sig_alg is unknown → reject. Display "unrecognised signature scheme."
4. Extract author_signature and author_pubkey. If either is absent → integrity error.
5. Reconstruct sig_input using fields from QubEnvelope (same formula as §9.3).
6. Verify(author_pubkey, sig_input, author_signature).
7. If verification succeeds → display "signed by [key fingerprint]."
8. If verification fails → display "signature verification failed."
Ang pagberipika ng lagda ay ang pinakamamahal na operasyon (lalo na ang ML-DSA-65). Ito ay SHOULD isagawa pagkatapos pumasa ang lahat ng mas murang pagsusuri (hash, qub_id, unlock_at).
9.5 Mga Attestation ng Pagkakakilanlan
Ang mga attestation ng pagkakakilanlan — ang pagmamapa ng author_pubkey sa mga claim ng pagkakakilanlan na nakikilala ng tao tulad ng qub handle, email address, social handle, o passkey credential — ay isang viewer-side progressive enhancement at hindi kinakailangan para sa pagberipika ng lagda. Ang mga mambabasang nagrereresolba ng attestation sa display identity ay MUST gamitin ang precedence:
handle > email > social > fingerprint
Ang fingerprint fallback ay ang lowercase hex ng SHA3-256(author_pubkey); palagi itong available para sa anumang naka-lagdang qub. Ang mga viewer ay MAY paikliin ito para sa pagpapakita — ang reference viewer ay nagre-render ng qub: na sinusundan ng unang at huling apat na bytes (qub:<8 hex>…<8 hex>).
Ang isang sumusunod na verifier ay maaaring makumpleto ang bawat tseke sa §9.4 nang hindi nakikipag-ugnay sa qub API, nang walang network maliban sa permanenteng storage at drand, at walang anumang server-side lookup. Ang attestation resolution ay isang hiwalay na best-effort step na isinasagawa lamang pagkatapos ng matagumpay na pagberipika ng lagda.
9.6 Epekto sa Laki
| Ed25519 | ML-DSA-65 | |
|---|---|---|
| Signature | 64 bytes | 3,309 bytes |
| Public key | 32 bytes | 1,952 bytes |
| Total per qub | 96 bytes | 5,261 bytes |
| Storage cost delta (at ~$5/MB) | ~$0.0005 | ~$0.026 |
Para sa isang text qub na 500–2,000 bytes, ang ML-DSA-65 ay halos triple ang naka-imbak na laki. Ang absolute cost ay napakaliit.
9.7 Pagberipika ng Kasamang Lagda (mga Bilateral na Kasunduan)
Para sa mga bilateral na kasunduan (content_type = 0x03), isang pangalawang signature layer ang nagpapatunay na pinagsang-ayunan ng dalawang panig ang parehong mga termino.
Mga envelope field:
cosigner_pubkey: ML-DSA-65 public key ng counter-signer (Party B).cosigner_signature: Lagda sa parehongsig_inputtulad ng sa may-akda (§9.3).
Parehong field ay MUST naroroon nang sama-sama o parehong wala. Kung eksaktong isa lamang ang naroroon, ang mga mambabasa ay MUST mag-ulat ng integrity error.
Pamamaraan ng pagberipika:
1. If cosigner_pubkey absent and cosigner_signature absent → no cosigner. Done.
2. If exactly one is present → integrity error.
3. Verify cosigner_pubkey != author_pubkey (prevent self-cosigning).
Fail → display "cosigner pubkey must differ from author."
4. Reconstruct sig_input using the same formula as §9.3.
5. Verify(cosigner_pubkey, sig_input, cosigner_signature).
6. Success → display "co-signed by [cosigner fingerprint]."
7. Failure → display "co-signature verification failed."
Mga katangian:
- Ang kasamang lumagda ay pumipirma ng magkaparehong
sig_inputtulad ng may-akda — parehong panig ay nangangako sa parehongqub_id,body_hash, atunlock_at. - Ang derivation ng
qub_id(§4.1) ay HINDI kasama ang mga field ng kasamang lumagda. Ang pagdaragdag ng kasamang lumagda sa umiiral na envelope ay hindi nagpapabago ngqub_id. - Ang isang kasunduan ay maaaring author-signed lamang (one-sided commitment), cosigner-only (hindi karaniwan), o pareho (full bilateral proof).
Email-binding gate (operasyonal). Kapag ang isang staged na kasunduan ay nagdadala ng Party B email contact (§6.1), ang qub upload service ay MUST tanggihan ang co-sign request maliban kung mayroong panandaliang email-verification marker na tumutugma sa staging id at sa normalised-email hash ng contact na iyon. Ang marker ay isinusulat ng /api/v1/auth/verify kapag ang magic-link token ay nagdadala ng staging_id at ang naverify na address ay tumutugma sa SHA-256(normalise_email(party_b.contact)) — kung saan ang normalise_email(addr) ay nagpapanatili ng case ng local-part at nagpapababa lamang ng case ng domain part (ayon sa RFC 5321 §2.3.11), at ang SHA-256 dito ay ang NIST FIPS 180-4 hash (naiibang mula sa SHA3-256 na ginagamit sa §4 derivations) — at nag-eexpire 900 segundo (15 minuto) pagkatapos ng pag-issue. Ito ay operasyonal na anti-impersonation gate, HINDI bahagi ng on-chain qub proof — ang isang third-party verifier na nagre-replay ng §11 ay nangangailangan lamang ng permanenteng storage at drand, walang anumang server-side lookup. Ang marker ay umiiral sa server-side lamang at hindi kailanman bahagi ng naka-lagdang katawan.
Epekto sa laki (ML-DSA-65 may-akda + kasamang lumagda):
| Component | Size |
|---|---|
| Author signature | 3,309 bytes |
| Author public key | 1,952 bytes |
| Cosigner signature | 3,309 bytes |
| Cosigner public key | 1,952 bytes |
| Total crypto overhead | 10,522 bytes |
| Storage cost delta | ~$0.05 |
10. Pag-render at Pag-sanitise ng Markdown
Ang seksyong ito ay kritikal sa seguridad. Ang mambabasa ay nagre-render ng mga text qub (content_type = 0x01) gamit ang isang pinaghihigpitang subset ng Markdown.
10.1 Mga Pinapayagang Elemento
- Mga heading:
#hanggang####(walang#####o######) - Emphasis: bold (
**), italic (*), strikethrough (~~) - Mga listahan: ordered (
1.) at unordered (-,*) - Blockquotes (
>) - Code: inline spans (```) at fenced blocks (`````)
- Mga horizontal rule (
---) - Mga line break (dalawang trailing space o blangkong linya)
- Mga talata
10.2 Mga Ipinagbabawal na Elemento
| Element | Handling |
|---|---|
Raw HTML (<div>, <script>, etc.) |
Stripped entirely. No HTML passes through. |
Images () |
Stripped. Image syntax is removed from output. |
Links ([text](url)) |
URL rendered as visible plain text. Not auto-linked. Not clickable without explicit user action. |
| Dangerous URL schemes | javascript:, data:, vbscript:, file: — stripped. |
| Iframes, embeds, objects | Stripped. |
| HTML entities | Decoded to display characters only if safe. |
10.3 Implementasyon
Ang mga implementasyon ay MUST gumamit ng strict allowlist parser, hindi blocklist. Ang inirerekomendang approach:
- I-parse ang Markdown gamit ang
pulldown-cmark(o katumbas). - Lakaran ang AST at i-drop ang anumang node na wala sa allowlist (§10.1).
- Para sa mga link node: i-emit ang URL bilang nakikitang text, hindi bilang isang clickable na
<a>element. - I-convert ang filtered AST sa isang typed intermediate representation (hal., isang
MarkdownNodeenum na may mga safe variant lamang). Ang raw HTML ay structurally unrepresentable sa IR na ito. - Mag-render mula sa typed IR papunta sa target view layer (hal., reactive view components, DOM nodes). Walang HTML string concatenation o
innerHTMLsa anumang punto.
Ang mga blocklist approach ay marupok dahil ang mga bagong Markdown extension o parser quirk ay maaaring magpasok ng mga hindi nasala na elemento. Ang typed-AST approach ay ginagawang structurally imposible ang XSS — walang variant na maaaring magdala ng arbitrary HTML.
10.4 Mga Limitasyon sa Laki at Istruktura
- Pinakamataas na rendered heading depth:
####(H4). Ang#####at mas malalim ay nire-render bilang bold text. - Walang limitasyon sa bilang ng talata (ang mga limitasyon sa laki ng katawan sa §6 ang nagdidikta).
- Mga fenced code block: walang syntax highlighting sa MVP. Naka-render bilang monospace preformatted text.
11. Pagberipika ng Third-Party
Ang sinumang third party ay maaaring magberipika ng isang pampublikong qub nang walang kooperasyon ng qub. Ang pamamaraan ng pagberipika:
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.
Ano ang pinatutunayan ng pagberipika:
| Proof | What it establishes |
|---|---|
| Commitment | The ciphertext existed by the permanent storage block timestamp. |
| Integrity | The plaintext body matches the committed hash and has not been altered. |
| Timing | The content was unreadable until the drand round, which corresponds to the chosen unlock time (subject to tlock and drand security assumptions). |
Ano ang HINDI pinatutunayan ng pagberipika:
| Non-proof | Why |
|---|---|
| Authorship | The sender_label is decorative. Without sig_alg ≥ 0x01, anyone could have sealed this content. |
| Intent | The qub proves content and timing, not what the creator subjectively meant. |
| Pre-event timing | Storage block inclusion may lag actual upload by minutes. The commitment timestamp is the block time, not the moment the user pressed “seal.” |
12. Bersyon
12.1 Bersyon ng Protokol
Ang version field (u8) sa parehong SealedQub at QubEnvelope ay kumikilala sa major protocol version.
- Ang mga mambabasa ay MUST tanggihan ang mga hindi kilalang major version na may malinaw na error.
- Ang mga kilalang major version ay MAY tanggapin ang mga hindi kilalang opsyonal na field kung pinapahintulutan ng forward compatibility rules (ang mga opsyonal na field na wala sa canonical key order ay binabalewala).
- Ang mga content type (
content_type) at signature scheme (sig_alg) ay version-gated: ang mga bagong value ay maaari lamang ipakilala kasabay ng bagong protocol version o tahasang update sa rehistro.
12.2 Kasaysayan ng Bersyon
| Version | Value | Description |
|---|---|---|
| v1 | 0x01 |
Public text qubs (content_type 0x01), pact bilateral agreements (0x03, structured/v1 schema, ML-DSA-65 author + cosigner), tlock, SHA3-256 |
12.3 Forward Compatibility
Ang isang v1 viewer na nakakaharap ng isang QubEnvelope na may mga hindi kilalang opsyonal na CBOR map key (mga key na wala sa §3.2 canonical order) ay SHOULD balewalain ang mga key na iyon at magpatuloy sa pagberipika gamit ang mga kilalang field. Pinapahintulutan nito ang mga hinaharap na maliit na karagdagan (hal., bagong metadata) nang hindi nangangailangan ng pagtaas ng major version.
Ang isang v1 viewer na nakakaharap ng sig_alg = 0x01 (ML-DSA-65) ngunit walang suporta sa pagberipika ng ML-DSA-65 ay SHOULD ipakita ang nilalaman ng qub na may "signature present but not verifiable" na abiso, hindi tanggihan ang qub nang buo. Ang reference implementation ngayon ay tinatanggihan ang bawat sig_alg value maliban sa 0x00 at 0x01 dahil ang v1 registry ay walang ibang valid na algorithm — ang mahigpit na pagtanggi at soft-fail ay observationally magkapareho hanggang sa mairehistro ang ikatlong algorithm. Ang soft-fail na ugali sa itaas ay nagiging load-bearing kapag ang §9.2 ay tumanggap ng bagong entry, at ang reference viewer ay i-aapdeyt para mag-soft-fail sa puntong iyon.
12.4 Bersyon ng Outer Wrapper
Ang OuterWrapper na inilarawan sa §13 ay nagdadala ng sarili nitong version byte, independyente mula sa SealedQub.version at QubEnvelope.version. Ang dalawang version space ay umuunlad nang hiwalay: ang isang hinaharap na post-quantum-safe symmetric replacement ay nagpapataas sa wrapper byte nang hindi hinahawakan ang panloob na protocol version, at ang isang hinaharap na protocol-layer addition (hal., bagong envelope field) ay nagpapataas sa panloob na version nang hindi hinahawakan ang wrapper byte.
OUTER_WRAPPER_VERSION_* |
Value | Algorithm | Status |
|---|---|---|---|
OUTER_WRAPPER_VERSION_1 |
0x01 |
AES-256-GCM with 12-byte nonce, 16-byte authentication tag, AAD bound to qub_id |
v1 default |
| — | 0x02–0xFF |
Reserved | Future |
Ang mga mambabasa ay MUST tanggihan ang mga hindi kilalang wrapper version na may malinaw na error. Sadyang pinananatili ng protokol na makitid ang wrapper version space hangga't walang kongkretong migration driver na lumitaw (hal., NIST guidance na nagsusulong ng ibang AEAD); isang 0x02 slot ang ilalaan sa parehong rebisyon na nagpapakilala ng algorithm.
13. Outer Encryption Wrapper
13.1 Katwiran
Ang mga protocol layer (QubEnvelope → tlock → SealedQub) ay gumagawang time-locked ng isang naselyong qub: ang katawan ay hindi mababasa hanggang sa unlock_at at na-publish ang drand round signature. Pagkatapos ng paghahayag, gayunpaman, ang round signature ay pampubliko at ang canonical CBOR shape ng SealedQub ay nakikilala, kaya ang isang harvester na nag-index ng mga permanenteng-storage transaction ay maaaring bulk-decrypt ang buong qub corpus.
Sinasara ng outer encryption wrapper ang channel na iyon sa pamamagitan ng paggitna ng karagdagang symmetric AEAD layer sa pagitan ng canonical SealedQubCbor at ng bytes na isinusulat sa permanenteng storage. Ang 256-bit key na K ay nabubuhay lamang sa URL fragment ng delivery URL at sa mga device ng gumagamit; ang mga browser ay hindi nagpapadala ng URL fragments sa mga server, kaya ang qub.social, bawat storage gateway, at bawat CDN sa harap ng alinman dito ay observationally bulag sa K. Ang bawat qub sa permanenteng storage ay samakatuwid ay isang opaque ciphertext na ang plaintext ay hindi mababawi nang walang URL na pinili ng tagalikha na ibahagi.
Net effect:
- Enumeration immunity by default. Ang wrapped bytes sa permanenteng storage ay byte-indistinguishable mula sa arbitrary ciphertext. Ang isang harvester strategy ng "GraphQL-query para sa qub-shaped uploads, bulk-decrypt with public drand signatures" ay hindi nagtatapos sa plaintext.
- Crypto-shredding privacy posture. Ang qub.social ay literal na hindi makakapag-decrypt ng sarili nitong corpus. Ang mga subpoena ay umaabot sa ciphertext, hindi sa plaintext.
- Two-tier confidentiality ladder. Default = link-controlled access (seksyong ito). Ang recipient-encrypted private qubs (isang nakareserbang feature sa Phase 2, hindi pa naitatakda) ay nag-layer sa ibabaw bilang ikalawang tier.
13.2 Layering
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)
Ang pag-selyo at paghahayag sa protocol layer (§7, §8) ay hindi nagbabago sa ilalim ng wrapper boundary; ang wrapper ay nakakabit sa call site ng seal() at nahihiwalay sa call site ng unlock().
13.3 Istruktura ng Datos ng 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
}
Mga invariant ng field.
- Ang
versionay MUST katumbas ng0x01para sa v1.0 wrapper bytes. - Ang
qub_iday MUST katumbas ngqub_idfield ng SealedQub na nabawi pagkatapos ng pag-unwrap. Ang hakbang sa pag-unwrap ay hindi direktang ipinapatupad ito (ang AEAD AAD binding ay ginagawang imposible ang byte-level tampering), ngunit ang unlock layer ay nagsusuri ng kaugnayan nang transitively: kung ang isang tagalikha ay nag-wrap ngSealedQubCborna ang panloob naqub_iday hindi tumutugma sa wrapperqub_id, ang §8 step 11 ay nabigo. - Ang
nonceay MUST 96 bits (12 bytes), nabuong sariwa ng isang CSPRNG para sa bawat wrap operation. Ang pag-reuse ng nonce sa ilalim ng parehong key ay nagpapahintulot ng mga AEAD nonce-reuse attack na nagbabawi ng plaintext; ang mga producer ay MUST tratuhin ang (key,nonce) pairs bilang one-shot. - Ang
ciphertextay ang AES-256-GCM output: ciphertext bytes na pinagsama sa 16-byte authentication tag.ciphertext.len() == SealedQubCbor.len() + 16nang eksakto.
Encoding ng CBOR. Canonical CBOR ayon sa §3, na may parehong key-ordering rule (na-sort ayon sa encoded byte length na pataas, pagkatapos ay lexicographically). Ang apat na key ay:
| Key | Encoded bytes | Order |
|---|---|---|
nonce |
6 | 1 |
qub_id |
7 | 2 |
version |
8 | 3 |
ciphertext |
11 | 4 |
Ang unang byte ng OuterWrapper CBOR ay samakatuwid ang definite-length map header para sa 4-entry map (0xA4).
13.4 AAD Binding sa qub_id
Iniuugnay ng wrapper ang qub_id bilang AEAD additional authenticated data. Ito ay ang load-bearing structural defence laban sa tatlong klase ng atake:
| Attack | Defence |
|---|---|
Move ciphertext under a different qub_id field in the wrapper |
AAD mismatch → AEAD authentication fails |
| Mix the URL fragment of qub A with the permanent-storage bytes of qub B | AAD mismatch → AEAD authentication fails |
Tamper with the qub_id field of the wrapper after upload |
AAD mismatch → AEAD authentication fails |
Ang pagdadala ng qub_id sa wrapper plaintext ay hindi nagpapahina sa enumeration immunity nang makabuluhan — ang qub_id mismo ay isang SHA3-256 hash ng §4.1 preimage na walang mababawing preimage mula sa digest, at ang isang enumerator na nag-harvest na ng wrapper bytes ay walang natututunan mula sa nakikitang qub_id na hindi nila mahihinuha mula sa pag-iral mismo ng upload.
13.5 Mga Wrap at Unwrap Algorithm
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
Pagbagsak ng failure-mode. Ang maling K, maling nonce, AAD mismatch, at tampered ciphertext ay lahat gumagawa ng parehong DECRYPT_FAILED error. Ito ay sadyang AEAD property: ang pagkilala sa failure mode ay magbubuo ng side channel na maaaring sundutin ng isang malayong umaatake sa pamamagitan ng pagpapadala ng malformed wrapper at pag-time ng tugon. Ang mga reference implementation ay MUST i-collapse ang lahat ng AEAD failure sa iisang error shape.
13.6 Susing Materyal at Pamamahagi
Ang wrapping key na K ay isang 256-bit uniform random value na nabuo bawat qub ng isang CSPRNG. Ang mga reference implementation ay kumukuha nito mula sa:
- WASM creator:
getrandom(WebCrypto sa ilalim ngwasm_jsbackend). - Worker server-side seal route:
crypto.getRandomValues.
Pamamahagi: ang K ay MUST naka-encode bilang URL-safe base64 (RFC 4648 §5, walang padding) at idinaragdag sa delivery URL bilang fragment component:
delivery_url = <origin>/c/<arweave_tx_id>#<base64url(K)>
Ang fragment ay hindi kailanman ipinapadala sa anumang server ng isang sumusunod na browser. Ang mga recovery channel (server-side history index, opt-in email auto-send) na nagpapanatili ng buong link sa paghahatid — kasama ang fragment — lampas sa device ng gumagamit ay isang tahasang trade laban sa default crypto-shredding posture at MUST naka-gate sa tahasang pahintulot ng gumagamit.
Pagkawala ng fragment. Kung mawawalan ang isang gumagamit ng URL fragment at walang recovery channel, ang qub ay hindi mababasa. Ito ay ang load-bearing trade-off ng disenyo at MUST ipakita sa gumagamit sa oras ng pag-selyo. Pinalalakas ng MVP ang seal-time disclosure na may tahasang "save this URL" copy at isang verified-email recovery channel para sa mga gumagamit na nag-opt in.
13.7 Wala sa Saklaw para sa Seksyong Ito
- Ang paglagda ng pagiging may-akda (§9) ay hindi nagbabago: ang mga lagda ay kinakalkula sa loob ng panloob na
QubEnvelopeat nababawi pagkatapos ng unwrap → tlock decrypt → CBOR parse. - Ang mga recipient-encrypted private qub (isang nakareserbang feature sa Phase 2, hindi pa naitatakda) ay nakakabit sa ibabaw ng wrapper na ito bilang pangalawang tier ng pagiging kompidensyal; pareho ang mga tier ay maaaring aktibo nang sabay.
- Ang mga kasunduan (§6, content_type
0x03) ay naka-wrap nang eksaktong tulad ng mga text qub; ang wrapper ay byte-blind sa panloob na content type.
13.8 Mga Pampublikong qub (pag-alis ng wrapper)
Ang outer wrapper ay opsyonal sa delivery layer. Maaaring selyuhan ng isang tagalikha ang isang qub bilang pampubliko, kung saan ang canonical SealedQubCbor ay isinusulat sa permanenteng storage nang direkta, nang walang OuterWrapper layer at walang key na K:
SealedQubCbor bytes ──(public)──▶ uploaded to permanent storage as-is
SealedQubCbor bytes ──(private)─▶ AES-256-GCM(K, …) ▶ OuterWrapper ▶ uploaded
Ang isang pampublikong qub ay time-locked ngunit hindi link-gated: nananatili itong hindi mababasa hanggang sa ma-publish ang drand round nito (ang tlock layer ay hindi nagbabago), ngunit pagkatapos ng paghahayag, sinuman ang may arweave_tx_id ay maaaring mag-decrypt nito — walang kailangang URL fragment, dahil walang K. Ito ang sinadyang trade para sa mga surface na kailangang patakbuhin ng server: ang mga reveal-notification email, mga third-party embed, at mas mayamang post-reveal SEO ay lahat nangangailangan ng isang link na gumagana nang walang lihim na hindi kailanman hawak ng server (§13.6).
Mga kahihinatnang DAPAT isaalang-alang ng isang producer:
- Walang enumeration immunity. Ang mga pampublikong qub ay sa pamamagitan ng disenyo ay tinatalikuran ang §13.1 enumeration-immunity property. Ang reference upload service ay naglalagay ng
Visibility: publicpermanent-storage tag sa kanila (at sa kanila lamang) upang sila ay sadyang matuklasan; ang mga pribadong qub ay walang ganitong tag at pinananatili ang kanilang byte-indistinguishability. - Nakalantad ang plaintext title sa oras ng pag-selyo. Ang §3.2
titlefield ay plaintext sa loob ngSealedQubCbor. Sa ilalim ng wrapper ay nakatago ito hanggang magbigay ang isang mambabasa ngK; kapag walang wrapper, mababasa ito ng buong mundo sa permanenteng storage mula sa sandali ng pag-upload, bago ang paghahayag. Ang mga sumusunod na creator app ay DAPAT ipaalam ito sa oras ng pag-selyo. - Istruktural ang pagtukoy. Ang isang sumusunod na mambabasa/embed ay nakikilala ang dalawang shape sa pamamagitan ng parse: ang mga byte na nag-parse bilang
OuterWrapperay tumatahak sa unwrap-with-Kna landas; ang mga byte na nag-parse bilang isang hubad naSealedQubCboray tinatanggap nang direkta. Walang kailangang wire flag, at angqub_iday hindi nagbubuklod ng visibility — ang parehong nilalaman ay byte-identical saSealedQublayer maging selyadong pampubliko o pribado.
Ang pribado (naka-wrap) ay nananatiling default; ang pampubliko ay isang tahasang per-qub na pagpili ng tagalikha.
14. Mga Test Vector
14.1 qub_id Derivation
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
Ang mga implementasyon ay MUST gumawa ng magkaparehong body_hash at qub_id na halaga para sa input na ito. Ang test vector na ito ay SHOULD ang unang unit test na isinulat. Ang mga canonical value sa itaas ay kinakalkula ng reference implementation at MUST tugma nang bit-for-bit. Mga historical na layout ng preimage (pre-launch — walang live na qub ang umaasa sa mga ito): ang 92-byte V1.0 qub_id ay 3d9fc2390eab043d38a1669ed3b71be76f9eefe872b9569ab1aaa027b88392b0; ang 100-byte V1.1 qub_id (pagkatapos isama ang outcome_at_or_zero) ay b0d032898ad629795150fdcb3f84e518f59ed05b7a2a82bc24ebdb87f52144ed. Isinasama ng V1.2 ang drand_round at itinataas ang domain separator patungong QUB_ID_V2.
14.2 Pagmamapa ng Unlock-Round
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 Canonical CBOR Round-Trip
Ang mga implementasyon ay MUST magberipika na serialize(parse(serialize(qub))) == serialize(qub) para sa lahat ng valid na input. Ito ay isang property test, hindi iisang vector.
14.4 PactTerms CBOR (content_type 0x03)
Input:
pact_version = 1
title = "Scooter deposit"
terms = [
{ key: "Item", value: "Honda Metropolitan scooter" },
{ key: "Price", value: "$100" },
{ key: "Deposit", value: "$10" }
]
party_a = { label: "Alice" }
party_b = { label: "Bob", contact: "bob@example.com" }
notes = absent
Canonical CBOR key order (PactTerms):
"notes"(6) < "terms"(6) < "title"(6) < "party_a"(8) < "party_b"(8) < "pact_version"(13)
Canonical CBOR key order (PactTerm):
"key"(4) < "value"(6)
Canonical CBOR key order (PartyIdentifier):
"label"(6) < "contact"(8)
Ang canonical CBOR bytes at SHA3-256 body_hash ay kinakalkula ng reference implementation. Ang mga implementasyon ay MUST gumawa ng byte-identical na CBOR para sa input na ito.
Ang mga implementasyon ay MUST din magberipika na serialize(parse(serialize(pact))) == serialize(pact) para sa lahat ng valid na PactTerms na input (property test).
14.5 Mga Cross-Language Vector ng Outer Wrapper
Ang outer wrapper (§13) ay may hiwalay na canonical fixture sa crates/qub-core/tests/vectors/wrapper_v1.json. Bawat kaso ay nag-aayos ng isang (key, nonce, qub_id, sealed_cbor) tuple bilang opaque hex inputs at nag-aassert ng tiyak na expected_wrapper_hex output. Parehong reference implementations ay kumokonsumo ng parehong JSON file:
- 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).
Ang fixture ay kasalukuyang nag-pin ng tatlong kaso:
| Case | Coverage |
|---|---|
basic-text-public |
Smallest realistic SealedQub shape; no optional fields. Establishes the canonical wrapper shape for a v1.0-typical qub. |
with-recipient-pubkey |
SealedQub with recipient_pubkey set (Phase 2 path). Different inner CBOR key set, different qub_id. |
longer-body |
~4 KiB body — exercises multi-byte CBOR length prefixes inside both the inner envelope and the outer ciphertext. |
Ang mga implementasyon ay MUST gumawa ng byte-identical na expected_wrapper_hex para sa mga naitalang input. Ang pag-regenerate ng fixture ay nangangailangan ng QUB_REGEN_VECTORS=1 cargo test -p qub-core --test wrapper_vectors at nakalaan para sa mga sadyang pagbabago sa format.
15. Pamamahala ng Crypto Profile (Hinaharap)
Ang seksyong ito ay impormatibo para sa v1 at magiging normatibo sa unang pagkakataong pumasok ang ikalawang algorithm sa anuman sa mga kriptograpikong primitibo ng qub.
15.1 Kasalukuyang Posisyon
Ang protokol v1 ay nagbubuklod ng eksaktong isang algorithm bawat primitibo:
- Lagda: ML-DSA-65 (
sig_alg = 0x01; 1952-byte public key, 3309-byte signature) at unsigned (sig_alg = 0x00). Ang §9.2 registry ay walang tinukoy na ibang value; ang isang v1 verifier ay MUST tanggihan ang bawatsig_algsa labas ng{0x00, 0x01}. Ang isang hinaharap na Ed25519 entry ay inaasahan (§15.3) ngunit hindi nakalaan sa v1. - Timelock: drand quicknet lamang — ang chain hash, public key, genesis time, at period ay nakapirming network parameters na dala ng reference na
DrandTimelockProvider::quicknet()(crates/qub-core/src/tlock.rs) atconfig/drand-endpoints.json. - Outer wrapper: AES-256-GCM v1 lamang (§13).
Kasalukuyang naka-hardcode ng mga verifier ang key at signature lengths bawat primitibo. Walang agility surface na nailantad sa wire format.
15.2 Inaasahang Hugis
Kapag pumasok ang ikalawang algorithm sa protokol, ang verifier ay iko-configure para sa isang pinangalanang CryptoProfile (hal., ExqubV1) na naglilista sa eksaktong set ng mga pinahihintulutang value bawat primitibo — sig_algs, drand chains, wrapper versions, content types. Ang profile ay nakapirmi sa verify time, hindi kailanman pinag-uusapan in-band. Ang anumang value sa labas ng aktibong profile ay tinatanggihan.
Tinitiyak nito na ang pagdaragdag ng ML-DSA-87 o pag-activate ng Ed25519 ay hindi maaaring retroactively magpahina sa mga umiiral na verifier configuration: ang isang v1 verifier ay nananatiling isang v1 verifier kahit na pagkatapos i-publish ang v2 profile.
15.3 Mga Kondisyon ng Trigger
I-promote ang §15 sa normatibong status kapag ang sumusunod ay iminungkahi:
- Ang ikalawang
sig_algbyte (Ed25519 activation, ML-DSA-87, o anumang bagong entry sa §9 registry). - Ang ikalawang drand chain sa production use.
- Ang ikalawang outer-wrapper version.
Hanggang sa, ang §15 ay isang placeholder na nag-aayos ng migration shape upang ang mga hinaharap na PR ay mapunta sa isang kilalang target sa halip na muling pag-usapan ang negotiation surface mula sa simula.