مواصفات بروتوكول qub

qub بروتوكول للالتزامات الزمنية التشفيرية: نظام لختم الكلمات حتى تاريخ مستقبلي ثم التحقق لاحقًا مما خُتم بالضبط، وأي جولة drand قيّدت إصداره، وعند توفر معاملة تخزين أو إثبات من سجل الشفافية، حدٍّ أقصى مستقل ذي طابع زمني لموعد الالتزام بالنص المشفر.

تجعل ثلاث بدائيات هذا النظام يعمل. drand منارة عشوائية لامركزية—يُفرض تاريخ الكشف تشفيريًا لا بحسن نية qub. ويحفظ التخزين المتين مع سجل شفافية للإلحاق فقط البايتات المختومة ويُرسي التزامات مجمّعة في تخزين عام دائم؛ ويكتب مسار T3 المدفوع أيضًا معاملة تخزين دائم فردية. وML-DSA-65 توقيع رقمي ما بعد الكم—عند تفعيل النسب، يرتبط qub بزوج مفاتيح لا يغادر سرّه جهاز المؤلف أبدًا.

تُنتج هذه البدائيات مجتمعةً بيانًا مُقفلًا زمنيًا وكاشفًا للعبث، وقابلًا للنسب اختياريًا ولإسناد طابع زمني مستقل—إيصالًا تنمو قيمته بنمو قدرة العالم على تزييف الماضي.

ما تبقى من هذه الوثيقة هو المواصفة المعيارية المطلوبة لتنفيذات قابلة للتشغيل البيني.


مواصفة بروتوكول qub

الحقل القيمة
إصدار المستند 1.0.0 (protocol-v1.0.0)
بروتوكول السلك 0x01
الغلاف الخارجي 0x01
تاريخ السريان 2026-09-23
الحالة حالي
مراجَع حتى 2026-09-23

هذه الوثيقة هي مواصفة البروتوكول المعيارية لنظام الالتزام الموقوت في qub. تُعرّف هياكل البيانات، وقواعد التسلسل، وصيغ الاشتقاق، وإجراءات التحقق المطلوبة لتنفيذات قابلة للتشغيل البيني.

النطاق: طبقة البروتوكول محايدة لغويًا عن قصد — يكون جسم qub نصًا أو ماركداون أو بايتات ميثاق غير مفسَّرة، والعَرْض المراعي للُّغة هو مسؤولية المُشاهِد (تطبيق qub.social على الويب، إطار <qub-embed>، عملاء MCP، إلخ).


1. الترميز والاصطلاحات

الترميز المعنى
u8, u64, i64 أعداد صحيحة بدون/بإشارة بعرض البتات المحدد
[u8; N] مصفوفة بايتات بطول ثابت يبلغ N بايتًا
Vec<u8> مصفوفة بايتات متغيرة الطول
Option<T> قيمة من النوع T أو غائبة
String سلسلة نص UTF-8، مُطبَّعة وفق NFC
`
SHA3-256(x) تجزئة NIST SHA3-256 لسلسلة البايتات x (FIPS 202)
ceil(x) دالة السقف: أصغر عدد صحيح ≥ x
CBOR تمثيل ثنائي مختصر للكائنات (RFC 8949)
big-endian البايت الأهم أولًا

تُرمَّز جميع الأعداد الصحيحة في تركيب الصور الأولية على هيئة مصفوفات بايتات big-endian بعرض ثابت (i64 ← 8 بايتات، u8 ← 1 بايت) ما لم يُحدَّد خلاف ذلك.

جميع الطوابع الزمنية بـ ثوان Unix بتوقيت UTC.


2. هياكل البيانات

2.1 ComposeQub (حالة المُنْشِئ في الذاكرة)

غير متسلسلة إلى CBOR. غير مكتوبة إلى التخزين الدائم. محلية لتطبيق المُنْشِئ.

ComposeQub {
    draft_id:       [u8; 16],        // Random, generated locally
    created_at:     i64,             // Unix seconds UTC
    unlock_at:      Option<i64>,     // Unix seconds UTC; None while composing
    visibility:     u8,              // 0x00 = private; 0x01 = public
    content_type:   u8,              // 0x01 text; 0x03 pact; 0x04 verdict
    plaintext:      Vec<u8>,         // Raw body bytes (UTF-8 for text)
    sender_label:   Option<String>,  // Display name; V2-signed when authorship is enabled
    title:          Option<String>,  // Plaintext countdown title; bound via title_hash
    reply_to:       Option<[u8; 32]>,// Parent qub_id; V2-signed when authorship is enabled
    outcome_at:     Option<i64>,     // Optional future judgment time; bound to qub_id
    status:         DraftStatus,     // Composing | Sealed | Uploaded | Failed
}

2.2 QubEnvelope (الحُمولة المفكوكة)

تُسلسَل باستخدام CBOR قانوني (§3). مُشفَّرة داخل SealedQub. هذا هو الهيكل الذي يُثبت سلامة المحتوى بعد فكّ التشفير.

QubEnvelope {
    version:             u8,              // Protocol major version (0x01 for v1)
    qub_id:              [u8; 32],        // Derived (see §4.1)
    content_type:        u8,              // Content type registry (see §6)
    created_at:          i64,             // Unix seconds UTC
    unlock_at:           i64,             // Unix seconds UTC
    outcome_at:          Option<i64>,     // When reality renders judgment; bound to qub_id
    sender_label:        Option<String>,  // Not in qub_id; V2-signed when authorship is enabled
    reply_to:            Option<[u8; 32]>,// Parent qub_id; not in qub_id; V2-signed when present
    body:                Vec<u8>,         // UTF-8 text or canonical CBOR pact/verdict body
    body_hash:           [u8; 32],        // SHA3-256(body) (see §4.2)
    sig_alg:             u8,              // Signature algorithm (see §9.2)
    author_signature:    Option<Vec<u8>>, // Set when sig_alg != 0x00
    author_pubkey:       Option<Vec<u8>>, // Set when sig_alg != 0x00
    cosigner_pubkey:     Option<Vec<u8>>, // Set for cosigned pact bilateral agreements
    cosigner_signature:  Option<Vec<u8>>, // Set for cosigned pact bilateral agreements
}

الأساس (qub نصّي غير موقّع): version = 0x01، وcontent_type = 0x01، وsig_alg = 0x00؛ وحقول التوقيع وشريك التوقيع غائبة. وقد توجد حقول بيانات وصفية اختيارية أخرى.

تكوينات v1 الأخرى: content_type = 0x03 (جسم ميثاق، انظر §6.1)؛ sig_alg = 0x01 (ML-DSA-65) مع وجود author_signature وauthor_pubkey (انظر §9.3)؛ وجود cosigner_pubkey وcosigner_signature معًا للمواثيق ذات التوقيع المُشتَرَك (انظر §9.7)؛ ضبط reply_to على qub_id للـ qub الأب لـ qubs سلاسل الردود (انظر §9.3 لاستتباعات نطاق التوقيع).

2.3 SealedQub (الصيغة السلكية القانونية)

تُسلسَل باستخدام CBOR قانوني (§3). هذه هي القطعة الأثرية السلكية الداخلية: تُخزَّن هذه البايتات عارية للتسليم العام، بينما يلفّها التسليم الخاص في OuterWrapper قبل التخزين (§13).

SealedQub {
    version:           u8,              // Protocol major version (0x01 for v1)
    qub_id:            [u8; 32],        // Same as QubEnvelope.qub_id
    visibility:        u8,              // 0x00 = private/wrapped; 0x01 = public/bare
    unlock_at:         i64,             // Unix seconds UTC
    outcome_at:        Option<i64>,     // Surfaced on the verdict-watch CTA
                                        //   before reveal; mirrors QubEnvelope.outcome_at;
                                        //   bound to qub_id via the §4.1 preimage.
    drand_chain_id:    String,          // drand chain hash (hex string)
    drand_round:       u64,             // Target drand round number
    drand_chain_version: Option<u8>,    // W3 — chain-migration version. Absent / 0 = quicknet
                                        //   (the only chain today). Lets a future chain swap
                                        //   be expressed on the wire without a breaking format
                                        //   change. NOT part of the §4.1 qub_id preimage, so its
                                        //   addition never alters an existing qub's identity.
    tlock_ciphertext:  Vec<u8>,         // tlock-encrypted QubEnvelope CBOR bytes
    recipient_pubkey:  Option<[u8; 32]>,// Reserved field; accepted by canonical CBOR
                                        //   but not interpreted by the v1 reference viewer
    title:             Option<String>,  // Plaintext title surfaced on the viewer
                                        //   countdown before reveal. Bound to qub_id
                                        //   via title_hash (§4.1). 1..=100 NFC code
                                        //   points, no hostile/control code points.
}

2.4 RevealedQub (حالة تطبيق المُشاهِد)

غير متسلسلة إلى CBOR. محلية لتطبيق المُشاهِد. تُبنى بعد فكّ التشفير والتحقق بنجاح.

RevealedQub {
    qub_id:              [u8; 32],
    arweave_tx_id:       String,
    visibility:          u8,
    content_type:        u8,
    created_at:          i64,
    unlock_at:           i64,
    outcome_at:          Option<i64>,       // Carried from both wire layers; drives the verdict-watch block
    drand_chain_id:      String,
    drand_round:         u64,
    sender_label:        Option<String>,
    title:               Option<String>,    // Carried forward from SealedQub.title
    reply_to:            Option<[u8; 32]>,
    body:                Vec<u8>,
    body_hash:           [u8; 32],
    body_hash_verified:  bool,
    author_signature:    Option<Vec<u8>>,
    author_pubkey:       Option<Vec<u8>>,
    signature_verified:  Option<bool>,
    cosigner_pubkey:     Option<Vec<u8>>,
    cosigner_signature:  Option<Vec<u8>>,
    cosigner_verified:   Option<bool>,
}

3. ملف CBOR القانوني

يَجِب أن يلتزم كل تسلسل لـ SealedQub وQubEnvelope بهذا الملف. أيّ تنفيذيْن يعالجان البنية المنطقية نفسها يَجِب أن يُنتجا بايتات متطابقة.

3.1 قواعد الترميز

القاعدة المواصفة
المعيار RFC 8949 §4.2.1 (متطلبات الترميز الحتمي الأساسي)
ترتيب مفاتيح الخريطة تُرتَّب حسب طول البايتات المُرمَّزة أولًا (الأقصر قبل الأطول)، ثم معجميًا (بايت ببايت للترميزات بنفس الطول)
ترميز الأعداد الصحيحة أقصر صيغة: 0–23 في البايت الابتدائي؛ 24–255 في بايتين؛ 256–65535 في 3 بايتات؛ إلخ.
ترميز الطول أطوال محدَّدة فقط. لا يُسمح بأي مصفوفات أو خرائط أو سلاسل بايتات أو سلاسل نص بطول غير محدَّد (المعلومات الإضافية = 31 محظورة).
الوسوم لا توجد وسوم CBOR (النوع الرئيسي 6 محظور).
الفاصلة العائمة لا أعداد عائمة (النوع الرئيسي 7 بقيم 0xF9–0xFB محظور).
سلاسل النص مُرمَّزة بـ UTF-8، مُطبَّعة وفق NFC (صيغة تطبيع يونيكود C).
سلاسل البايتات بايتات خام. لا ترميز base64 في طبقة CBOR.
المفاتيح المكرَّرة يُرفض مع خطأ. يَجِب على المحللات ألّا تقبل بصمت مفاتيح خريطة مكرَّرة.
المفاتيح غير المعروفة يُرفض مع خطأ. يَجِب على المحللات ألّا تتسامح مع مفاتيح خريطة خارج مجموعة المفاتيح القانونية للنوع — يَجِب ألّا يُفضي فكُّ ترميز سلسلتي بايتات قانونيتين متمايزتين إلى القيمة نفسها أبدًا (encode(decode(x)) == x)، وفي الحمولات الموقّعة سيكون أي مفتاح إضافي محتوى خفيًا يلتزم به كلا التوقيعين. تطوُّر المخطط يمرّ عبر version، لا عبر مفاتيح إضافية أبدًا.
القيم البسيطة يُسمح فقط بـ true (0xF5) وfalse (0xF4) وnull (0xF6).
الحقول الاختيارية الحقول الاختيارية الغائبة تُحذف من خريطة CBOR كليًا (لا تُرمَّز كـ null). الحقول الاختيارية الموجودة تُدرَج بترتيب المفاتيح المُصنَّف.

3.2 ترتيبات المفاتيح القانونية المُتحقَّق منها

ترتيبات المفاتيح هذه معيارية. يَجِب على التنفيذات إصدار المفاتيح بهذا الترتيب تمامًا. يَنْبَغي لتأكيدات التصحيح أن تتحقق من الترتيب في بنيات ما قبل الإصدار.

QubEnvelope (الإصدار 0x01، غير موقّع، جميع الحقول الاختيارية غائبة):

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

اشتقاق ترتيب مفاتيح QubEnvelope: كل مفتاح هو سلسلة نصية CBOR. الطول المُرمَّز = 1 بايت رأس + طول السلسلة (للسلاسل الأقصر من 24 بايتًا). فرز حسب الطول المُرمَّز الكلي أولًا، ثم معجميًا للمفاتيح ذات الطول نفسه.

SealedQub (الإصدار 0x01، عام، بلا مُستقبِل):

"title"             (6 encoded bytes)   ← only if present
"qub_id"            (7 encoded bytes)
"version"           (8 encoded bytes)
"unlock_at"         (10 encoded bytes)
"outcome_at"        (11 encoded bytes)  ← only if present (verdict mechanic)
"visibility"        (11 encoded bytes)
"drand_round"       (12 encoded bytes)
"drand_chain_id"    (15 encoded bytes)
"recipient_pubkey"  (17 encoded bytes)  ← only if present
"tlock_ciphertext"  (17 encoded bytes)
"drand_chain_version" (20 encoded bytes) ← only if present (W3; absent = quicknet)

PactTerms (جسم الميثاق، content_type 0x03):

"notes"         (6 encoded bytes)  ← only if present
"terms"         (6 encoded bytes)
"title"         (6 encoded bytes)
"party_a"       (8 encoded bytes)
"party_b"       (8 encoded bytes)
"pact_version"  (13 encoded bytes)

PactTerm (صفّ في مصفوفة terms):

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

PartyIdentifier (خريطة party_a / party_b):

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

3.3 مرجع ترميز البايتات

النوع ترميز CBOR مثال
تجزئة SHA3-256 (32 بايتًا) 0x58 0x20 + 32 بايتًا body_hash، qub_id
الطوابع الزمنية (i64) النوع الرئيسي 0 (موجب) أو 1 (سالب)، أقصر ترميز ثوان Unix
الإصدار (u8، القيمة 1) 0x01 (بايت واحد)
نوع المحتوى (u8، القيمة 1) 0x01 (بايت واحد)
sig_alg (u8، القيمة 0) 0x00 (بايت واحد)
توقيع ML-DSA-65 (3,309 بايتات) 0x59 0x0C 0xED + 3,309 بايتات author_signature، cosigner_signature
مفتاح ML-DSA-65 العام (1,952 بايتات) 0x59 0x07 0xA0 + 1,952 بايتات author_pubkey، cosigner_pubkey

4. الاشتقاقات المعيارية

4.1 qub_id

يُحدِّد qub_id qub بشكل فريد ويربط QubEnvelope بـ SealedQub. يُشتق حتميًا من محتوى الغلاف.

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

ترميز فاصل النطاق: السلسلة "QUB_ID_V2" هي 9 بايتات ASCII. يُضاف بايت حشو 0x00 واحد للوصول إلى 10 بايتات للمحاذاة. يَجِب على التنفيذات استخدام هذه البايتات العشر بالضبط: [0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00].

ترميز outcome_at: وسّعت مراجعة تنفيذية سابقة للإصدار الصورة الأولية من 92 إلى 100 بايت لطيّ حقل outcome_at الاختياري ضمن الربط. يُرمَّز غياب outcome_at بثماني بايتات صفرية؛ وترفض مُتحقِّقات البروتوكول outcome_at <= 0 في كل موضع، فلا تستطيع هذه القيمة الحارسة أن تتصادم مع قيمة مشروعة. انظر §3.2 (الصيغة السلكية) والملف الداخلي tasks/verdict-uplift-plan.md للآلية التحكيمية التي تُحفّز هذا الحقل.

ترميز drand_round: وسّعت مراجعة تنفيذية لاحقة سابقة للإصدار الصورة الأولية من 100 إلى 108 بايت لطيّ drand_round (جولة drand المستهدفة، §4.3) ضمن الربط، ورفعت فاصل النطاق إلى QUB_ID_V2. وهذا يربط جولة القفل الزمني ضمن هوية qub: لا تستطيع أي بوابة إعادة ربط النص المُشفَّر بجولة مختلفة (مثلًا جولة ماضية بالفعل) عن الجولة التي يقتضيها unlock_at المعروض. كما يتحقق إجراء فكّ القفل (§8) إضافةً إلى ذلك من أن الجولة المخبوزة في مقطع نص tlock المُشفَّر تطابق unlock_round(unlock_at)، فيكون وقت فكّ القفل المعروض هو الجولة التي تتحكم في فكّ التشفير بشكل قابل للإثبات.

الخصائص:

4.2 body_hash

body_hash = SHA3-256(body)

حيث body هو حمولة المحتوى الخامة Vec<u8>. لـ qubs النصية، هذا هو جسم qub المُرمَّز بـ 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

حيث title هو العنوان النصي الاختياري الذي يظهر على عدّ المُشاهِد التنازلي قبل الكشف (انظر §3.2). يُجرى تطبيع NFC وقت التجزئة كي يكون الموجز ثابتًا عبر تتابعات نقاط الترميز المتكافئة بصريًا. القيمة الصفرية بالكامل محجوزة لحالة الغياب؛ والسلسلة الفارغة مرفوضة عند حدود CBOR القانوني بوصفها ترميزًا غير قانوني لـ "غائب" (الترميز القانوني يحذف الحقل كليًا).

4.3 ربط جولة فكّ القفل

drand_round = floor((unlock_at - chain_genesis_time) / chain_period_seconds) + 1
المُعامل المصدر مثال
unlock_at ثوان Unix UTC يختارها المستخدم 1735689600 (2025-01-01 00:00:00 UTC)
chain_genesis_time معلومات سلسلة drand (genesis_time) 1595431050
chain_period_seconds معلومات سلسلة drand (period) 30

هذا هو ربط tlock المرجعي (CurrentRound في drand). تنشر drand الجولة N عند chain_genesis_time + (N - 1) * chain_period_seconds، لذا تختار الصيغة الجولة الحالية عند unlock_at—الجولة التي يكون توقيعها أول توقيع يستطيع استخدامه مشاهد يصل عند unlock_at.

خاصية المحاذاة (الحالة المهمة عمليًا): عندما يكون (unlock_at - chain_genesis_time) قابلًا للقسمة تمامًا على chain_period_seconds، يُنشر توقيع الجولة المحددة بالضبط عند unlock_at، لا قبله أبدًا. وينطبق ذلك دائمًا على النشر المرجعي: وقت نشأة quicknet (1692803367) قابل للقسمة على فترتها البالغة 3 ثوانٍ، وتثبّت التطبيقات المرجعية أوقات الفتح على دقائق كاملة. وعند unlock_at غير محاذٍ، يُنشر توقيع الجولة المحددة قبل unlock_at بأقل من فترة واحدة—ودقة الالتزام الزمنية هي فترة منارة واحدة.

الربط القديم السابق للإصدار وتسامح جهة فك القفل: كان الربط الأصلي ceil((unlock_at - chain_genesis_time) / chain_period_seconds)، الذي اختار—في الحالة المحاذية أعلاه—الجولة المنشورة قبل unlock_at بفترة كاملة، فجعل النص المشفر قابلًا لفك التشفير مبكرًا بفترة واحدة بالضبط. يختلف الربطان بـ +1 تحديدًا عندما تقبل دلتا القسمة على الفترة، ويتفقان فيما عدا ذلك. ولأن drand_round مطوي في صورة qub_id الأولية الثابتة (§4.1)، لا يمكن إعادة اشتقاق القطع المختومة بالربط القديم؛ لذلك يَجِب على المتحققين عند إجراء الفحص المتقاطع في §8 الخطوة 6a قبول drand_round مخزنة مساوية إما للجولة المشتقة أو للجولة المشتقة ناقص واحد (ويَجِب أن يشترطوا أن تساوي جولة مقطع tlock الجولة المخزنة تمامًا). يوسع التسامح أبكر توقيع مُقيِّد بفترة واحدة على الأكثر. وتطبّق خدمة تجهيز المواثيق التسامح نفسه حين تعيد اشتقاق qub_id لميثاق مجهز (عند التجهيز وعند التوقيع المشترك): إذا لم يُعِد الربط الحالي إنتاج qub_id الملتزم به وكانت دلتا تقبل القسمة على الفترة، فإنها تعيد المحاولة بالجولة ناقص واحد، وتختم الميثاق النهائي إلى الجولة التي يربطها qub_id فعليًا—لا إلى الجولة المعاد حسابها عميانيًا، إذ سيجعل ذلك القطعة غير قابلة للاشتقاق إلى الأبد.

التحقق: يَجِب أن يكون unlock_at في المستقبل وقت الختم. يَجِب ألّا يبعد unlock_at أكثر من 10 سنوات عن created_at (للحدّ من مخاطر الاعتماد بعيد الأفق على drand؛ يَنْبَغي لواجهة المستخدم أن تُحذّر من تواريخ فكّ قفل تتجاوز سنتين).


5. أنواع جديدة للصيغة السلكية

تُوفّر الأنواع الجديدة للصيغة السلكية سلامة وقت الترجمة ضد الخلط بين بايتات CBOR و JSON والنص الخام وغيرها من ترميزات البايتات.

النوع يحتوي يُنتج بواسطة يُستهلَك بواسطة
SealedQubCbor CBOR قانوني لـ SealedQub serialize_sealed_qub() القطعة الأثرية السلكية الداخلية؛ تُخزَّن عارية للتسليم العام أو مغلَّفة للتسليم الخاص، ثم يستعيدها المُشاهِد
QubEnvelopeCbor CBOR قانوني لـ QubEnvelope serialize_qub_envelope() مدخل تشفير tlock، مخرج فكّ تشفير tlock

5.1 قواعد البناء

// 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 التحقق عند البناء

يَنْبَغي لـ from_encoded() أن تتحقق من أن المدخل يبدأ برأس خريطة CBOR صالح. يحدث التحقق البنيوي الكامل وقت التحليل، لا وقت البناء، تجنبًا للتحليل المزدوج.


6. سجل أنواع المحتوى

القيمة النوع أقصى حجم للجسم ملاحظات
0x00 محجوز (غير صالح) — يَجِب ألّا يُستخدم
0x01 نص عادي (UTF-8، ماركداون مقيّد) 50 KB مدفوع / 10 KB مجاني انظر §10 لقواعد العرض. تُفرض القسمة بين المجاني والمدفوع بواسطة خدمة الرفع؛ السقف الصلب لطبقة البروتوكول هو 50 KB.
0x02 محجوز (مستقبلي) — مُخصَّص لنوع محتوى مستقبلي؛ غير صالح في v1. يَجِب على المُشاهِدين رفضه وفقًا للقاعدة أدناه.
0x03 ميثاق (اتفاق ثنائي، جسم CBOR) 100 KB الجسم هو CBOR قانوني PactTerms (§6.1). توقيع شريك التوقيع وفقًا لـ §9.7.
0x04 حكم (تقييم ذاتي من المنشئ، جسم CBOR) 8 KB الجسم هو CBOR قانوني VerdictBody (§6.2). يصدر فقط عبر نيّة verdict على جانب النظام. علاقة الأب موجودة على وسم Arweave Parent-Tx-Id، لا في الجسم. انظر verdict-uplift-plan §3.4.

يَجِب على المُشاهِدين رفض أنواع المحتوى غير المعروفة برسالة خطأ واضحة مرئية للمستخدم. يَجِب على المُشاهِدين ألّا يحاولوا عرض الأنواع غير المعروفة كنصّ.

6.1 جسم الميثاق (content_type = 0x03)

جسم الميثاق هو ترميز CBOR القانوني لقيمة 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 bytes), value: String (≤ 2,000 bytes) } // NFC
PartyIdentifier{ label: String (≤ 100 bytes), contact: Option<String (≤ 320 bytes)> }

ترتيبات مفاتيح CBOR القانونية للخرائط الثلاث جميعها مذكورة في §3.2. يَجِب ألّا يتجاوز إجمالي CBOR الميثاق المتسلسل 100 KB (يطابق §6).

مُميِّز المخطط. الصف الأول في terms لميثاق structured/v1 يَجِب أن يكون { key: "pact_schema", value: "structured/v1" }. الصفوف بدون هذه العلامة هي مواثيق "مخصصة" ولا تتلقى أي تحقق منظَّم أو عرضًا واعيًا بالمخطط.

خانات الإقرار المُجمَّدة. تحمل مواثيق structured/v1 بالضبط أربعة صفوف إقرار تحت هذه المفاتيح:

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

value لكل منها هو واحدة من ثماني سلاسل إنجليزية مُجمَّدة يختارها زوج (role, kind)، حيث role ∈ { seller, buyer, provider, client } وkind ∈ { standard, capacity }. السلاسل نفسها هي بيانات بروتوكول معيارية — توقيعات ML-DSA-65 لكلا الطرفين تلتزم بالبايتات الدقيقة عبر body_hash. لا تُترجَم محليًا؛ الجسم الموقّع محايد لغويًا. أي تغيير في الصياغة يستلزم إصدارًا جديدًا من المخطط (structured/v2).

تُثبَّت السلاسل الثماني وعملية البحث عنها (acknowledgement_for(role, kind)) ومبررات كل منها بواسطة التنفيذ المرجعي. يَجِب على التنفيذات المطابقة إصدار قيم إقرار متطابقة بالبايتات؛ واختبارات body-hash لـ SHA3-256 الذهبية المُغطّية للتركيبات الأربعة للأدوار تكشف أي انجراف.

ترتيب عرض المُشاهِد. تحتوي سلاسل الإقرار على عبارات مثل "described above"، والتي تفترض أن صفوف الوصف / النطاق تُعرَض قبل الإقرارات. يَجِب على المُشاهِدين عرض مصفوفة terms بترتيب CBOR؛ إعادة الترتيب تكسر دلالات النص.

جهة اتصال الطرف المقابل. عندما يكون contact للطرف B عنوان بريد إلكتروني صالحًا، تُرسل خدمة رفع qub تلقائيًا بريدًا إلكترونيًا لدعوة المراجعة / التوقيع المشترك وقت التحضير وتربط التوقيع المشترك النهائي بالتحقق من ذلك العنوان نفسه (§9.7). المواثيق التي يكون فيها جهة اتصال الطرف B غائبة لا يزال يمكن التوقيع المشترك عليها، لكن فقط عبر قناة خارجية — ترفض الخدمة طلبات التوقيع المشترك التي لا يمكنها إنتاج علامة تحقق-بريد-إلكتروني مطابقة لمدة 15 دقيقة.

6.2 جسم الحكم (content_type = 0x04)

جسم الحكم هو ترميز CBOR القانوني لقيمة 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
}

ترتيب مفاتيح CBOR القانوني:

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

يَجِب ألّا يتجاوز إجمالي CBOR الحكم المتسلسل 8 KB (يطابق صفّ السجل أعلاه).

تعداد النتيجة. بايت السلك محايد لجهة النيّة؛ السلال الأربع Right / Partial / Wrong / Unfalsifiable تغطّي فضاء النتائج لكل نيّة حاملة للحكم. أما التسميات الخاصّة بكل نيّة ("توقّعتها" / "وفّيت به" / "أُطلق" / "أكّدتها الأحداث" لـ Right، إلخ) فهي شأن عرض من جانب المُشاهِد يُحَلّ مقابل نيّة qub الأب — يبقى السلك محايدًا لغويًّا ولجهة النيّة. القيم خارج النطاق 1..=4 يَجِب رفضها عند فكّ الترميز.

ربط الأب. لا يحمل qub الحكم مرجع الأب في جسمه. معرّف معاملة Arweave لـ qub الأب يُصدَر كوسم تخزين Parent-Tx-Id وقت الرفع (طبقة وسوم التخزين §7). هذا يُبقي الجسم بيانًا موقّعًا قائمًا بذاته للتقييم الذاتي؛ سلسلة التدقيق ("مُحقّ بشأن ماذا؟") تُثبَت عبر بحث وسم Arweave.

سلامة رابط الدليل (معياري). عندما يكون evidence_url حاضرًا، يَجِب على المُتحقّقين (جانب التأليف، جانب السلك، حافة الـ Worker) فرض ما يلي:

  1. HTTPS فقط. يَجِب أن تبدأ السلسلة بمتسلسلة البايتات https://. أي مخطّط آخر — http أو ftp أو javascript أو data أو file وغيرها — يُرفض.
  2. حدّ الطول. ≤ 2,048 بايت (الحدّ العملي لعنوان المتصفح).
  3. NFC + فحص النقاط المعادية. نفس قاعدة title وreflection — نقاط إلغاء ترتيب الكتابة الثنائي / العرض الصفري / كتلة الوسوم / BOM / C0 / C1 تُرفض. التعريف يطابق crate::handle::contains_hostile_text_codepoint في Rust وworkers/api/src/utils/unicode.ts::isHostileCodepoint في TS (حافظ على التوافق بينها).
  4. بدون مسافات أو أحرف تحكم ASCII. المسافات / DEL / البايتات تحت 0x20 في أيّ موضع من الرابط تُرفض — يُغلق ذلك ناقل حقن \n/\t الذي لا تغطّيه قاعدة bidi.
  5. مقطع مضيف غير فارغ. كل ما بين https:// وأول / أو ? أو # يَجِب أن يكون غير فارغ.

لا جلب من جانب الخادم. يَجِب ألّا يتولّى الـ Worker وكالة الرابط أو جلبه أو معاينته. البروتوكول يخزّن سلسلة فقط؛ العرض يحدث على جانب المُشاهِد بـ rel="nofollow noopener noreferrer" target="_blank" مع إظهار المضيف بجوار نصّ الرابط.

التأمّل. نصّ تأمّل اختياري يكتبه المنشئ ("ما الذي تغيّر، ما الذي تعلّمته"). نفس تحقّق NFC + النقاط المعادية المطبَّق على title. الإدخال الفارغ أو المؤلَّف من مسافات فقط يُختزل إلى غائب وقت البناء.

نسخة المخطط. v1 تدعم verdict_version = 0x01 فقط. مراجعات المخطط المستقبلية ترفع هذا البايت وتظهر مع نسخة بروتوكول جديدة وفق §12.


7. بروتوكول الختم

تسلسل الختم الكامل. كل خطوة معيارية.

 1. User composes plaintext and metadata in ComposeQub.
 2. Validate:
    a. body is non-empty.
    b. body size ≤ max for content_type and user tier (see §6).
    c. unlock_at is in the future.
    d. unlock_at ≤ created_at + 10 years.
    e. content_type is a known, supported value.
    f. visibility is 0x00 (private) or 0x01 (public).
 3. Compute body_hash = SHA3-256(body).
 4. Set created_at = current Unix seconds UTC.
 5. Select drand chain. Load chain_genesis_time and chain_period_seconds, and
    compute drand_round = floor((unlock_at - chain_genesis_time) / chain_period_seconds) + 1
    (§4.3). (Computed here, before qub_id, because drand_round is bound into the
    qub_id preimage—§4.1.)
 6. Compute qub_id (see §4.1), folding in drand_round from step 5.
 7. Construct QubEnvelope with all fields.
 8. Serialise QubEnvelope using canonical CBOR → bytes B.
    Assert: serialised output matches canonical profile (§3).
 9. Compute C = tlock_encrypt(B, drand_round, drand_chain_public_key).
10. Construct SealedQub with tlock_ciphertext = C and matching qub_id, version,
    visibility, unlock_at, drand_chain_id, and drand_round.
11. Serialise SealedQub using canonical CBOR → SealedQubCbor.
12. Select the delivery shape from visibility:
    a. Private (0x00): generate K = 32 random bytes and N = 12 random bytes
       using a CSPRNG. Compute W = wrap_sealed_qub(SealedQubCbor,
       qub_id=qub_id, key=K, nonce=N) per §13. Upload payload = W.
    b. Public (0x01): upload payload = bare SealedQubCbor; do not generate K.
13. Display seal-time disclosure. User confirms.
14. Validate upload eligibility via the qub upload service (bot-detection, entitlement, rate limits).
15. Submit the selected upload payload to the qub upload service. For a private
    browser seal, the service is byte-blind to the inner SealedQubCbor and never
    receives K. The Builder `/api/v1/seal` route is an explicit exception: it
    receives plaintext and caller-supplied K in memory, then persists neither.
16. Receive arweave_tx_id from the service. For private delivery, construct
    `<origin>/c/<arweave_tx_id>#<base64url(K)>` (or the equivalent short-code
    path). For public delivery, omit the fragment. Browsers do not transmit URL
    fragments to servers, so K from the browser-seal path is not observed by
    qub.social or any storage gateway.

طبقة وسوم التخزين (خارج النطاق). تُرفق خدمة رفع qub مجموعة صغيرة عمدًا من وسوم معاملات التخزين إلى جانب حُمولة الرفع المحددة. Content-Type=application/octet-stream مطلوب معياريًا. تُرفق الخدمة المرجعية إضافيًا ثلاثة وسوم اختيارية عندما يختار المُنْشِئ إظهارها: Intent (نية إنشاء مُتحقَّق منها بقائمة السماح—announcement أو thesis أو prediction أو letter أو secret أو commitment أو proof أو verdict الصادر عن النظام)، وAuthor (بصمة المفتاح العام للمُنْشِئ وفقًا لـ §9.3 ست عشرية صغيرة من 64 رمزًا)، وParent-Tx-Id (معرّف معاملة التخزين لـ qub الأب لسلاسل الردود، base64url من 43 رمزًا).

وسم Author اختياري لكل qub: تطبيق المُنْشِئ المرجعي يُرفقه فقط عندما يُفعّل المستخدم صراحةً النسب العام وقت الختم. عند إيقاف التفعيل — وهو الافتراضي — لا يُكتب وسم Author ويكون qub غير منسوب على السلسلة: لا شيء في التخزين الدائم يربط الرفع بمعرّف المُنْشِئ أو بريده الإلكتروني أو qubs أخرى له. عند تفعيل التبديل، تنحلّ بصمة Author إلى @handle الذي اختاره المُنْشِئ عبر سلسلة التَّوْثيق في §9.5. علاقات سلاسل الردود وIntent لا تُعرّف الهوية. في التسليم الخاص يشفّر الغلاف الخارجي (§13) قطعة SealedQub الداخلية القابلة للتمييز، لذلك لا يكفي حصاد الأغلفة المخزّنة والحصول على توقيعات drand العامة لاستعادة الجسم من دون K؛ وتظل وسوم التخزين بيانات وصفية عامة عمدًا.

لا تُرفق الخدمة المرجعية عمدًا وسوم App-Name أو App-Version أو Type: أي مرشّح قائم على قيمة واحدة من هذا القبيل سيُرجع كامل مجموعة qubs لاستعلام GraphQL، وهذا يتعارض مع نطاق سرية الجسم فقط للغلاف.

يَجِب على المُتحقِّق المطابق ألّا يعتمد على أي وسم تخزين للتحقق من طرف ثالث وفقًا لـ §11؛ تجزئة الجسم / qub_id / التوقيع تلتزم بالـ CBOR الداخلي فقط، لا بمجموعة الوسوم.


8. بروتوكول فكّ القفل

تسلسل فكّ القفل الكامل. كل خطوة معيارية.

 1. Viewer opens delivery URL. Extract arweave_tx_id from the path and retain
    the optional URL fragment. Do not assume a missing fragment is an error:
    public/bare delivery intentionally has no K.
 2. Check denylist. If tx_id is denylisted → display block message. Stop.
 3. Fetch the stored bytes (with multi-gateway fallback).
 3a. Resolve the delivery shape structurally:
    a. If the bytes parse as OuterWrapper, require a well-formed 32-byte K in
       the URL fragment, require wrapper version 0x01, and unwrap per §13.
       Any missing/malformed K or AEAD failure is a terminal error.
    b. Otherwise require the bytes to parse as bare SealedQubCbor; no K is
       required. If neither shape parses, report an integrity error.
 4. Parse SealedQubCbor → SealedQub.
 5. Validate: SealedQub.version is known (0x01), visibility is known, and the
    delivery shape matches it (wrapped = 0x00 private; bare = 0x01 public).
    Reject any mismatch or unknown value.
 6. If current time < SealedQub.unlock_at → display countdown. Poll or wait.
 6a. Round-binding check. Recompute expected_round from
    SealedQub.unlock_at per §4.3. Reject unless SealedQub.drand_round ==
    expected_round OR SealedQub.drand_round == expected_round - 1 (the
    legacy pre-release mapping—see §4.3), AND the round baked into the tlock
    ciphertext stanza (read via the age/tlock header, no signature required)
    == SealedQub.drand_round exactly. The stanza round is the one that
    actually gates decryption; without this check a malicious creator could
    bind the ciphertext to an already-past round while displaying a future
    countdown, so anyone reading the stored bytes could decrypt before
    unlock_at. Implementations with no chain identity (test mocks) skip this
    check.
 7. Once current time ≥ SealedQub.unlock_at:
    a. Fetch drand round signature for SealedQub.drand_round from drand network.
    b. Compute B = tlock_decrypt(SealedQub.tlock_ciphertext, round_signature).
 8. Parse B → QubEnvelope.
 9. Validate QubEnvelope.version is known.
10. Verify: SHA3-256(QubEnvelope.body) == QubEnvelope.body_hash.
    Fail → integrity error.
11. Verify: QubEnvelope.qub_id == SealedQub.qub_id.
    Fail → integrity error.
12. Verify: QubEnvelope.unlock_at == SealedQub.unlock_at.
    Fail → integrity error.
12a. Verify: QubEnvelope.outcome_at == SealedQub.outcome_at (both absent, or
    both present and equal). Fail → integrity error.
12b. Content re-derivation. Recompute qub_id per §4.1 from the decrypted
    fields — (QubEnvelope.version, content_type, created_at, unlock_at,
    outcome_at, SealedQub.drand_round, QubEnvelope.body_hash,
    title_hash(SealedQub.title)) — and verify it equals SealedQub.qub_id.
    Fail → integrity error. The pairwise checks in steps 10-12a only prove
    the two layers agree with EACH OTHER; a forger who rewrites a bound
    field consistently on both surfaces (a pre-reveal title swap, or a
    post-round body swap with a recomputed body_hash re-encrypted to the
    same round under the same qub_id) passes them all. Only re-deriving
    the identity from content closes this.
13. Verify: QubEnvelope.content_type is known and renderable.
    Known values: 0x01 (text), 0x03 (pact), 0x04 (verdict).
    Unknown → display error.
14. If QubEnvelope.sig_alg != 0x00 → verify author signature (see §9.4).
15. If cosigner_pubkey or cosigner_signature present → verify cosigner (see §9.7).
16. Render content using the appropriate renderer (see §10 for text and §6 for pact/verdict).
17. Construct RevealedQub for display.

9. توقيع التأليف

9.1 المبرر

تُخزَّن qubs في التخزين الدائم. يَجِب أن تظل توقيعات التأليف غير قابلة للتزوير إلى أجل غير مسمى، ولذلك تستخدم v1.0 مخطط ML-DSA-65 (FIPS 204) ما بعد الكم بدلًا من مخطط كلاسيكي قد يتآكل أمانه خلال عمر qub الدائم.

9.2 سجل الخوارزميات

sig_alg المخطط حجم المفتاح حجم التوقيع الحالة
0x00 لا توقيع (غير موقّع) — — نشط
0x01 ML-DSA-65 (FIPS 204) 1,952 بايتًا 3,309 بايتات نشط
0x02 Ed25519 32 بايتًا 64 بايتًا ثابت محجوز؛ غير مدعوم في البروتوكول v1

يَجِب على مشاهدي البروتوكول v1 رفض كل قيمة خارج {0x00, 0x01}، بما فيها القيمة المحجوزة 0x02. يمنع الحجز إعادة الاستخدام عرضيًا، ولا يعني التفعيل. ويتطلب تفعيلها التغيير المحكوم الموصوف في §15.

9.3 بناء الصورة الأولية الموقَّعة

وُجدت نسختان من الصورة الأولية. يَجِب أن تستخدم كل التوقيعات V2، ويَجِب على المُتحقِّقين قبول V2 فقط. كانت الصورة الأولية القديمة V1 (الموثَّقة أدناه للرجوع التاريخي) مقبولة كخيار احتياطي للتحقق فقط أثناء الانتقال إلى V2؛ وقد أُلغي ذلك الخيار الاحتياطي، ويُرفض الآن أي توقيع يعتمد على V1 وحده.

V2 (الحالية — تُنتِجها كل عمليات توقيع المؤلف الجديدة، وكلا التوقيعين في مسار تحضير الميثاق / التوقيع المشترك):

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 اصطلاح قيمة-الغياب الحارسة نفسه الذي يتبعه title_hash (§4.2.1): 32 بايتًا صفريًا ليست ناتجًا صالحًا لـ SHA3-256، لذا لا يمكن أبدًا أن يتصادم «الغائب» مع تسمية حاضرة. كل الحقول ثابتة العرض، لذا تكون الصورة الأولية غير ملتبسة دون بادئات طول.

V1 (قديمة — مُلغاة؛ لم تعد تُنتَج ولم تعد مقبولة عند التحقق):

sig_input = SHA3-256(
    "QUB_AUTHOR_SIG_V1"  ||    // domain separator (17 bytes)
    version              ||    // u8 (1 byte)
    qub_id               ||    // [u8; 32] (32 bytes)
    body_hash            ||    // [u8; 32] (32 bytes)
    unlock_at            ||    // i64 big-endian (8 bytes)
    0x00                       // u8 (1 byte): MUST be 0x00 in v1.0
)

// Total preimage: 91 bytes → 32-byte hash

أغفلت الصورة الأولية V1 حقلي sender_label وreply_to. كانت مقبولة كخيار احتياطي للتحقق فقط أثناء الانتقال إلى V2؛ وقد أُلغي ذلك الخيار الاحتياطي منذئذٍ — يَجِب على المُتحقِّقين قبول الصورة الأولية V2 فقط. يُحتفَظ بالتعريف هنا للرجوع التاريخي ولشرح فاصل النطاق أدناه. يَجِب معاملة أي توقيع يتحقق فقط مقابل V1 على أنه فشل في التحقق.

فواصل النطاق: "QUB_AUTHOR_SIG_V1" / "QUB_AUTHOR_SIG_V2" كل منهما 17 بايتًا ASCII ([0x51, 0x55, 0x42, 0x5F, 0x41, 0x55, 0x54, 0x48, 0x4F, 0x52, 0x5F, 0x53, 0x49, 0x47, 0x5F, 0x56, 0x31/0x32]). بلا حشو. يفصل الفاصلُ المختلف بين البِنيتين نطاقيًا، لذا لا يمكن أبدًا أن يتحقق توقيع على إحدى الصورتين الأوليتين بوصفه الأخرى.

البايت الزائل: يَجِب أن يكون البايت التالي لـ unlock_at 0x00. يُعرّض التنفيذ المرجعي هذا كثابت ORG_ID_PRESENT_INDIVIDUAL = 0x00 في crates/qub-core/src/signing.rs؛ على المُشاهِدين الذين يعيدون بناء sig_input للتحقق إصدار البايت نفسه.

نطاق التوقيع — ما يُغطَّى وما لا يُغطَّى. يلتزم sig_input في V2 مباشرةً بـ version وqub_id وbody_hash وunlock_at وsender_label وreply_to (إضافة إلى فاصل النطاق الثابت وبايت org_id_present). وqub_id نفسه مُشتق من version وcontent_type وcreated_at وunlock_at وoutcome_at وdrand_round وbody_hash عبر الصورة الأولية في §4.1، لذا أي تغيير في تلك الحقول يُنتج qub_id مختلفًا ويُبطل التوقيع تَعَدّيًا. السطح الموثَّق هو إذن:

الحقل موثَّق بالتوقيع كيف
version ✓ مدخل مباشر إلى sig_input
qub_id ✓ مدخل مباشر
body_hash ✓ مدخل مباشر
unlock_at ✓ مدخل مباشر
sender_label ✓ مدخل مباشر عبر sender_label_hash (صورة V2 الأولية — الصيغة الوحيدة المقبولة)
reply_to ✓ مدخل مباشر عبر reply_to_or_zero (صورة V2 الأولية — الصيغة الوحيدة المقبولة)
content_type ✓ تَعَدّيًا، عبر صورة qub_id الأولية
created_at ✓ تَعَدّيًا، عبر صورة qub_id الأولية
outcome_at ✓ تَعَدّيًا، عبر صورة qub_id الأولية
drand_round ✓ تَعَدّيًا، عبر صورة qub_id الأولية
body ✓ تَعَدّيًا، عبر body_hash = SHA3-256(body)
author_pubkey — (ضمني) المفتاح الذي تحقَّق من التوقيع هو المؤلف، بحكم التعريف
cosigner_pubkey / cosigner_signature — مُوقَّع باستقلال على sig_input نفسه (انظر §9.7)
drand_chain_id, tlock_ciphertext, visibility — حقول SealedQub الخارجية، ليست داخل الغِلاف — مُغطّاة بثوابتها البنيوية الخاصة (اتساق الجولة / السلسلة) لكن ليس بتوقيع المؤلف. (أصبح drand_round الآن مربوطًا انتقاليًا عبر الصورة الأولية لـ qub_id — انظر أعلاه.)

لماذا V2 هي الصورة الأولية الوحيدة المقبولة.

التنفيذات التي تعرض sender_label أو reply_to على المستخدمين النهائيين يَجِب أن تُظهر الهوية الموثَّقة (بصمة المفتاح العام، التَّوْثيق) كإشارة الهوية الأساسية، لا التسمية.

9.4 إجراء التحقق

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

التحقق من التوقيع هو أغلى عملية (لا سيما ML-DSA-65). يَنْبَغي تنفيذه بعد اجتياز كل الفحوصات الأرخص (التجزئة، qub_id، unlock_at).

9.5 توثيقات الهوية

توثيقات الهوية — ربط author_pubkey بادعاءات هوية يتعرّف عليها البشر مثل معرّف qub أو عنوان بريد إلكتروني أو معرّف اجتماعي أو شهادة passkey — هي تحسين تدريجي من جانب المُشاهِد وليست مطلوبة للتحقق من التوقيع. يَجِب على المُشاهِدين الذين يحلّون التَّوْثيقات إلى هوية عرض تطبيق الأسبقية:

handle > email > social > fingerprint

البصمة الاحتياطية هي ست عشرية صغيرة لـ SHA3-256(author_pubkey)؛ تكون متاحة دائمًا لأي qub موقّع. يَجوز للمُشاهِدين اختصارها للعرض — يعرض المُشاهِد المرجعي qub: متبوعًا بأول وآخر أربع بايتات (qub:<8 hex>…<8 hex>).

يستطيع المُتحقِّق المطابق إكمال كل فحص في §9.4 دون الاتصال بـ API الخاص بـ qub، ودون أي شبكة وراء التخزين الدائم و drand، ودون أي بحث على جانب الخادم. حلّ التَّوْثيق هو خطوة منفصلة على أفضل جهد تُنفَّذ فقط بعد نجاح التحقق من التوقيع.

9.6 أثر الحجم

Ed25519 ML-DSA-65
التوقيع 64 بايتًا 3,309 بايتات
المفتاح العام 32 بايتًا 1,952 بايتًا
الإجمالي لكل qub 96 بايتًا 5,261 بايتًا
دلتا تكلفة التخزين (بسعر ~$5/MB) ~$0.0005 ~$0.026

لـ qub نصّي بحجم 500–2000 بايت، يُضاعف ML-DSA-65 الحجم المخزَّن تقريبًا ثلاث مرات. التكلفة المطلقة لا تُذكر.

9.7 التحقق من شريك التوقيع (اتفاقيات الميثاق الثنائية)

للاتفاقيات الثنائية (content_type = 0x03)، تُثبت طبقة توقيع ثانية أن كلا الطرفين وافقا على البنود نفسها.

حقول الغِلاف:

يَجِب أن يكون كلا الحقلين موجودين معًا أو غائبين معًا. إذا وُجد حقل واحد فقط، يَجِب على المُشاهِدين الإبلاغ عن خطأ في السلامة.

إجراء التحقق:

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

الخصائص:

بوابة ربط البريد الإلكتروني (تشغيلية). عندما يحمل ميثاق محضَّر جهة اتصال بريد إلكتروني للطرف B (§6.1)، يَجِب على خدمة رفع qub رفض طلب التوقيع المشترك ما لم توجد علامة تحقق بريد إلكتروني قصيرة العمر تطابق كلًا من معرّف التحضير وتجزئة البريد المُطبَّع لتلك الجهة. تُكتب العلامة بواسطة /api/v1/auth/verify عندما يحمل رمز الرابط السحري staging_id ويطابق العنوان المُتحقَّق منه SHA-256(normalise_email(party_b.contact)) — حيث تُحافظ normalise_email(addr) على حالة الجزء المحلي وتُصغّر فقط جزء النطاق (وفقًا لـ RFC 5321 §2.3.11)، وSHA-256 هنا هي تجزئة NIST FIPS 180-4 (متميزة عن SHA3-256 المستخدمة في اشتقاقات §4) — وتنتهي صلاحيتها بعد 900 ثانية (15 دقيقة) من الإصدار. هذه بوابة تشغيلية لمكافحة انتحال الشخصية، وليست جزءًا من إثبات qub على السلسلة — يحتاج المُتحقِّق من طرف ثالث الذي يُعيد تنفيذ §11 فقط إلى التخزين الدائم و drand، دون أي بحث على جانب الخادم. توجد العلامة على جانب الخادم فقط ولا تكون أبدًا جزءًا من الجسم الموقّع.

أثر الحجم (مؤلف ML-DSA-65 + شريك التوقيع):

المكوّن الحجم
توقيع المؤلف 3,309 بايتات
مفتاح المؤلف العام 1,952 بايتًا
توقيع شريك التوقيع 3,309 بايتات
مفتاح شريك التوقيع العام 1,952 بايتًا
إجمالي العبء التشفيري 10,522 بايتًا
دلتا تكلفة التخزين ~$0.05

10. عرض الماركداون والتعقيم

هذا القسم حرج أمنيًا. يعرض المُشاهِد qubs النصية (content_type = 0x01) باستخدام مجموعة فرعية مقيَّدة من الماركداون.

10.1 العناصر المسموح بها

10.2 العناصر المحظورة

العنصر المعالجة
HTML خام (<div>، <script>، إلخ) يُجرَّد كليًا. لا يمرّ أي HTML.
الصور (![alt](url)) تُجرَّد. تُزال صيغة الصورة من المخرج.
الروابط ([text](url)) يُعرَض الـ URL كنصّ عادي مرئي. لا يُربط تلقائيًا. غير قابل للنقر دون إجراء صريح من المستخدم.
مخططات URL الخطرة javascript:، data:، vbscript:، file: — تُجرَّد.
الإطارات الداخلية، التضمينات، الكائنات تُجرَّد.
كيانات HTML تُفكَّك إلى أحرف عرض فقط إن كانت آمنة.

10.3 التنفيذ

يَجِب على التنفيذات استخدام محلّل قائمة سماح صارمة، لا قائمة حظر. النهج المُوصى به:

  1. تحليل الماركداون باستخدام pulldown-cmark (أو ما يكافئه).
  2. السير على الـ AST وإسقاط أي عقدة ليست في قائمة السماح (§10.1).
  3. لعُقد الروابط: إصدار الـ URL كنصّ مرئي، لا كعنصر <a> قابل للنقر.
  4. تحويل الـ AST المُرشَّحة إلى تمثيل وسيط من نوع مُحدَّد (مثل enum MarkdownNode بمتغيرات آمنة فقط). HTML الخام غير قابل للتمثيل بنيويًا في هذا الـ IR.
  5. العرض من الـ IR المُحدَّد النوع إلى طبقة العرض المستهدفة (مثل مكوّنات العرض التفاعلية، عُقد DOM). لا تسلسل سلسلة HTML ولا innerHTML في أي نقطة.

نهج قائمة الحظر هشّ لأن امتدادات الماركداون الجديدة أو غرابات المحلل يمكن أن تُدخل عناصر غير مرشَّحة. نهج الـ AST المُحدَّد النوع يجعل XSS مستحيلًا بنيويًا — لا يوجد متغيّر يمكن أن يحمل HTML تعسفيًا.

10.4 حدود الحجم والبنية


11. التحقق من طرف ثالث

يستطيع أي طرف ثالث يحمل البايتات المخزنة (ويحمل K لـ qub خاص/مغلّف) التحقق من القطعة التشفيرية دون تعاون من qub. ويتطلب ادعاء وجود ذي طابع زمني مستقل أيضًا إما إدراجًا متحققًا منه في التخزين الدائم لكل qub، أو إثباتًا متحققًا منه من سجل الشفافية في §16.

1. Obtain the stored bytes. For a private delivery, also obtain K from the
   delivery-link fragment.
2. Resolve the delivery shape (§8 step 3a): unwrap OuterWrapper with K, or
   accept bare SealedQubCbor. Require shape/visibility agreement.
3. Parse SealedQubCbor → SealedQub; validate protocol version, visibility,
   content type, chain identity, and structural bounds.
4. Recompute expected_round from unlock_at (§4.3); require the stored round
   (allowing the documented legacy minus-one case) and ciphertext-stanza round
   to agree exactly as §8 step 6a specifies.
5. Obtain the drand signature for SealedQub.drand_round and verify its BLS
   signature against the pinned chain public key.
6. tlock_decrypt(tlock_ciphertext, round_signature) → QubEnvelope CBOR bytes.
7. Parse → QubEnvelope.
8. Verify SHA3-256(body) == body_hash.
9. Verify envelope/sealed equality for qub_id, unlock_at, and outcome_at.
10. Recompute qub_id from the decrypted fields, sealed drand_round, and title;
    require it to equal the carried qub_id (§4.1).
11. If sig_alg != 0x00, verify the V2 author signature (§9.4). If cosigner
    fields are present, verify their pairing, key separation, and signature
    (§9.7).
12. For an existence-time claim, independently verify either:
    a. the permanent-storage transaction's data-to-id binding, owner, block
       inclusion, and block timestamp; or
    b. a §16 inclusion proof through its pinned anchor and anchor block time.
13. Report each verified claim separately; do not collapse an absent storage
    or anchor proof into a successful timing verdict.

ما يُثبته التحقق:

مدخل الإثبات ما يُرسّخه
حزمة/قطعة مختومة صالحة + توقيع drand يطابق المتن المستعاد body_hash؛ وتظل البيانات الوصفية المرتبطة بـ qub_id سليمة؛ ويرتبط النص المشفر بجولة drand المعلنة؛ وقد انقضت تلك الجولة. ولا يثبت هذا متى أُنشئ النص المشفر.
توقيع V2 صالح للمؤلف/شريك التوقيع صادق حامل المفتاح السري المقابل (أو حاملَاهما) على السطح الموقع في §9.3.
معاملة تخزين لكل qub متحقق منها مستقلًا كان النص المشفر المخزن نفسه موجودًا في موعد أقصاه طابع كتلتها الزمني.
إثبات صالح من سجل الشفافية المُرسى الادعاء الخاص بنوع الورقة في §16.11، بما فيه وقت التزام أقصى من كتلة المرساة.

ما لا يُثبته التحقق:

غير الإثبات السبب
التأليف sender_label زخرفي. بدون sig_alg ≥ 0x01، كان يمكن لأي شخص ختم هذا المحتوى.
النية تثبت القطعة البايتات والعلاقات التشفيرية، لا ما قصده المُنشئ ذاتيًا.
التزام سابق من .qub وحدها يستطيع المُنشئ تجميع حزمة صالحة بعد انقضاء الجولة المرتبطة. يثبت توقيع drand المضمّن أن الجولة انقضت، لا أن النص المشفر كان موجودًا قبلها.
الوقت الدقيق لضغط زر الختم طابع كتلة التخزين أو المرساة حد أقصى يمكن التحقق منه مستقلًا، وقد يتأخر عن فعل المستخدم المحلي. أما ادعاءات sealed_at / received_at فليست أدلة.

يوسّع سجل الشفافية المنفّذ (§16) التحقق عبر qubs بترتيب كاشف للعبث ووقت التزام أقصى عديم الثقة (وقت كتلة المرساة)، محدد بنوع الورقة (§16.11). ولا يضيف النسب أو النية؛ وفي مسار الرفع الافتراضي الأعمى للبايتات لا يثبت بنفسه body_hash أو drand_round، إذ تظل هذه من فحوص القطعة.


12. الإصدارات والتحكم في الإصدار

إصدارات المستند، وبروتوكول السلك الداخلي، والغلاف الخارجي مساحات إصدارات منفصلة. لذلك لا يغيّر توضيح خاص بالمستند البايتات بصمت، ولا يمكن لهجرة سلك مستقبلية أن تتنكر في صورة مراجعة تحريرية.

12.1 إصدار المستند

تستخدم هذه المواصفة إصدارات مستند دلالية (MAJOR.MINOR.PATCH) ووسم Git ثابتًا باسم protocol-v<release>.

حالة الإصدار واحدة من مسودة (لم يصبح معياريًا بعد)، أو حالي (هدف التنفيذ الوحيد الموصى به)، أو مستبدَل (محفوظ للتحقق التاريخي). يعرض المسار غير ذي الإصدار /protocol الإصدار الحالي؛ ويحفظ وسم الإصدار مصدره الدقيق وكل لغة منشورة معه. ويتطلب تغيير الحالة أو رقم الإصدار تحديث هذا الجدول وسجل الإصدار في التغيير المراجَع نفسه.

إصدار المستند تاريخ السريان الحالة بروتوكول السلك الغلاف المصدر
1.0.0 2026-09-23 حالي 0x01 0x01 protocol-v1.0.0

12.2 إصدار البروتوكول

حقل version (u8) في كل من SealedQub وQubEnvelope يُحدّد الإصدار الرئيسي للبروتوكول.

12.3 سجل إصدارات البروتوكول

الإصدار القيمة الوصف
v1 0x01 تسليم خاص/مغلّف وعام/مجرّد؛ أجسام نص (0x01) وميثاق (0x03) وحكم (0x04)؛ توقيع V2 للمؤلف/شريك التوقيع بـ ML-DSA-65؛ قفل tlock على drand quicknet؛ SHA3-256.

12.4 التوافق الأمامي

مُشاهِد v1 يصادف QubEnvelope بمفاتيح خريطة CBOR غير معروفة (مفاتيح ليست في الترتيب القانوني في §3.2) يَجِب عليه رفضه مع خطأ فكّ ترميز (§3.1). يقوم التوافق الأمامي على حقل version، لا على التسامح مع المفاتيح: الإضافات المستقبلية — حتى البيانات الوصفية الثانوية — تصدر تحت قيمة version جديدة يرفضها مُشاهِد v1 برسالة خطأ واضحة تفيد "بروتوكول أحدث"، بدلًا من أن يُسقِط بصمت محتوى تلتزم به التوقيعات.

مُشاهِد v1 يصادف sig_alg = 0x01 (ML-DSA-65) لكنه يفتقر إلى دعم التحقق من ML-DSA-65 يَنْبَغي أن يعرض محتوى qub بإشعار "التوقيع موجود لكن غير قابل للتحقق"، لا أن يرفض qub كليًا. اليوم يرفض التنفيذ المرجعي كل قيمة sig_alg غير 0x00 و0x01 لأن سجل v1 لا يحتوي على خوارزمية صالحة أخرى — الرفض الصارم والإخفاق الناعم متطابقان رصديًا حتى تُسجَّل خوارزمية ثالثة. يصبح سلوك الإخفاق الناعم أعلاه حاملًا للحمل بمجرد قبول §9.2 إدخالًا جديدًا، وسيُحدَّث المُشاهِد المرجعي إلى الإخفاق الناعم عند تلك النقطة.

12.5 إصدار الغلاف الخارجي

يحمل OuterWrapper الموصوف في §13 بايت version خاصًا به، مستقلًا عن SealedQub.version وQubEnvelope.version. تتطور مساحتا الإصدارين بشكل منفصل: استبدال متماثل آمن ما بعد الكم مستقبلي يرفع بايت الغلاف دون لمس إصدار البروتوكول الداخلي، وإضافة طبقة بروتوكول مستقبلية (مثل حقل غِلاف جديد) ترفع الإصدار الداخلي دون لمس بايت الغلاف.

OUTER_WRAPPER_VERSION_* القيمة الخوارزمية الحالة
OUTER_WRAPPER_VERSION_1 0x01 AES-256-GCM مع nonce بطول 12 بايتًا، وسم مصادقة بطول 16 بايتًا، AAD مرتبط بـ qub_id نشط للتسليم الخاص
— 0x02–0xFF محجوز مستقبلي

يَجِب على المُشاهِدين رفض إصدارات الغلاف غير المعروفة برسالة خطأ واضحة. يُبقي البروتوكول عمدًا مساحة إصدار الغلاف ضيقة حتى يظهر دافع هجرة ملموس (مثل توجيه NIST يفضّل AEAD مختلفًا)؛ ستُخصَّص خانة 0x02 في نفس المراجعة التي تُدخل الخوارزمية.


13. غلاف التشفير الخارجي

13.1 المبرر

تجعل طبقات البروتوكول (QubEnvelope ← tlock ← SealedQub) qub المختوم مُقفلًا زمنيًا: الجسم غير قابل للقراءة حتى unlock_at وبعد نشر توقيع جولة drand. بعد فكّ القفل، مع ذلك، يصبح توقيع الجولة عامًا والشكل القانوني لـ CBOR لـ SealedQub قابلًا للتعرف، لذا يستطيع حاصد فهرس معاملات التخزين الدائم فكّ تشفير مجموعة qubs كاملة بالجملة.

في التسليم الخاص، يُغلق غلاف التشفير الخارجي تلك القناة بإقحام طبقة AEAD متماثلة إضافية بين SealedQubCbor القانوني والبايتات المخزنة. وفي مسار ختم المتصفح، يعيش مفتاح 256-بت K فقط في جزء URL لرابط التسليم وعلى أجهزة المستخدمين؛ ولا تنقل المتصفحات أجزاء URL إلى الخوادم، لذا يكون qub.social وكل بوابة تخزين وكل CDN أمام أي منهما عميًا رصديًا عن K. لذا يكون التمثيل المخزن لـ qub خاص نصًا مشفرًا معتمًا لا يمكن استرداد نصه الواضح دون الـ URL الذي اختار المُنشئ مشاركته. ويحذف التسليم العام هذه الطبقة عمدًا (§13.8).

التأثير الصافي:

13.2 الطبقات

plaintext body                       ← QubEnvelope.body (§2.2)
  ↓ canonical CBOR (§3)
envelope CBOR
  ↓ tlock encrypt to drand round (§7 step 10)
tlock_ciphertext (inside SealedQub) (§2.3)
  ↓ canonical CBOR (§3)
SealedQubCbor bytes                  ← inner wire artifact
  ├─ public (visibility=0x01) ───────────────▶ stored directly (§13.8)
  └─ private (visibility=0x00)
       ↓ AES-256-GCM(K, nonce, AAD=qub_id) (§7 step 12, this section)
     OuterWrapper CBOR bytes         ← stored private payload

الختم وفكّ القفل في طبقة البروتوكول (§7، §8) لم يتغيّرا تحت حدود الغلاف؛ يلتصق الغلاف عند موقع استدعاء seal() وينفصل عند موقع استدعاء unlock().

13.3 هيكل بيانات OuterWrapper

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

ثوابت الحقول.

ترميز CBOR. CBOR قانوني وفقًا لـ §3، بنفس قاعدة ترتيب المفاتيح (مرتبة تصاعديًا حسب طول البايتات المُرمَّزة، ثم معجميًا). المفاتيح الأربعة هي:

المفتاح البايتات المُرمَّزة الترتيب
nonce 6 1
qub_id 7 2
version 8 3
ciphertext 11 4

أول بايت من CBOR لـ OuterWrapper هو إذن رأس الخريطة بطول محدَّد لخريطة من 4 إدخالات (0xA4).

13.4 ربط AAD بـ qub_id

يربط الغلاف qub_id بوصفه بيانات AEAD المصادَق عليها إضافيًا. هذا هو الدفاع البنيوي الحامل للحمل ضد ثلاث فئات من الهجمات:

الهجوم الدفاع
نقل نص مشفّر تحت حقل qub_id مختلف في الغلاف عدم تطابق AAD ← تفشل مصادقة AEAD
خلط جزء URL لـ qub A مع البايتات المخزنة لـ qub B مفتاح خاطئ (ومعه AAD مرتبط بصورة مستقلة) ← تفشل مصادقة AEAD
العبث بحقل qub_id في الغلاف بعد الرفع عدم تطابق AAD ← تفشل مصادقة AEAD

حمل qub_id في النص العادي للغلاف لا يُضعف حصانة التعداد بشكل ذي معنى — qub_id نفسه تجزئة SHA3-256 لصورة §4.1 الأولية دون صورة أولية قابلة للاستعادة من الموجز، والمُعدِّد الذي حصد بالفعل بايتات الغلاف لا يتعلّم شيئًا من qub_id المرئي لم يكن ليستنتجه من وجود الرفع نفسه.

13.5 خوارزميات التغليف وفكّ التغليف

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

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

انهيار وضع الإخفاق. K خاطئ، nonce خاطئ، عدم تطابق AAD، ونص مشفّر معبوث به تُنتج جميعًا نفس الخطأ DECRYPT_FAILED. هذه خاصية AEAD مقصودة: التمييز بين وضع الإخفاق سيُنشئ قناة جانبية يستطيع مهاجم بعيد سبرها بإرسال أغلفة مشوَّهة وحساب زمن الاستجابة. يَجِب على التنفيذات المرجعية طيّ كل إخفاقات AEAD إلى شكل خطأ واحد.

13.6 مادة المفتاح والتوزيع

مفتاح التغليف K هو قيمة عشوائية موحَّدة بطول 256 بتًا تُولَّد لكل qub بواسطة CSPRNG. تستمدّه التنفيذات المرجعية من:

التوزيع: يَجِب ترميز K كـ base64 آمن للـ URL (RFC 4648 §5، بلا حشو) وإلحاقه برابط التَّسْليم كمكوّن الجزء:

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

لا يُنقل الجزء أبدًا إلى أي خادم بواسطة متصفح مطابق. قنوات الاستعادة (فهرس السجل على جانب الخادم، الإرسال التلقائي الاختياري بالبريد الإلكتروني) التي تستمر بحفظ كامل رابط التَّسْليم — بما في ذلك الجزء — وراء جهاز المستخدم هي مقايضة صريحة ضد الوضعية الافتراضية لتمزيق التشفير ويَجِب أن تكون مشروطة بموافقة المستخدم الصريحة.

فقدان الجزء. إذا فقد المستخدم جزء URL وليست لديه قناة استعادة، يكون qub غير قابل للقراءة. هذه هي المقايضة الحاملة للحمل في التصميم ويَجِب الإفصاح عنها للمستخدم وقت الختم. يُعزّز الـ MVP الإفصاح وقت الختم بنصّ صريح "احفظ هذا الرابط" وقناة استعادة ببريد إلكتروني مُتحقَّق منه للمستخدمين الذين يختارون التفعيل.

13.7 خارج نطاق هذا القسم

13.8 qubs العامة (حذف الغلاف)

الغلاف الخارجي اختياري في طبقة التَّسْليم. يجوز للمُنْشِئ ختم qub بوصفه عامًا، وفي هذه الحالة يدخل SealedQubCbor القانوني مسار التخزين مباشرةً، دون طبقة OuterWrapper ودون مفتاح K:

SealedQubCbor bytes  ──(public)──▶  stored as-is
SealedQubCbor bytes  ──(private)─▶  AES-256-GCM(K, …) ▶ OuterWrapper ▶ stored

qub العام مُقفل زمنيًا لكنه غير محكوم بالرابط: يبقى غير قابل للقراءة حتى تنشر جولة drand الخاصة به (طبقة tlock لم تتغير)، لكن بعد فكّ القفل يستطيع أي شخص يملك معرّف معاملة التخزين فكّ تشفيره — لا يلزم جزء URL، إذ لا وجود لـ K. هذه هي المقايضة المقصودة للأسطح التي يَجِب أن يقودها الخادم: رسائل إشعار الكشف بالبريد الإلكتروني، وروابط oEmbed/التضمين التلقائي الخالية من الأجزاء، وتحسين محركات البحث الأغنى بعد الكشف — كلها تحتاج إلى رابط يعمل دون سرّ لا يحوزه الخادم أبدًا (§13.6). ولا يزال بوسع qub خاص استخدام الصيغة الصريحة <qub-embed src="full_delivery_url"> عندما يوفّر الناشر قدرته الكاملة الحاملة للجزء.

عواقب يَجِب على المُنتِج أخذها في الحسبان:

الخاص (المُغلَّف) يبقى الافتراضي؛ والعام خيار مُنْشِئ صريح لكل qub.


14. متّجهات الاختبار

14.1 اشتقاق 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  = 4695446  (= floor((1736294400 - 1595431050) / 30) + 1, §4.3 mapping, drand mainnet params §14.2)
  body         = "Hello, future."  (UTF-8, 14 bytes)
  title        = absent

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

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

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

Expected output:
  qub_id = SHA3-256(preimage)
         = 4a84e3dfaec32954949c30073f8e6506
           fd3204c1bb97f9162b81c7587afe412e

يَجِب على التنفيذات إنتاج قيم body_hash وqub_id متطابقة لهذا المدخل. يَنْبَغي أن يكون متّجه الاختبار هذا أول اختبار وحدة يُكتب. حُسبت القيم القانونية أعلاه بواسطة التنفيذ المرجعي ويَجِب أن تتطابق بتًا ببتٍ. استخدمت تخطيطات نموذجية تاريخية سابقة للإطلاق (لم تعتمد qubs حية على أول اثنين) 92 بايتًا قبل outcome_at (3d9fc2390eab043d38a1669ed3b71be76f9eefe872b9569ab1aaa027b88392b0) و100 بايت بعد إضافة outcome_at_or_zero (b0d032898ad629795150fdcb3f84e518f59ed05b7a2a82bc24ebdb87f52144ed). ثم أضاف التخطيط الحالي ذو 108 بايتات drand_round وفاصل النطاق QUB_ID_V2. واستخدم متجه مبكر ذو 108 بايتات ربط الجولة القديم ceil (drand_round = 4695445) فأنتج 3a9fcb31b750d985c262fada6d4f777fd6a28be831d941d85c131f5a4bbaf8a4—وهو qub_id صالح لذلك المدخل، بينما يتبع المثال أعلاه ربط الجولة الحالي في §4.3.

14.2 ربط جولة فكّ القفل

Input:
  unlock_at           = 1735689600
  chain_genesis_time  = 1595431050
  chain_period_seconds = 30

Calculation:
  (1735689600 - 1595431050) / 30 = 4675285.0
  floor(4675285.0) + 1 = 4675286

drand_round = 4675286

تُنشر الجولة 4675286 عند 1595431050 + (4675286 - 1) * 30 = 1735689600—بالضبط عند unlock_at، لا قبله أبدًا. (أعطى ربط ceil القديم السابق للإصدار الجولة 4675285، المنشورة عند 1735689570—مبكرًا بـ 30 ثانية؛ ويقبل المتحققون تلك الجولة القديمة وفق §4.3.)

14.3 جولة CBOR قانونية ذهابًا وإيابًا

يَجِب على التنفيذات التحقق من أن serialize(parse(serialize(qub))) == serialize(qub) لكل المدخلات الصالحة. هذا اختبار خصائص، لا متّجه واحد.

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)

تُحسب بايتات CBOR القانونية وbody_hash لـ SHA3-256 بواسطة التنفيذ المرجعي. يَجِب على التنفيذات إنتاج CBOR متطابق بايتيًا لهذا المدخل.

كما يَجِب على التنفيذات التحقق من أن serialize(parse(serialize(pact))) == serialize(pact) لكل مدخلات PactTerms الصالحة (اختبار خصائص).

14.5 متّجهات الغلاف الخارجي عبر اللغات

يحتوي الغلاف الخارجي (§13) على تثبيت قانوني منفصل في crates/qub-core/tests/vectors/wrapper_v1.json. تثبت كل حالة رباعية (key, nonce, qub_id, sealed_cbor) كمدخلات ست عشرية غامضة وتؤكد مخرج expected_wrapper_hex محدَّدًا. يستهلك كلا التنفيذين المرجعيين ملف JSON نفسه:

يثبّت التثبيت حاليًا ثلاث حالات غلاف منخفضة المستوى. تختبر هذه الحالات ترميز OuterWrapper الحتمي وقابلية تشغيل AEAD بين التطبيقات بمعزل عن ثابت شكل التسليم في §13.8؛ وعلى وجه الخصوص، لا يجعل الاسم التاريخي basic-text-public ولا قيمة visibility = 0x01 الداخلية البايتات المغلَّفة تسليمًا عامًا مطابقًا. يجب على المُنتِج مع ذلك تخزين البايتات الداخلية العامة عارية، وألا يغلّف إلا البايتات الخاصة (0x00).

الحالة التغطية
basic-text-public اسم تاريخي لمُثبّت منخفض المستوى. أصغر شكل واقعي لـ SealedQub، بلا حقول اختيارية؛ يختبر بايتات الغلاف فقط وليس تسليمًا مخزّنًا مطابقًا لـ §13.8.
with-recipient-pubkey SealedQub مع تعيين recipient_pubkey (مسار مستقبلي محجوز). يختبر مجموعة مختلفة من مفاتيح CBOR الداخلية؛ وينتج محتوى المُثبّت المختلف بذاته qub_id مختلفًا (ولا يدخل recipient_pubkey نفسه في الصورة السابقة في §4.1).
longer-body جسم بحجم ~4 KiB — يُمارس بادئات طول CBOR متعددة البايتات داخل كل من الغِلاف الداخلي والنص المشفّر الخارجي.

يَجِب على التنفيذات إنتاج expected_wrapper_hex متطابق بايتيًا للمدخلات المُسجَّلة. تتطلب إعادة توليد التثبيت QUB_REGEN_VECTORS=1 cargo test -p qub-core --test wrapper_vectors ومحجوزة لتغييرات صيغة متعمَّدة.


15. حوكمة ملف التشفير (مستقبلي)

هذا القسم إعلامي لـ v1 ويصبح معياريًا أول مرة تدخل فيها خوارزمية ثانية إلى أي من العناصر التشفيرية الأولية لـ qub.

15.1 الوضعية الحالية

يربط بروتوكول v1 بالضبط خوارزمية واحدة لكل عنصر أولي:

يُرسّخ المُتحقِّقون حاليًا أطوال المفتاح والتوقيع لكل بدائية نشطة. وبايتا sig_alg وإصدار الغلاف محدِّدان صريحان، لكن v1 لا يجري تفاوضًا ضمن النطاق ولا يقبل إلا القيم النشطة أعلاه.

15.2 الشكل المنشود

عندما تدخل خوارزمية ثانية إلى البروتوكول، سيُهيَّأ المُتحقِّق لـ CryptoProfile مُسمّى (مثل ExqubV1) يسرد المجموعة الدقيقة للقيم المسموح بها لكل عنصر أولي — sig_algs، سلاسل drand، إصدارات الغلاف، أنواع المحتوى. يُثبَّت الملف وقت التحقق، ولا يُتفاوَض عليه ضمن النطاق. أي قيمة خارج الملف النشط تُرفض.

يضمن هذا أن إضافة ML-DSA-87 أو تفعيل Ed25519 لا يمكن أن يُضعفا تكوينات المُتحقِّقين القائمة بأثر رجعي: مُتحقِّق v1 يبقى مُتحقِّق v1 حتى بعد نشر ملف v2.

15.3 شروط التفعيل

ارفع §15 إلى حالة معيارية عندما يُقترح أي مما يلي:

حتى ذلك الحين، §15 هو حافظ مكان يُثبّت شكل الهجرة كي تنزل PRs المستقبلية على هدف معروف بدلًا من إعادة التقاضي على سطح التفاوض من الصفر.


16. سجل الشفافية ومستويات المتانة (تم تنفيذه — اكتملت المراجعة)

الحالة. هذا القسم تم تنفيذه (W5/UP-B1، المراحل 1–8)، مع تحديد نطاق المنتج ونطاق جذر الثقة هنا. تنسيقات الأسلاك، التجزئة، ومسارات التحقق نشطة: أنواع Merkle + CBOR الكانونية الأساسية (qub-core)، مرآة TypeScript + حزمة ANS-104 (workers/api/src/crypto/)، LogDO كاتب واحد + مخزن عقدة R2 بمفتاح الإحداثيات، محاولة إضافة /upload log-appt، كرونات المذكور اليومي + تجميع البنجر، نقاط النهاية GET /api/v1/qub/:tx_id/proof (الإدمن) و GET /api/v1/log/consistency (RFC 9162)، إثبات التضمين المطبوع المحمول في حزمة .qub (§17.5)، وجهاز التحقق الأصلي من المرساة ANS-104 (tools/qub-verify)، وخطاف الرأسين المنشورين ذاتيا (§16.6). /upload الناجح دائما ما يكون مدوما في R2 لكنه يغطي السجل فقط عندما يتم تكوين LOG_DO وينجح الإضافة الداخلية؛ عندها فقط يحمل رده log_seq receipt و anchor_status. إذا كان RECEIPT_SK غائبا أو غير صحيح، فإن sig_b64url ذلك الإيصال يصبح فارغا ولا يوفر أي رفض للنفي. مسارات نشر /seal واتفاقية الحالية تجدول معاملات Arweave الفردية لكنها لا تضيف ورقة سجل. لا يوجد كود حاليا يقوم بتسوية لاحقة مقترحة لتعليق /upload بعد فشل الإضافة. اكتملت مراجعة W5 الخارجية: §16.15 تسجل قرارات التصميم وقيود الإطلاق، لكن هذه القيود لا توسع تغطية المنتج المذكورة للتو. ثلاثة عناصر للثقة/النشر تبقى مغلقة: (أ) محفظة المرساة المخصصة (ANCHOR_JWK؛ LogProfile.anchor_owner لا تزال [0xAB; 32] المؤقتة); (ب) مفتاح توقيع الإيصال ورمز المفتاح العام المطابق (RECEIPT_SK اختياري و LogProfile.receipt_pubkey فارغ حاليا); و(ج) مستودع GitHub المنشور ذاتيا + الرمز (§16.6). حتى يتم توفير دبابيس المثبت/الملف الشخصي، يقوم المتحقق المستقل بالإبلاغ عن حالة الإثبات بصدق بدلا من الادعاء بالتحقق المثبت والمثبت بالكامل. التصميم مضاف تماما ولا يوجد تغيير في صيغة SealedQub / QubEnvelope الأسلاك.

16.1 المنطق ومستويات المتانة

تفصل مسارات النشر الحالية التأكيد عن تأكيد Arweave: فهي تستتق وتوقع معاملة فردية، وتستمر في حالة الإرسال الدقيقة والحالة الدقيقة في R2، ثم تنشر بشكل غير متزامن. يضيف سجل الشفافية طبقة ترتيب مستقلة لمجموعة طلبات /upload العامة التي ينجح LogDO إضافتها:

المستوى الاسم الضمان متى
T1 R2-إقرار متزامن أول حد التحمل — يتم كتابة البايتات المختومة وحالة النشر الدقيقة إلى التخزين الدائم قبل إرجاع النجاح. تم التطبيق عبر مسارات النشر الحالية.
T2 إدراج سجل الشفافية بشكل مجمّع التزام للكتابة فقط يظهر أي تلاعب + ترتيب كامل بمجرد إدراجه والتثبيت. المنتج الحالي: الإضافات الناجحة من LogDO من /upload؛ الاستجابة تحمل زوج الإيصال. ليس عالميًا.
T3 ديمومة كل qub على Arweave معاملة Arweave فردية للـ qub. حاليًا مُعدة لكل نشر مقبول ويتم نشرها بشكل غير متزامن؛ المعاملة الموقعة الدقيقة تبقى في صندوق الصادر القابل للتصريف حتى يتم التسليم.

تصف المستويات خصائص الأدلة والديمومة المميزة، وليس الخطة التجارية الحالية. لا يزال الكود الحالي يبرمج معاملة Arweave فردية لكل نشر مقبول؛ لا يعرض T3 فقط كخيار مدفوع. تظل حدود الحصة لمفتاح API/الحساب منضبطة بواسطة التطبيق بشكل منفصل.

أمان الديمومة. كتابة T1 متزامنة، لذا فإن الاستجابة الناجحة تؤسس الديمومة على مستوى التطبيق دون انتظار بوابة Arweave. لم تحدد هي نفسها طابعًا زمنيًا مستقلًا. توفر المعاملة الفردية المؤكدة حدود الوقت العليا للكتلة. بالنسبة لاستجابة تحمل زوج الإيصال الكامل لـ T2، يمكن للمرساة المؤكدة التالية أن توفر دليل السجل الموصوف أدناه. إذا لم يكن الزوج موجودًا، فلا يمكن لأي سطح أن يوحي بأن هذا qub موجود بالفعل في سجل الشفافية. لا يوجد اتفاق مستوى الخدمة الرقمي على بروتوكول المؤشر والنشر.

16.2 هيكل LogLeaf (شكلان مثبتان)

سجل الدخول هو LogLeaf، مشفر بصيغة CBOR القانونية المكتوبة يدويًا وفقًا لملف §3.1 (طول محدد، بدون وسوم، بدون أرقام عشرية، أعداد صحيحة بأقصر صيغة، نصوص بتكويد NFC، حقول اختيارية تُحذف عند غيابها، المفاتيح مرتبة حسب طول البايت المشفر تصاعديًا ثم حسب البايت). يتم تطبيق الحماية القانونية parse → re-encode → compare وفق §3.1 على مسار التشفير قبل التجزئة (وليس فقط عند فك التشفير)، لذلك لا يمكن لتطبيقين مختلفين أن يختلفا في بايتات الأوراق بسبب فرق عرض الأعداد أو ترتيب المفاتيح. جميع الأعداد صحيحة u8 / u64 / i64؛ جميع الملخصات (digests) هي سلاسل بايت 32-بايت (bstr[32]). معرف معاملة Arweave المخزن هو ملخص SHA-256 خام مكون من 32 بايت، يُحمل بصيغة bstr[32]، وليس أبدًا سلسلة نصية base64url (يتطابق مع §3.3).

تأخذ الورقة شكلين يتم اختيارهما بواسطة بايت kind، لأن مسار التحميل العام لا يميز نوع البايت: POST /api/v1/upload يتعامل بشكل متعمد مع كلتا صيغتي الحمولة المقبولة على أنهما غامضتان ويتلقى qub_id وunlock_at فقط كـ ادعاءات عميل غير موثوقة. في المسار الخاص الافتراضي، body_hash وdrand_round وcreated_at وdrand_chain_version تكون مخفية أيضًا داخل الغلاف الخارجي §13، الذي لا يحتفظ العامل بمفتاحه. كما يعرف نظام النوع شكلًا مثبتًا للمنتج الذي يستنبط body_hash / drand_round بنفسه. مسار /seal الحالي يحتوي على تلك القيم ولكنه لا يستدعي LogDO، لذلك الإنتاج حاليًا يصدر فقط أوراق مثبتة (0x02) من الإضافات العامة الناجحة. الانقسام يحافظ على صدق كل قيمة ملتزمة دون الادعاء بأن المنتج المثبت متصل بشكل مباشر:

المفتاح العنصر نوع الحضور يعني
seq 4 u64 يتطلب مؤشر ورقة عالمي قائم على 0؛ الموقع الذي يلتزم به إثبات الشمول.
kind 5 u8 تتطلب 0x01 الشهادة (معرفة، غير صادرة حاليا) أو 0x02 التأكيد (ختم العميل / رفع أعمى البايتات).
ref 4 bstr[32] المرجع الورقي. موثق → qub_id الخام. تم تأكيده → المعرف الأعمى SHA3-256(qub_id ‖ log_blind_secret) (§16.2.1).
chash 6 bstr[32] المطلوبة SHA3-256(stored_bytes) المحتوى — وهو الرابط الوحيد للمحتوى الذي يمكن للعامل دائما حسابه بصدق، على كلا المسارين.
unlock_at 10 i64 يتطلب نسخ (توثيق) أو تأكيد (تأكيد)؛ التحقق من صحة > 0 قبل دخوله الورقة.
received_at 12 i64 يتطلب ساعة الحائط للعامل عند R2-ack. غير دليلية (مؤكد من المشغل؛ §16.6). حاضر لوصف الذات، لا يوجد إثبات أبدا. تم التحقق > 0.
body_hash 10 bstr[32] kind=0x01 حذفها فقط في 0x02 — حيث يفتقر العامل إليها بموجب المادة 13.
drand_round 12 u64 kind=0x01 حذف فقط في 0x02.

ورقة kind=0x02 لا تلتزم عمدا ب body_hash ولا drand_round: فهي تشهد الالتزام وترتيب النص المشفر المعتم عند chash عنوان المحتوى، مدعية qub_id و unlock_at — وليس نصه الأصلي أو الدائري. تأتي أرجل النص العادي/الدائري للكيووب المؤكد من التحقق الموجود في حزمة .qub §11، وليس من السجل (§16.11). drand_chain_version ليست في الورقة (هي داخل الغلاف في المسار الافتراضي)؛ تعيش دقة السلسلة على المرساة (§16.7). انضباط المشفر: رفض ref أو chash صفري بالكامل، ورفض unlock_at / received_at غير الإيجابية، مما يعكس حارس الحارس outcome_at > 0 في cbor.rs.

16.2.1 تعمية قوب خاصة

يجب ألا يصبح السجل هو Oracle العد الذي تهدف الغلاف الخارجي §13 لمنعه (§13.1). بالنسبة لقوب خاص (مغلف) فإن ورقة asserted تُلزم المُعرف المعمى SHA3-256(qub_id ‖ log_blind_secret)، حيث أن log_blind_secret هو سر محتفظ به من قبل الخادم، وتتجاهل body_hash. لا يمكن لطرف ثالث ربط مثل هذه الورقة بـ qub_id محدد؛ حامل القوب، الذي لديه عنوان التوصيل وبالتالي qub_id، يمكنه إعادة حساب التعمية لتأكيد إدراجه الخاص. القوب العام (القابل للعد بالفعل، ويحمل بالفعل وسم Visibility: public الخاص بـ Arweave وفق §13.8) يلزم الـ qub_id الخام. هذا هو المكان الوحيد الذي تتنازل فيه إمكانية التحقق المستقل عن ثباتية الخصوصية الحاملة للحمل؛ الارتباط المستقل للقوب الخاصة هو chash (§16.9).

log_blind_secret الحفظ (تم الحل — §16.15 Q4). التعمية تحمي عدم قابلية ربط الورقة، وليس سرية النص الصريح (فالـ §13 الغلاف يحتفظ بذلك بشكل مستقل). عند حدوث اختراق log_blind_secret، بالنسبة لأي qub_id يمتلكه الخصم بالفعل أو يمكنه إعادة بنائه (كل قوب يمتلك حزمه/عنوانه، بالإضافة إلى أي qub_id منخفض التعقيد أو عام) فإنه يعيد حساب الورقة ref في هاش واحد ويربطها — هذا هو الربط المباشر للسكان المعروفين، وليس قوة قسرية عبر مساحة غير معروفة. صنف log_blind_secret كسري من فئة ارتباط/Sybil ضمن نفس طبقة الحفظ مثل باقي أسرار الخادم، وقلِّبها للأمام فقط (تدوير يعيد تعمية الأوراق المستقبلية؛ لا يمكنه فك ارتباط الأوراق المثبتة بالفعل).

16.3 تجزئة الورقة والعقدة

RFC 6962 §2.1 التجزئة المنفصلة بالنطاق مع SHA-256 المستبدل بـ 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

بايتات بادئة النطاق 0x02 (سلسلة الإدخال، §16.4) و 0x03 (هاش STH، §16.6) محفوظة ومنفصلة عن هذه. إنها بايت واحد ولا يمكن أن تتصادم مع فواصل النطاق ASCII الحالية ذات 10 بايت (QUB_ID_V2، إلخ). الشجرة هي شجرة RFC 6962 غير متوازنة يسارًا بالكامل (كل انقسام داخلي عند أكبر قوة للعدد اثنين أصغر بصرامة من عدد أوراق الشجرة الفرعية)، مما يسمح بمشاركة أدوات إثبات الإدراج والاتساق خوارزمية مسار التدقيق واحدة. يحمل المعيار المرجعي رمزًا مزيفًا صريحًا للاشتقاق الأيسر/الأيمن ويثبت متجه اختبار غير قوة 2 (5 أوراق) حتى يتم تجربة حالة ترقية الحافة اليمنى — التي يخفيها متجه 4 أوراق.

16.4 سلاسل الهاش (داخلية)

يحافظ LogDO على سلسلة إدخال داخلية لمصداقية الانتكاس فقط. لا يتم نشرها أبدًا ولا مواجهة من قبل المتحققين:

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

السلطة المنشورة للقراءة فقط المضافة هي جذر ميركل التراكمي + مُرساها (§16.5–16.6)، وليست ترتيب الإدراج الخام الذي يحدث أن يقوم المشغل بخدمة الأوراق الخاصة به: السلسلة تعيد الحساب لأي ترتيب يتم خدمته، لذا فقط الجذر المثبت يحدد الموضع القانوني.

16.5 شجرة ميركل التراكمية والتجميع

هناك شجرة RFC 6962 واحدة متزايدة باستمرار عبر جميع الأوراق بترتيب seq — وليست أشجار منفصلة لكل دفعة. (تم رفض إنشاء ورقة متسلسلة لكل دفعة: فهي ليست علاقة بادئة حقيقية، لذا فإن "إثباتات التناسق" الخاصة بها غير صحيحة.) توفر الشجرة التراكمية إثباتات تناسق RFC 9162 حقيقية وتتيح لمُرسٍ حديث واحد إثبات الإدراج لأي وحدة قديمة.

كائن LogDO الدائم هو الكاتب الوحيد (blockConcurrencyWhile، يعكس QuotaDO / EntitlementDO) — الإضافة إلى سجل مشترك هي قراءة-تعديل-كتابة على الحالة المشتركة وبالتالي يجب أن تتم عبر DO وليس KV. يقوم بتخزين حدود حافة الشجرة اليمنى (O(log n) تجزئات) بحيث يصبح إغلاق دفعة O(batch). الدفعة هي مجموعة الأوراق المثبتة معًا؛ مشغلاتها المطبقة هي تقدم tree_size بمقدار لا يقل عن LOG_BATCH_MAX_LEAVES (الإعداد الافتراضي 4096)، أو وصول العمر إلى تتابع المرسى، أو إغلاق صريح إداري/مجدول. root_i هو تجزئة شجرة ميركل التراكمية على الأوراق 0 .. tree_size_i.

16.6 رأس الشجرة الموقع عبر مرساة Arweave

معاملة مرساة Arweave هي رأس الشجرة الموقع وتستبدل توقيع المشغل لرأس الشجرة نفسه: المرساة اليومية لا تحتاج مفتاح وحدة لأن معاملة Arweave owner هي التوقيع. تنطبق فرضية الخندق — الركيزة غير القابلة للتغيير، وليست سر مملوك لوحدة، هي الحاملة للجذر المثبت.

تصميم السجل يتطلب مفتاح استلام مضاف ناجح دافئ واحد (§16.10)، مثبت في LogProfile وموقع متقاطع بواسطة anchor_owner. التنفيذ الحالي لم يكمل توفير جذر الثقة هذا: RECEIPT_SK اختياري، والمفتاح الغائب/غير الصالح يؤدي إلى sig_b64url: ""، والـ LogProfile.receipt_pubkey المجمعة فارغة. يمكن للمستند المرفق أن يصف الورقة المضافة لكنه ليس استلامًا موقعًا لا يمكن إنكاره. ينطبق الادعاء التصميمي الأقوى فقط بعد أن يقوم الإصدار المحقق بتثبيت مفتاح عام مطابق ويوقع مالك المرساة عليه بشكل متقاطع. استجابة النشر بدون مجموعة الاستلام الكاملة لا تصدر أي ادعاء بقبول السجل؛ بينما استجابة بتوقيع فارغ تصدر ادعاءً بموقع الإضافة ولكن لا تصدر ادعاءً بالتحقق من التوقيع.

SignedTreeHead هو CBOR القانوني (المفاتيح حسب الطول المشفر): size:u64، root:bstr[32]، batch:u64، prev:bstr[32] (السابق sth_hash؛ الأصل = 32 بايت صفر)، log_id:bstr[32]، first_seq:u64، anchored_at:i64. تجزئته هي sth_hash = SHA3-256(0x03 || canonical_cbor(SignedTreeHead)).

جذر الثقة مثبت. log_id = SHA3-256("QUB_TLOG_V1" || anchor_owner_address). يجب أن يتطلب المتحقق المطابق anchor_tx.owner == LogProfile.anchor_owner، حيث يتم دمج anchor_owner (ومفتاح مفتاح الإيصال) في qub_core ك LogProfile — إلى جانب ثوابت الشبكة السريعة الموجودة بالفعل في DrandTimelockProvider::quicknet() — وتوزيعه مع الملف الثنائي للمتحقق (COMPRIVER). يجب على المتحقق أيضا التحقق من → tx_id ربط البيانات في Arweave tx محليا** بدلا من الوثوق ببوابة /raw/ الرد. هذا يغلق فجوة التشويش في المحفظة المارقة: "المثبت على Arweave" لا معنى له حتى يقوم المتحقق بتثبيت أي محفظة.

التدوير هو حوكمة §15 امتداد، وليس إعادة استخدام (تم حل — §16.15 Q3). سطح ملف §15.2 حاليا يعدد فقط sig_algs / سلاسل drand / إصدارات التغليف / أنواع المحتوى، ومحفزات §15.3 لا تذكر أيا من هذه — LogProfile / anchor_owner ليس بعد في سطح §15. لذا يجب بناء: §15.3 ممتد (أدناه) لإضافة LogProfile الزناد، والدوران هو انتفاخ LogProfile موقع تم شحنه في تحديث للتحقق. التدوير المخطط* يحمل توقيعا متقاطع → واردا صادرا؛ لا يمكن لتدوير **المفتاح الخارج غير موثوق/غير متوفر بالضبط حينها) ويعود إلى الحافة المحكوم عليها §15، مع فحص شوكة المرساة السابقة (أدناه) لتحديد الضرر في الوقت الانتقالي.

نافذة التداخل (معلمة الثقة من الدرجة الأولى). تكون الورقة مقاومة للتشويش فقط عندما يكون مرساة تغطيتها Arweave-مؤكد. النافذة هي received_at → anchor confirmation (الوتيرة + نهائية Arweave، بدون ضمان تأخير البروتوكول). قبل توفير جذر الثقة، يوفر التنفيذ الحالي سلامة تشغيل QUB بالإضافة إلى أي بيانات وصفية غير موقعة موجودة؛ لا يوفر ضمان عدم الرفض المخطط له. ثلاث عيوب مساءلة تحدد التصميم المكتمل (نموذج الشاهد هو §16.15 دقة Q2):

  1. إيصال ختم (يعتمد على التجهيز) — يتم إرجاع نظير SCT عندما ينجح ملحق سجل التحميل (§16.10). يصبح غير قابل للرفض فقط عندما يكون sig_b64url غير فارغ و يتم تثبيت علاقة المفتاح العام/المالك المتطابقة في المدقق. لا يمكن لرمز ملف الإنتاج الفارغ حاليا دعم هذا الحكم. هذا التحكم لا ينطبق على مجموعة إيصالات محذوفة أو إيصال غير موقع.
  2. منهجية المراقبة المنشورة + سير السلسلة السابقة — سلسلة prev المرساة هي رأس مشي→نشأة؛ التفرع (مرساتين في size واحد مع root مختلف، أو prev مكسورة) هو دليل قابل للنشر على سوء السلوك. اكتشاف التشويش هو التزام تشغيلي معلن، وليس افتراضا صامتا.
  3. رؤوس منشورة ذاتيا — كل رأس جديد {sth_hash, tree_size} ينشر في مستودع GitHub عام مخصص مملوك للكوب (الجانب الذاتي المنتشر الظاهر بالعبث الواضح)، مع منشور اجتماعي كتأكيد لأفضل جهد فقط. صفحة النشر الفاشلة يجب أن تكون صامتة (ليست صامتة). تم تنفيذها (المرحلة 8) كخطاف publishHead على كرون الرابط (workers/api/src/utils/heads-publish.ts): PUT لواجهة برمجة المحتوى بدون sha تكون ملحقة فقط (422 تعني أن الرأس منشور بالفعل، وليس استبدال); الاشتراك / الانتشار على PUBLISH_HEAD_GITHUB_{TOKEN,OWNER,REPO} وغير فعال حتى يتم توفير المستودع. صفحات فشل صعبة في GitHub عبر قناة health_alert وترفع مقياس فشل دائم (m:tlog_publish_head_fail); مرساة Arweave نفسها لا تتراجع عن فشل النشر. "عدم فشل الصامت" مضمونة من خلال هذا المقياس الدائم — الذي يجب على المشغلين تنبيه لوحة القيادة عليه — حتى لو لم يتم تسليم صفحة البريد الإلكتروني بأفضل جهد. هناك قيدان صادقان يتبعان من "الرابط عند التقدم" (المكرر ينشر فقط عندما يزداد الحجم): فشل مؤقت في GitHub يترك فجوة في تسلسل الرؤوس المنشورة لهذا الحجم — محدودة، وليست صامتة (تظهر صفحات)، ولأن كل رأس يلتزم بشجرة فائقة، فإن إثبات الاتساق §16.9 يجسر الفجوة؛ والأهم من ذلك، أن إثبات الاتساق هذا يتم حسابه من شجرة الاعتماد المرتبطة ب Arweave، وليس من سطح GitHub*، لذا فإن فجوة GitHub لا تضعف أبدا قابلية التحقق. ملء التعويض الذي يملأ فجوات الرؤوس المنشورة هو تعزيز مؤجل.*

قيد الأمانة (قيد ملزم). لأن QUB يتحكم في سطحي النشر المخططين لها، فهذا منشور ذاتيا، وليس شاهدا مستقلا. لا يجوز لأي منتج أو تسويق أو سطح قانوني أن يدعي أن السجل "مشاهد بشكل مستقل". بعد توفير الإيصال/الملف الشخصي/بوابات الرأس، الادعاء المسموح به هو أن التشويش قابل للكشف وأن الملحق الموقع بنجاح يترك إيصالا غير قابل للنفي. قبل ذلك، هذا الادعاء غير متاح. يتم تأجيل شاهد مستقل حقيقي من طرف ثالث إلى زيادة حوكمة مستقبلية بموجب §15.

received_at يتم تأكيده من قبل المشغل ولا يمكن لأي مطالبة أن يعتمد عليه — لا يظهر أبدا كدليل أو كتأكيد نزاع على أي منتج / قانوني / واجهة برمجة تطبيقات / عرض إثبات. T زمن كتلة التثبيت في Arweave هو الطابع الزمني الوحيد الذي لا يعتمد عليه الثقة (وهو حد أعلى ل "تم تسجيله بواسطة"). أي فحص سلامة مراقبة على received_at يجب أن يقارن مع T، وليس مع حقل STH anchored_at الذي يتحكم به المشغل؛ مثل هذا الفحص هو حماية فقط ضد خطأ الساعة الخاص بالمشغل النزيه، ليس تحكم محاسبة ضد مشغل خبيث (§16.15 Q5).

16.7 صيغة وإيقاع معاملات المحور

AnchorBundle هو جسم معاملات Arweave الرسمي ل CBOR، مكتوب عبر المجمع §16.8: ver:u8، sth:bstr (بايت SignedTreeHead القانوني)، prev_anchor:bstr (بايتات التعريف الخام لتعريف المرساة السابقة؛ تم حذفها عند التكوينيس)، chain_hash:tstr (سلسلة drand في الغالب — الشبكة السريعة)، وتدفق الأوراق وCBOR الخاص بالدفعة بترتيب seq بحيث يكون المرساة مكتفيا ذاتيا: يعيد المشتعل اشتقاق root من الجسم بدون اعتماد على الكوب. (إذا أصبح تدفق الأوراق كبيرا عند حجم عالي، قد يلتزم التعديل المستقبلي بنطاق أوراق فقط بالمرجع؛ وقد تم تدوينه، ولم يعتمد في الإصدار 1.)

وسوم Arweave قابلة للتعداد عمدا — السجل مقصود أن يتم العثور عليه، على عكس الكيوبس الخاصة: App-Name: qub-tlog، Anchor-Format: 1، Log-Id: <hex>، Batch: <n>، Tree-Size: <n>، Root: <hex>، Prev-Anchor: <tx>، Content-Type: application/cbor. الوسوم هي تلميحات غير موثوقة؛ هيئة CBOR هي السلطة الوحيدة.

Cadence: يوميا بشكل افتراضي، يعاد النظر فيه مع الحجم (تفعيل الحجم يقلل تلقائيا من الإيقاع الفعال تحت الحمل). المنتج الحالي لا يطبق خطاف القوة المدفوع بالختم. محفظة المرساة مخصصة ومنخفضة السرعة، منفصلة عن محفظة الرفع — يجب أن تكون JWK خاصة بها (مفتاح مميز، وليس دورا منطقيا في محفظة الرفع) لذا لا يمكن لاختراق محفظة الرفع تشكيل مراسي — مع ميزانية معاملات مرساة صارمة يوميا. وضع الحفظ موضح بوضوح: مفتاح اختصار ضيق النطاق مع قاطع دائرة ضيق وتوازن منخفض، وليس "باردا" — محفظة توقع تلقائيا يوميا لا يمكن أن تكون باردة، والمواصفات لا تدعي خلاف ذلك.

16.8 ANS-104 Bundler

مشفر ANS-104 DataItem داخلي وتوقيع تجزئة عميقة، حوالي 300 خط، تشفير ويب فقط، صفر تبعيات npm (كلا حكومتا Turbo SDK يفشلان في npm ci --ignore-scripts بوابة سلسلة التوريد). تخطيط بايت 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

التوقيع هو Arweave deepHash — ملخص متكرر SHA-384 (متطلب الأسلاك في Arweave، crypto.subtle.digest("SHA-384")) عبر ["dataitem", "1", sig_type, owner, target, anchor, encoded_tags, data] — ثم RSA-PSS عبر التجزئة العميقة مع المحفظة JWK عبر crypto.subtle؛ id = base64url(SHA-256(signature)). SHA-384 هنا محزن كبدائي فقط عبر أسلاك Arweave، وليس بدائي لثقة الكيوب (§15 يسجل السياج؛ وتجزئة صندوق الكوب هو SHA3-256 طوال الوقت).

يخدم مسار كود ANS-104 آلية الاحتياط/التصريف المؤجلة ويكتب AnchorBundle DataItems. ينشئ مسار النشر العادي أولا معاملة Arweave موقعة بدقة ويحافظ على JSON الخاص به في صندوق خروج متين؛ النشر المباشر هو تحسين للزمن، ويعيد مسار التصريف محاولة نفس المعاملة قبل تطبيق احتياطي الحزمة الخاص به. مخطط التوقيع (تم الحل — §16.15 Q8): علامات v1 مع RSA-PSS (نوع التوقيع 1) مع إعادة استخدام آلية JWK الحالية لمحفظة Arweave (صفر حفظ مفتاح جديد طويل العمر، مع تقديم أطروحة "سر واحد أقل"); تم تأجيل Ed25519 إلى مسار ترحيل PQ §15.

الرمز العميق اليدوي هو أعلى رمز مخاطرة وأقل تغطية طبيعية في W5، لذا فإن حدوده غير قابلة للتفاوض (§16.15 Q8):

  1. يغطي عنصر التثبيت متعدد اللغات tlog_v1.json (Rust + TS، النمط §14.5 wrapper_v1.json) التجزئة العميقة، بايتات DataItem + المعرف، تجزئات الأوراق، جذر مكون من 5 أوراق + مسار التدقيق، تجزئة STH، دليل الشمول، ودليل التناسق — في اتجاهي التوقيع والتحقق (اتجاه التحقق مهم لأن فحص tx → tx_id المحلي في §16.6 يسحب التجزئة العميقة إلى كل متحقق مستقل، وليس الكاتب فقط).
  2. جولة تفاعل مرة واحدة من خلال جامع مرجعي ANS-104، يتم استخدامها كـ بيانات اختبار ثابتة فقط — وليست كمكتبة npm وقت التشغيل أبدًا (الموقف الخاص بـ Web-Crypto فقط / بدون سكريبتات تثبيت ثابت).
  3. يجب أن تمر مسار التجزئة العميقة + RSA-PSS عبر نفس الأساسيات crypto.subtle المستخدمة في الإنتاج، بحيث يكون الترميز الداخلي متوافقًا على مستوى البايت.
  4. يضمن مراقب قبول ما بعد الحزمة الجاري أن كل DataItem للمرساة / النسخ الاحتياطي يحقق قبول Arweave فعليًا، مع إنذار + قاطع دائرة — لأن التجزئة العميقة تخدم أيضًا قائمة انتظار النسخ الاحتياطي عند عدم توفر Arweave، لذا فإن أي تراجع صامت سيملأ تلك القائمة بعناصر مرفوضة من الشبكة أثناء انقطاع الشبكة الذي يُفترض أن يغطيه.

16.9 أدلة الشمول والتناسق

كلاهما RFC 9162، SHA3-256، يتم تقديمهما بصيغة CBOR القانونية.

InclusionProof — GET /api/v1/qub/:tx_id/proof: ver:u8، leaf:bstr (CBOR الورقة الدقيقة — يعيد المتحقق حساب leaf_hash بنفسه ولا يثق بالتجزئة المزودة)، index:u64، size:u64، audit:[bstr[32]]، root:bstr[32]، anchor:{ txid:bstr[32], batch:u64, sth:bstr[32], log_id:bstr[32], block_height?:u64, anchored_at?:i64 }.

ConsistencyProof — GET /api/v1/log/consistency?first=<size_a>&second=<size_b>: ver:u8، first_size:u64، second_size:u64، first_root:bstr[32]، second_root:bstr[32]، nodes:[bstr[32]]، first_anchor، second_anchor. قائمة مفاتيح واحدة واضحة، مثبتة بواسطة متجه الاختبار.

التحقق المستقل (بدون خادم qub، يوسع §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).

يجب أن يكون التخزين الذي يخدم الإثبات مفوزا بمفتاح الإحداثيات (تم حله — §16.15 Q7، مع حظر الشرط المسبق). توليد إثبات الورقة الباردة محايد للصحة فقط إذا مادة تدقيق R2 هي مخزن عقد ميركل دائم يتم تسويته بواسطة إحداثيات الشجرة المطلقة (level, index)** — وليس دلتا لكل batch عقدة. مع المخزن المحدد بمفتاح الإحداثيات، أي مسار تدقيق (leaf i, size N) هو مجموعة من O(log N) R2 مباشرة GET مع بدون إعادة حساب عبر حدود الدفعات; أما مع المخزن المدفوع بالدفعات فلا يكون كذلك، وهو فجوة ترتيب التخزين التي يغلقها هذا الدقة. أجسام الأوراق قابلة أيضا لعناوين المحتوى بواسطة seq. يجب على ناقل اختبار W5 إثبات وجود ورقة باردة من حقبة النشأة ضد جذر لاحق بكثير باستخدام R2 + Arweave فقط مع مسح تخزين LogDO، لذا فإن ادعاء السلامة في الاستصلاح في §16.13 مدعوم بدلا من تأكيده. تنتمي الناقلات التسلسلية O(log N) ل R2 GET إلى فقط على نقطة النهاية اللامتزامنة — لا على مسار الختم الساخن (§16.10) أو كرون لكل نكتة.

16.10 R2-ترتيب الهجوم الأول

تسلسل POST /api/v1/upload المنفذ هو:

  1. بوابات النصف الأمامي (التصديق، التحقق، مفتاح شظية الهواء) — دون تغيير.
  2. إنشاء ووضع علامة وتوقيع على معاملة Arweave الفردية بالضبط. يتم اشتقاق tx_id محليا، رغم أن إنشاء المعاملة قد يجلب بيانات المكافأة/المرساة من البوابة. فشل التحضير لا يزال يفشل في الطلب قبل التأكيد.
  3. اكتب العنصر المختار بشكل متزامن عند qub-cache/<tx_id> وحافظ على سجلات عملية الإنشاء/صندوق الخروج المستقرة. هذه هي مستوى المتانة وإعادة المحاولة؛ الفشل قبل عودة التسوية 503.
  4. عند تكوين LOG_DO، محاولة متزامنة LogDO.append(leaf). يقوم الكاتب الوحيد بتعيين seq، ويمدد سلسلة الإدخال، ويحدث الحدود. يقوم RPC الملحق بذلك فقط؛ يتم إغلاق الدفعة خارج المسار على الإنذار. فشل النقل/التطبيق في الإلحاق هو حاليا fail-soft: يمكن للاستجابة النجاح بدون log_seq أو receipt أو anchor_status. على الرغم من تعليق التنفيذ، لا يتم توصيل السجل التلقائي لاحقا اليوم.
  5. إعادة التأكيد. تضمين { log_seq, anchor_status: "pending", receipt } فقط عندما يعيد الملحق المجموع الناجح بالكامل. receipt.sig_b64url فارغ عندما يكون موقع الإيصال غير متاح؛ يجب على العملاء ألا يعتبروا تلك القيمة موقعة أو غير قابلة للنفض. غياب التوبل يعني النشر الدائم فقط، وليس قبول سجل الشفافية.
  6. استخدم مهمة مؤجلة واحدة لنشر المعاملة الموقعة بالضبط. النجاح يزيل صندوق الخروج؛ يترك الفشل الصندوق الصادر للمصرف المحدود ولا يجب تغيير tx_id المعترف به مسبقا. البيانات الوصفية المؤقتة وغيرها من الكارات الجانبية التي تتطلب أفضل جهد تؤجل أيضا.

حد الكمون. يشمل مسار الطلب عمل السلطة/الحصة في النصف الأمامي، وإعداد/توقيع المعاملة، وكتابات R2 الدائمة، و(عند الإعداد) محاولة LogDO. يظهر < 300 ms في مراجعة التصميم كهدف عملي، وليس كضمان بروتوكولي؛ قد يقوم خطوة إعداد المعاملة الحالية بتنفيذ طلب بيانات الجهة الوسيطة. إن تنبيهات الكمون وبوابات الإطلاق هي ضوابط تشغيلية، وليست دليلاً متاحاً للمدقق.

16.11 نموذج الثقة — المطالبة الدقيقة، محدودة بنوع الورقة

بالنسبة إلى kind=0x01 (موثق): "تم الالتزام بهذا المحتوى — الجسم مطابق لـ body_hash، محدد بواسطة qub_id — في سجل qub الذي يُضاف فقط عند الموضع seq وكان موجودًا في أقصى حد عند وقت كتلة Arweave T؛ وكان غير قابل للقراءة بطريقة تشفيرية حتى جولة drand R = unlock_round(unlock_at)." هذا هو الثلاثي الكامل {tlock round binding + Merkle inclusion + anchored root}.

بالنسبة إلى kind=0x02 (مؤكد، الافتراضي): "تم الالتزام بشيفرة مغلقة بمحتوى chash، تدعي qub_id و unlock_at، في سجل الإضافة فقط عند الموضع seq وكان موجودًا في أقصى حد عند وقت كتلة Arweave T." يتم توفير أجزاء الجولة والجسم بواسطة التحقق الحالي لحزمة §11 .qub (qub_core::unlock)، وليس بواسطة السجل؛ وما يضيفه السجل فوق المعاملة الفردية لكل qub هو الترتيب المظهر للتلاعب، والالتزام الأعلى بدون ثقة، ومقاومة التناقض.

كلا المطالبتين تستبعدان، بحسب §11: التأليف بدون sig_alg ≥ 0x01، النية، وتوقيت دقة ما دون المرساة. لا يسمح أي منهما لأي مطالبة بالاعتماد على received_at.

سقف المطالبة (قيد الإطلاق الملزم — محلول §16.15 سؤال 1). بالنسبة إلى ورقة مؤكدة (kind=0x02)، المطالبة المحدودة أعلاه هي السقف لما يمكن لأي منتج أو تسويق أو شروط أو واجهة عرض الأدلة التأكيدية أن يدعيه. لا يجوز لأي واجهة أن تقول أو توحي بأن السجل يثبت المحتوى أو جولة إلغاء القفل لتحميل أعمي للبايت — السجل يثبت الترتيب + الالتزام الأعلى بدون ثقة لشيفرة مغلقة. إثبات المحتوى والجولة يأتي حصراً من التحقق الحالي لحزمة §11 .qub، المستقل عن السجل. المنشور الذي لم ينجح في الإضافة/الإيصال ليس له أي مطالبة سجلية على الإطلاق.

16.12 النسخ والتنسيق مع W3

لا يوجد نتوء سلكي SealedQub وبالتالي لا يوجد انتفاخ في إصدار البروتوكول (§12.2): السجل هو رمز جانبي يلتزم بحقول وبايت موجودة، لذا لا يدخل تاريخ إصدار البروتوكول §12.3. drand_chain_version الاختياري ل W3 لم يلمس ويبقى الحقل الاختياري الوحيد SealedQub. بدلا من ذلك، يقدم السجل فضاءات إصدارات مستقلة خاصة به — LOG_VERSION_1، ANCHOR_FORMAT_1، InclusionProof.ver — تعكس استقلالية إصدار §12.5 (يحمل الغلاف بايت إصدار مستقل عن نسخة البروتوكول، وتتبع نسخ السجل نفس الفصل).

يتم جلب الدليل افتراضيا، مع إمكانية الركوب الاختياري. لا يمكن أن يوجد إثبات في وقت الختم (لم تكتب المرساة بعد)، لذا تبقى حزمة .qub وقت الختم خالية من الإثبات. يجلب التحقق في W7 GET …/proof مرة واحدة، أو في وضع عدم الاتصال الكامل يعيد بناء الإثبات من AnchorBundle العامة عبر استعلام Arweave على Log-Id. حزمة .qub (W7) تحتفظ بعضو inclusion_proof اختياري** — غائب عند الختم، ويتم تعيينه إعادة تصدير بعد التثبيت للأرشفة الباردة — وتتبع نفس نمط "اختياري، محذوف افتراضيا، إضافي" كما في drand_chain_version W3.

16.13 الاحتفاظ

نوافذ الاحتفاظ للذيل المفتوح ل LogDO، وركيزة R2 التي تقدم الإثبات، وعدادات قاطع الدوائر المرساة، وقائمة الانتظار الاحتياطي للحزم محددة في docs/DATA-RETENTION.md. المبدأ: التخزين الساخن لكل دخول (LogDO) للسجل قابل للاسترداد بعد التثبيت؛ مادة التدقيق الخاصة به — مخزن عقدة ميركل (level, index) بمفتاح الإحداثي + أجسام الأوراق المعنونة seq (§16.9) + مراسي Arweave — دائمة. استعادة ورقة باردة من DO لا تبطل البرهان الصادر، لأن البرهان يحل ضد مخزن عقدة R2 الدائم ومرساة Arweave، وليس ضد DO (ومتجه اختبار DO §16.9 يثبت ذلك).

16.14 متجهات الاختبار

يشحن W5 tlog_v1.json التثبيت عبر اللغات (§16.8) بالإضافة إلى المتجهات المحلولة: → leaf_hash kind=0x01 وورقة kind=0x02؛ جذر الخمس أوراق؛ إثبات تضمين واحد؛ إثبات اتساق واحد؛ AnchorBundle واحد؛ ومعرف DataItem واحد. هذه الوحدات تعيش جنبا إلى جنب مع متجهات الغلاف الخارجي §14.5 وتمارس بواسطة كل من تنفيذ Rust (qub-core) وTypeScript (Worker).

16.15 قرارات مراجعة (W5 — تم حله)

اكتملت مراجعة W5 الخارجية (تصريح تصميم خصمي + توقيع المالك). يتم تسوية كل قرار أدناه وينعكس في نص §16 أعلاه؛ يتم إعادة صياغة قيود الإطلاق الملزمة في النهاية. يمكن المضي قدما في التنفيذ بخلالها.

  1. صدق الورقة في المسار الافتراضي (kind=0x02) — تم الحل. أرسل تقسيم النوع ذو الورقات كما هو محدد: kind=0x02 لا يلتزم ب body_hash ولا drand_round. لا يوجد حقل *_body_hash على مسار الأعمى للبايت (سيكون أكثر إشارة "مؤكدة" خاطئة وواضحا للمتكاملين وهو راحة توفرها المادة 11 بالفعل من الحزمة). لا يتطلب لا ختم الخادم للكواربات المعتمدة من السجل (الذي سيدفع النص العادي عبر العامل ويدمر خندق تمزيق التشفير). أي دائرة قصيرة تصف نفسها تنتمي إلى حزمة .qub / ظرف الإثبات كحقل معاد حسابه من قبل المحقق، وليس حقل ورقة. سقف المطالبة المؤكد من المالك: §16.11.

  2. التشويش / المساءلة عن الإغفال — تم حل التصميم، التوفير غير مكتمل. يتطلب التصميم تثبيت مفتاح الختم والاستلام في LogProfile وتوقيعه من قبل anchor_owner، بالإضافة إلى منهجية المراقبة، والمشي السابق، ورؤوس مزدوجة منشورة ذاتيا. الملف الشخصي المجمع وخطافات النشر لا تزال مجرد بدائل/اختيارية كما هو موضح في §16.6، لذا فإن الادعاء الأقوى قابل للاكتشاف + المستلم لا يكون ساريا حتى تغلق تلك البوابات. يجب ألا يتم تسويقه أبدا كشاهد مستقل. يتم تأجيل الشاهد الحقيقي من طرف ثالث إلى زيادة الحوكمة بموجب §15.

  3. جذر الثقة المثبت بين المالك والمرساة + الدوران — تم الحل. اعتماد دبوس LogProfile (§16.6); يقوم المتحقق بفحص anchor_tx.owner == anchor_owner ويتحقق من ربط بيانات الإرسال → tx_id محليا. حوكمة التدوير هي §15 امتداد للبناء (تمت إضافة محفز §15.3)، وليست إعادة استخدام؛ الدورانات المخطط لها تعود إلى نقطة الربط §15 مع فحص الشوكة لتلف الحدود.

  4. تعمية ورقة الكويب الخاص — تم الحل. استمر في تعميم الكوب الخاص (ref = SHA3-256(qub_id ‖ log_blind_secret))، qub_id خام للكيوبس العامة (بالفعل §16.2.1)، chash كرابط مستقل. log_blind_secret هو ارتباط سري/درجة سيبيل، يدور للأمام فقط (§16.2.1).

  5. received_at — تم الحل. الاحتفاظ بها في الورقة، مرتبة ولكن غير دليل صريح؛ لم تظهر أبدا كدليل أو تأكيد للنزاع على أي سطح. أي فحص سلامة مراقبة يقارن ب T زمن كتلة Arweave، وليس مع anchored_at التي يتحكم بها المشغل (§16.6).

  6. توقيت إثبات متدرج — دقة التصميم، وليس التوجيه الحالي. يعين التصميم الراجع توقيت كتلة المرساة للطبقة المجمعة وإثبات الساعة الدقيقة إلى T3 المدفوع، دون وجود رقم قياسي للسابق. المسارات الحالية لم تحدد هذا التمييز التجاري: فهي تحدد معاملة فردية لكل منشور مقبول، وتبقى تغطية السجل مشروطة كما هو مذكور في §16.1/§16.10. يجب أن يصف نسخة المنتج التنفيذ، وليس هذا التقسيم المستقبلي للطبقة.

  7. الشجرة التراكمية على العمال — تم الحل. شجرة RFC 9162 التراكمية الواحدة + سجل LogDO للكاتب الواحد مع تخزين الجبهة المؤقت (مساحة مريحة مقابل سقف ~1k عمليات الكتابة/ثانية في DO؛ تأجيل تقسيم Merkle لجذور الشظايا حتى الاقتراب منه). يتم تنفيذ مخزن عقدة (level, index) R2 المفهرس بالمفتاح التنسيقي + متجه اختبار الورقة الباردة DO الممسوحة (§16.9). يظل < 300 ms هدفاً للتصميم/التشغيل، وليس وعداً بالبروتوكول (§16.10).

  8. مخطط التوقيع ANS-104 + التجزئة العميقة — تم الحل. RSA-PSS (نوع التوقيع 1، باستخدام JWK المخصصة لمحفظة المرساة); تم تأجيل Ed25519 إلى مسار §15 PQ. التجزئة العميقة SHA-384 المصنوعة يدوياً مشروطة بفحص التوافق بين الاتجاهين، وفحص التشغيل المرجعي لتجميع الثوابت فقط، وجولة العودة المشتركة لـ crypto.subtle، ورصد قبول Arweave بعد التجميع (§16.8).

قيود الإطلاق الملزمة (يتم نقلها إلى التنفيذ + مراجعة المنتج/القانونية):


17. حزمة التحقق المحمولة (.qub)

الحالة. هذا القسم منفَّذ (W7 / UP-C2): تنتج qub_core::export الحزمة وتحللها، وtools/qub-verify أداة CLI عامة مستقلة تتحقق منها دون اتصال. تشير §11 و§16.9 بالفعل إلى «حزمة .qub» بوصفها الوحدة التي يستهلكها المتحقق المستقل؛ ويحدد هذا القسم بايتاتها ومسار التحقق. وهي إضافية بحتة—تجمع الحزمة مدخلات §11 القائمة ولا تغيّر أي صيغة سلكية على السلسلة.

17.1 الغرض

تقرر §11 أن أي طرف ثالث يستطيع التحقق من القطعة التشفيرية لـ qub دون تعاون من qub. وتجعل حزمة .qub ذلك التحقق محمولًا وغير متصل: فهي تجمع CBOR المختوم وتوقيع جولة drand الذي يفتح قفله في قطعة واحدة مستقلة، بحيث يستطيع المتلقي التحقق من نزاهة المحتوى وربط الجولة وأي توقيعات نسب من دون أي اتصال شبكي (لا جلب من التخزين، ولا طلب حي إلى drand، ولا API لـ qub). ولا تثبت الحزمة وحدها متى أُنشئ نصها المشفر؛ إذ توفر معاملة تخزين متحققًا منها مستقلًا أو إثباتًا مُرسًى في السجل ادعاء وقت الوجود المنفصل (§11، §17.5).

17.2 صيغة الحزمة

QubBundle هو CBOR قانوني مكتوب يدويًا وفق ملف §3.1 (أطوال محددة، بلا وسوم، بلا أعداد عائمة، أعداد صحيحة بأقصر صورة، نص NFC، حذف الحقول الاختيارية عند غيابها، وترتيب المفاتيح تصاعديًا بطول البايت المرمز ثم بايتيًا). وتُرتَّب المفاتيح الثلاثة ذات 15 حرفًا d < i < s. ملف .qub الخام هو هذه البايتات بالضبط؛ وللنقل عبر URL أو النسخ واللصق تكون البايتات نفسها base64url بلا حشو.

المفتاح الطول نوع CBOR الحضور المعنى
version 8 u8 مطلوب إصدار صيغة الحزمة (0x01).
sealed_at 10 i64 اختياري وقت الختم الذي يدعيه المُنشئ (ثواني Unix)؛ وصفي ذاتيًا وغير استدلالي.
drand_round 12 u64 مطلوب الجولة التي يُقفل إليها qub. إسقاط من qub المختوم المضمّن.
arweave_tx_id 14 tstr مطلوب معرّف المعاملة الذي خُزنت تحته البايتات المختومة (مؤشر منشأ).
drand_chain_id 15 tstr مطلوب سلسلة drand (hex). إسقاط من qub المختوم المضمّن.
drand_signature 16 bstr مطلوب توقيع منارة drand لـ drand_round—القيمة التي تفتح النص المشفر.
inclusion_proof 16 bstr اختياري إثبات إدراج Merkle من سجل الشفافية في §16.
sealed_qub_cbor 16 bstr مطلوب بايتات SealedQubCbor الداخلية (بعد فك غلاف §13)، أي مدخل التحقق في §11.

drand_round وdrand_chain_id إسقاطان للملاءمة من sealed_qub_cbor، محمولان كي تقرأهما الأدوات من دون تحليل CBOR الداخلي. ويُشتقان عند البناء ثم يُعاد فحصهما عند فك الترميز مقابل qub المختوم المحلل؛ وتُرفض حزمة يختلف حقلها العلوي عن حمولتها. يماثل انضباط المرمز بقية الصيغة السلكية: ارفض drand_signature أو arweave_tx_id فارغًا، وضع حدًا لكل حقل متغير الطول.

17.3 ما يثبته توقيع drand المضمّن

تحمل الحزمة توقيع drand بدل مطالبة المتحقق بجلبه. ولا ينجح فك تشفير القفل الزمني (tlock فوق سلسلة drand، §8) إلا بتوقيع المنارة الحقيقي للجولة المرتبطة—قيمة لا تنشرها السلسلة إلا عند انقضاء تلك الجولة، وتكون توقيع BLS صالحًا تحت المفتاح العام للسلسلة. ويفشل التوقيع المزور أو الخاطئ في التحقق بـ BLS أو في فك تشفير IBE/AEAD. لذلك تثبت الحزمة التي تنجح في فك التشفير أن النص المشفر مرتبط بالجولة R، وأن الجولة R قد انقضت. يثبّت المتحقق السلسلة (DrandTimelockProvider::quicknet()) ويطبق فحص ربط الجولة في §11، فلا يمكن للحزمة ادعاء جولة لا يرتبط بها نصها المشفر.

هذا إثبات لشرط الإصدار، لا طابعًا زمنيًا للإنشاء. فبعد انقضاء الجولة R، يستطيع أي شخص إنشاء نص مشفر جديد للجولة R وتغليف توقيعها الذي صار عامًا. لذلك يَجِب ألّا توصف الحزمة وحدها بأنها تثبت أن النص المشفر أو المحتوى كان موجودًا قبل R، أو قبل unlock_at، أو قبل أي حدث.

17.4 مسار التحقق دون اتصال

يشغّل qub-verify <file.qub> إجراء §11 القياسي بالكامل من الحزمة، ويقود qub_core::unlock::unlock بواسطة DrandTimelockProvider مثبّت:

1. Parse the .qub bytes → QubBundle (canonical-CBOR guard; bound every field;
   re-check drand_round / drand_chain_id against the embedded sealed qub).
2. BLS-verify bundle.drand_signature for the pinned chain and round, then
   tlock_decrypt(sealed.tlock_ciphertext, bundle.drand_signature) → QubEnvelope.
3. Verify SHA3-256(body) == body_hash               (§11 step 8).
4. Verify QubEnvelope.qub_id   == SealedQub.qub_id   (§11 step 9).
5. Verify QubEnvelope.unlock_at == SealedQub.unlock_at (§11 step 10).
6. Verify ciphertext round == unlock_round(unlock_at) and the chain binding.
7. If sig_alg != 0x00: verify author_signature (and any cosigner; §9.4).
8. Report integrity, round-elapsed/round-binding, authorship, and cosigner
   verdicts separately, plus the recovered body. Do not report a commitment
   timestamp unless step 9 succeeds.
9. Optional existence-time leg: verify an included §16 proof through its pinned
   anchor, or independently verify the referenced storage transaction. Report
   its block time as an upper bound on ciphertext existence.

تخرج أداة CLI بالقيمة 0 (تم التحقق)، أو 1 (فشل التحقق—ما زال مقفلًا، أو عدم تطابق تجزئة المتن، أو كسر في ربط الجولة/السلسلة، أو توقيع يفشل التحقق)، أو 2 (الاستخدام / حزمة مشوهة). ويحمل تقرير --json الأحكام نفسها للأتمتة. ولأن الحزمة مستقلة، فإن صندوق المتحقق (qub-core) وأداة CLI (qub-verify) هما البرنامجان الوحيدان اللذان يحتاج إليهما طرف ثالث؛ وكلاهما عام ويعيد استخدام مسار التحقق القائم للبروتوكول—بلا تشفير خاص مبتكر.

17.5 العلاقة بسجل الشفافية

inclusion_proof خانة اختيارية لإثبات إدراج Merkle في §16. يكتمل تحقق الحزمة وحدها (§17.4) من النزاهة، وربط الجولة / انقضاء الجولة، والنسب الاختياري، لكنه لا يحمل عمدًا ادعاء وجود ذي طابع زمني مستقل. ويضيف inclusion_proof المعبّأ والمتحقق بالكامل عبر المرساة الالتزام الخاص بنوع الورقة والوقت الأقصى من §16.11، دون تغيير إصدار صيغة الحزمة. ويعني غياب الإثبات فقط «لا يوجد إثبات مضمّن»—لا «غير صالح» ولا بالضرورة «غير مُرسى».

في التنفيذ المرجعي أصبحت الخانة الآن محددة النوع: تعيد qub_core::export::QubBundle::inclusion_proof_typed() قيمة Option<InclusionProof> تحمل بنية §16.9 كاملة (الورقة، ومسار التدقيق، والجذر المُرسى، وAnchorRef) عبر حقل CBOR المعتم نفسه—بلا رفع لإصدار صيغة الحزمة. وتستهلكها أداة qub-verify المستقلة عبر مسار --anchor الخاص بها، وحتى تُجهز محفظة المرساة (§16، الحالة)، تُبلغ عن إثبات ذي مالك عنصر نائب على أنه إدراج فقط بدل متحقق منه عبر المرساة بالكامل.