מפרט הפרוטוקול של 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 הוא טקסט גלוי / markdown / בתי pact אטומים, ורינדור מודע-לוקאל הוא באחריות הצופה (אפליקציית הרשת qub.social, iframe של <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 Concise Binary Object Representation (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 (גוף pact, ראו §6.1); sig_alg = 0x01 (ML-DSA-65) עם author_signature ו-author_pubkey נוכחים (ראו §9.3); cosigner_pubkey ו-cosigner_signature נוכחים יחד עבור pacts חתומים-במשותף (ראו §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 ב-2 בתים; 256–65535 ב-3 בתים; וכו'
קידוד אורך אורכים מוגדרים בלבד. אין מערכים, מפות, מחרוזות בתים או מחרוזות טקסט באורך בלתי-מוגדר (additional info = 31 אסור).
תגיות אין תגיות CBOR (טיפוס ראשי 6 אסור).
נקודה צפה אין floats (טיפוס ראשי 7 ערכים 0xF9–0xFB אסורים).
מחרוזות טקסט מקודדות ב-UTF-8, מנורמלות ב-NFC (Unicode Normalization Form C).
מחרוזות בתים בתים גולמיים. אין קידוד base64 בשכבת ה-CBOR.
מפתחות כפולים דחו עם שגיאה. מנתחים חייבים שלא לקבל בשקט מפתחות מפה כפולים.
מפתחות לא ידועים דחו עם שגיאה. מנתחים חייבים שלא לסבול מפתחות מפה מחוץ לקבוצת המפתחות הקנונית של הטיפוס — אסור ששתי מחרוזות בתים קנוניות שונות יפוענחו אי-פעם לאותו ערך (encode(decode(x)) == x), ובמטענים חתומים מפתח נוסף היה מהווה תוכן נסתר ששתי החתימות מתחייבות אליו. התפתחות סכמה עוברת דרך version, לעולם לא דרך מפתחות נוספים.
ערכים פשוטים רק true (0xF5), false (0xF4) ו-null (0xF6) מותרים.
שדות אופציונליים שדות אופציונליים נעדרים מושמטים ממפת ה-CBOR לחלוטין (לא מקודדים כ-null). שדות אופציונליים נוכחים נכללים בסדר מפתחות ממוין.

3.2 סדרי מפתחות קנוניים מאומתים

סדרי מפתחות אלה הם נורמטיביים. מימושים חייבים לפלוט מפתחות בדיוק בסדר זה. אסרציות ניפוי באגים מומלץ שיאמתו את הסדר בבנייות שאינן release.

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 (גוף pact, 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 bytes 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 bytes author_signature, cosigner_signature
מפתח ציבורי ML-DSA-65 (1,952 בתים) 0x59 0x07 0xA0 + 1,952 bytes 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 בתים ליישור. מימושים חייבים להשתמש בדיוק ב-10 בתים אלה: [0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00].

קידוד outcome_at: תיקון מימוש טרום-מהדורה הרחיב את הטרום-תמונה מ-92 ל-100 בתים כדי לקפל את השדה האופציונלי outcome_at לתוך הקשירה. outcome_at נעדר מקודד כ-8 בתי אפס; מאמתי הפרוטוקול דוחים outcome_at <= 0 בכל מקום כך שערך-סנטינל זה אינו יכול להתנגש עם ערך לגיטימי. ראו §3.2 (פורמט קווי) ואת tasks/verdict-uplift-plan.md שבעץ למנגנון הפסיקה שמניע שדה זה.

קידוד drand_round: תיקון מימוש טרום-מהדורה מאוחר יותר הרחיב את הטרום-תמונה מ-100 ל-108 בתים כדי לקפל את drand_round (סבב ה-drand היעד, §4.3) לתוך הקשירה, והעלה את מפריד התחום ל-QUB_ID_V2. זה קושר את סבב ה-timelock לתוך זהות ה-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), אי אפשר לגזור מחדש חפצים שנחתמו במיפוי הישן; מאמתים המבצעים את בדיקת-הנגד של הסבב בשלב 6א של §8 חייבים לקבל drand_round מאוחסן השווה או לסבב הנגזר או לסבב הנגזר פחות אחד (וחייבים לדרוש שסבב משבצת tlock יהיה שווה בדיוק לסבב המאוחסן). הסבילות מרחיבה את חתימת השחרור המוקדמת ביותר בתקופה אחת לכל היותר. שירות העמדת הפקט מחיל אותה סבילות כשהוא גוזר מחדש את qub_id של פקט מועמד (בהעמדה ובחתימה משותפת): אם המיפוי הנוכחי אינו משחזר את qub_id המחויב וההפרש מתחלק בתקופה, הוא מנסה שוב עם הסבב פחות אחד וחותם את הפקט המוגמר לסבב שאליו qub_id אכן קשור—לעולם לא באופן עיוור לסבב המחושב מחדש, דבר שהיה הופך את החפץ לבלתי-גזיר לצמיתות.

אימות: unlock_at חייב להיות בעתיד בזמן האטימה. unlock_at חייב שלא להיות יותר מ-10 שנים מ-created_at (כדי להגביל סיכון תלות-drand ארוכת-טווח; הממשק מומלץ שיזהיר עבור תאריכי שחרור מעבר ל-2 שנים).


5. Newtypes של פורמט קווי

Newtypes של פורמט קווי מספקים בטיחות בזמן הידור נגד בלבול של בתי 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, Markdown מוגבל) 50 KB בתשלום / 10 KB חינם ראו §10 לכללי רינדור. החלוקה חינם / בתשלום נאכפת על ידי שירות ההעלאה; תקרת-הקשיחות ברמת הפרוטוקול היא 50 KB.
0x02 שמור (עתידי) — מוקצה עבור טיפוס תוכן עתידי; לא תקף ב-v1. צופים חייבים לדחות אותו לפי הכלל שלהלן.
0x03 Pact (הסכם דו-צדדי, גוף CBOR) 100 KB הגוף הוא PactTerms בקידוד CBOR קנוני (§6.1). חתימת מאשר-משותף לפי §9.7.
0x04 פסק דין (ציון עצמי של היוצר, גוף CBOR) 8 KB הגוף הוא VerdictBody בקידוד CBOR קנוני (§6.2). נפלט אך ורק על ידי ה-intent המערכתי verdict. הקשר ל-qub ההורה נמצא בתג Arweave Parent-Tx-Id, ולא בגוף. ראו verdict-uplift-plan §3.4.

צופים חייבים לדחות טיפוסי תוכן לא ידועים עם שגיאה ברורה הנראית למשתמש. צופים חייבים שלא לנסות לרנדר טיפוסים לא ידועים כטקסט.

6.1 גוף Pact (content_type = 0x03)

גוף pact הוא הקידוד הקנוני ב-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 המסורייל של ה-pact חייב שלא לעבור 100 KB (תואם ל-§6).

מבחין סכמה. השורה הראשונה ב-terms עבור pact מסוג structured/v1 חייבת להיות { key: "pact_schema", value: "structured/v1" }. שורות ללא סמן זה הן pacts "מותאמים אישית" ואינן מקבלות אימות מובנה או רינדור מודע-סכמה.

משבצות אישור מוקפאות. pacts מסוג 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 על golden-fixture המכסות את כל ארבעת צירופי התפקידים תופסות כל סטייה.

סדר תצוגה בצופה. מחרוזות האישור מכילות ביטויים כגון "described above", המניחים ששורות התיאור / ההיקף מרונדרות לפני האישורים. צופים חייבים לרנדר את מערך ה-terms בסדר CBOR; שינוי סדר שובר את סמנטיקת הפרוזה.

איש קשר של הצד שכנגד. כאשר ה-contact של צד B הוא כתובת דוא"ל תקפה, שירות העלאת ה-qub שולח אוטומטית דוא"ל הזמנה לסקירה / חתימה-משותפת בזמן ההכנה וקושר את החתימה-המשותפת הסופית לאימות אותה כתובת (§9.7). pacts שאיש הקשר של צד 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 (תואם לשורה במרשם לעיל).

Enum של תוצאה. הבית על ה-wire הוא נייטרלי-intent; ארבע הקטגוריות Right / Partial / Wrong / Unfalsifiable מכסות את מרחב התוצאות של כל intent נושא-פסק-דין. תוויות ספציפיות-ל-intent ("צדקתי" / "עמדתי בזה" / "סופק" / "אושר" ל-Right וכו') הן עניין רינדור בצד הצופה הנפתר מול ה-intent של ה-qub ההורה — שכבת ה-wire נשארת נייטרלית בשפה וב-intent. ערכים מחוץ ל-1..=4 חייבים להידחות בעת הפענוח.

קישור להורה. qub פסק דין אינו נושא את ההפניה להורה בגוף שלו. מזהה הטרנזקציה של Arweave של ה-qub ההורה נפלט בזמן ההעלאה כתג אחסון Parent-Tx-Id (§7, שכבת תגי האחסון). כך הגוף נשאר הצהרת הערכה-עצמית חתומה ועצמאית; שרשרת הביקורת ("צדק במה?") נקבעת דרך חיפוש תג Arweave.

בטיחות כתובת הראייה (נורמטיבי). כאשר evidence_url קיים, מאמתים (בצד ההרכבה, בצד ה-wire, בקצה ה-Worker) חייבים לאכוף את הבאים:

  1. HTTPS בלבד. המחרוזת חייבת להתחיל ברצף הבתים https://. כל סכמה אחרת — http, ftp, javascript, data, file וכו' — נדחית.
  2. תקרת אורך. ≤ 2,048 בתים (גבול URL מעשי בדפדפנים).
  3. בדיקת NFC + קודפוינטים עוינים. אותו כלל כמו ל-title ול-reflection — קודפוינטים של bidi-override / zero-width / tag-block / BOM / C0 / C1 נדחים. ההגדרה תואמת לפונקציה הראסטית crate::handle::contains_hostile_text_codepoint ולפונקציית ה-TS שב-workers/api/src/utils/unicode.ts::isHostileCodepoint (יש לשמור על סנכרון מלא).
  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 תווי hex קטנים) ו-Parent-Tx-Id (מזהה עסקת האחסון של ה-qub האב עבור שרשראות תגובות, 43 תווי base64url).

תגית ה-Author היא בהצטרפות-מרצון לכל qub: אפליקציית היוצר של ההפניה מצרפת אותה רק כאשר המשתמש מאפשר במפורש ייחוס ציבורי בזמן האטימה. כאשר המתג כבוי — ברירת המחדל — לא נכתבת תגית Author וה-qub אינו מיוחס בשרשרת: שום דבר באחסון הקבוע אינו קושר את ההעלאה לכינוי, דוא"ל או qubs אחרים של היוצר. כאשר המתג דלוק, טביעת האצבע של Author מתפענחת ל-@handle שבחר היוצר דרך שרשרת האישורים של §9.5. יחסי שרשרת-תגובות ו-Intent אינם מזהים. במסירה פרטית העטיפה החיצונית (§13) מצפינה את ארטיפקט ה-SealedQub הפנימי הניתן לזיהוי, ולכן קצירת עטיפות מאוחסנות והשגת חתימות drand ציבוריות עדיין אינן מספיקות לשחזור הגוף ללא K; תגיות האחסון נשארות במכוון מטא-נתונים פומביים.

שירות-ההפניה במכוון אינו מצרף תגיות App-Name, App-Version או Type: כל מסנן בעל-ערך-יחיד כזה יחזיר את כל קורפוס ה-qub לשאילתת 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 (הנוכחית — מופקת על ידי כל חתימת מחבר חדשה, ועל ידי שתי החתימות של זרימת ההכנה / החתימה-המשותפת של pact):

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]). ללא ריפוד. המפריד השונה מפריד-תחום בין שתי הבניות, כך שחתימה על טרום-תמונה אחת לעולם אינה יכולה להתאמת מול האחרת.

בית org_id_present: הבית העוקב אחר 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

חלופת טביעת-האצבע היא ה-hex הקטן של 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–2,000 בתים, ML-DSA-65 משלש בערך את הגודל המאוחסן. העלות המוחלטת זניחה.

9.7 אימות מאשר-משותף (הסכמים דו-צדדיים של pact)

עבור הסכמים דו-צדדיים (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."

תכונות:

שער קישור-דוא"ל (תפעולי). כאשר pact בהכנה נושא איש קשר דוא"ל של צד 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. רינדור וחיטוי Markdown

סעיף זה הוא קריטי-לאבטחה. הצופה מרנדר qubs טקסט (content_type = 0x01) באמצעות תת-קבוצה מוגבלת של Markdown.

10.1 אלמנטים מותרים

10.2 אלמנטים אסורים

אלמנט טיפול
HTML גולמי (<div>, <script> וכו') מוסר לחלוטין. שום HTML אינו עובר.
תמונות (![alt](url)) מוסר. תחביר תמונה מוסר מהפלט.
קישורים ([text](url)) הכתובת מרונדרת כטקסט פשוט גלוי. אינה מקושרת אוטומטית. אינה ניתנת ללחיצה ללא פעולת משתמש מפורשת.
סכמות URL מסוכנות javascript:, data:, vbscript:, file: — מוסרות.
Iframes, embeds, objects מוסרים.
ישויות HTML מפוענחות לתווי תצוגה רק אם בטוחות.

10.3 מימוש

מימושים חייבים להשתמש במנתח רשימת-היתר קפדני, לא ברשימת-חסימה. הגישה המומלצת:

  1. נתחו את ה-Markdown באמצעות pulldown-cmark (או שווה-ערך).
  2. הלכו על ה-AST והשמיטו כל צומת שאינו ברשימת-ההיתר (§10.1).
  3. עבור צמתי קישור: פלטו את הכתובת כטקסט גלוי, לא כאלמנט <a> הניתן ללחיצה.
  4. המירו את ה-AST המסונן לייצוג ביניים מטופס (למשל, enum מסוג MarkdownNode עם וריאנטים בטוחים בלבד). HTML גולמי בלתי-ניתן-לייצוג מבנית בייצוג ביניים זה.
  5. רנדרו מייצוג הביניים המטופס לשכבת התצוגה היעד (למשל, רכיבי תצוגה ריאקטיביים, צמתי DOM). אין שרשור מחרוזות HTML או innerHTML בשום שלב.

גישות רשימת-חסימה שבריריות מכיוון שהרחבות Markdown חדשות או מוזרויות מנתח יכולות להכניס אלמנטים לא-מסוננים. גישת ה-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>.

סטטוס מהדורה הוא אחד מאלה: טיוטה (טרם נורמטיבית), נוכחית (יעד המימוש המומלץ היחיד), או הוחלפה (נשמרת לצורך אימות היסטורי). הנתיב חסר-הגרסה /protokol מציג את המהדורה הנוכחית; תג המהדורה משמר את המקור המדויק שלה ואת כל השפות שפורסמו עמה. שינוי סטטוס או מספר מהדורה מחייב עדכון של טבלה זו ושל היסטוריית המהדורות באותו שינוי שנבדק.

מהדורת מסמך תאריך תחילה סטטוס פרוטוקול על-החוט עטיפה מקור
1.0.0 2026-09-23 נוכחית 0x01 0x01 protocol-v1.0.0

12.2 גרסת פרוטוקול

שדה ה-version (u8) גם ב-SealedQub וגם ב-QubEnvelope מזהה את גרסת הפרוטוקול הראשית.

12.3 היסטוריית גרסאות הפרוטוקול

גרסה ערך תיאור
v1 0x01 מסירה פרטית/עטופה וציבורית/חשופה; גופי טקסט (0x01), pact (0x03) ופסק דין (0x04); חתימות מחבר/חותם-משותף ML-DSA-65 V2; 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 ניתנת לזיהוי, כך שקוצֵר שאינדקס עסקאות אחסון-קבוע יכול לפענח-בכמות את כל קורפוס ה-qub.

עבור מסירה פרטית, עטיפת ההצפנה החיצונית סוגרת את הערוץ הזה על ידי הוספת שכבת 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

הבית הראשון של ה-OuterWrapper CBOR הוא לפיכך כותרת המפה באורך-מוגדר עבור מפה בת 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/הטמעה-אוטומטית ללא מקטע ו-SEO עשיר יותר לאחר השחרור — כולם זקוקים לקישור שפועל ללא סוד שהשרת לעולם אינו מחזיק בו (§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 זהים עבור קלט זה. וקטור בדיקה זה מומלץ שיהיה בדיקת היחידה הראשונה שנכתבת. הערכים הקנוניים לעיל חושבו על ידי מימוש-ההפניה וחייבים להתאים סיבית-אחר-סיבית. פריסות אב-טיפוס היסטוריות מלפני ההשקה (שום qub חי לא הסתמך על השתיים הראשונות) השתמשו ב-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 CBOR של PactTerms (content_type 0x03)

Input:
  pact_version = 1
  title        = "Scooter deposit"
  terms        = [
    { key: "Item",    value: "Honda Metropolitan scooter" },
    { key: "Price",   value: "$100" },
    { key: "Deposit", value: "$10" }
  ]
  party_a      = { label: "Alice" }
  party_b      = { label: "Bob", contact: "bob@example.com" }
  notes        = absent

Canonical CBOR key order (PactTerms):
  "notes"(6) < "terms"(6) < "title"(6) < "party_a"(8) < "party_b"(8) < "pact_version"(13)

Canonical CBOR key order (PactTerm):
  "key"(4) < "value"(6)

Canonical CBOR key order (PartyIdentifier):
  "label"(6) < "contact"(8)

בתי ה-CBOR הקנוניים ו-body_hash של SHA3-256 מחושבים על ידי מימוש-ההפניה. מימושים חייבים להפיק CBOR זהה-בתים עבור קלט זה.

מימושים חייבים גם לאמת ש-serialize(parse(serialize(pact))) == serialize(pact) עבור כל קלטי PactTerms התקפים (בדיקת תכונה).

14.5 וקטורים חוצי-שפות של העטיפה החיצונית

לעטיפה החיצונית (§13) יש fixture קנוני נפרד ב-crates/qub-core/tests/vectors/wrapper_v1.json. כל מקרה מקבע צמד (key, nonce, qub_id, sealed_cbor) כקלטי hex אטומים ומאשר פלט expected_wrapper_hex ספציפי. שני מימושי-ההפניה צורכים את אותו קובץ JSON:

ה-fixture מקבע כרגע שלושה מקרי עטיפה ברמה נמוכה. הם בודקים קידוד דטרמיניסטי של OuterWrapper ופעולת-גומלין של AEAD בנפרד מאינווריאנט צורת המסירה של §13.8; בפרט, השם ההיסטורי basic-text-public והערך הפנימי שלו visibility = 0x01 אינם הופכים את הבתים העטופים למסירה פומבית תואמת. יצרן עדיין חייב לשמור בתים פנימיים פומביים חשופים ולעטוף רק בתים פנימיים פרטיים (0x00).

מקרה כיסוי
basic-text-public שם היסטורי של fixture ברמה נמוכה. צורת SealedQub הריאליסטית הקטנה ביותר, ללא שדות אופציונליים; בודק רק את בתי העטיפה ואינו מסירה מאוחסנת תואמת-§13.8.
with-recipient-pubkey SealedQub עם recipient_pubkey מוגדר (מסלול עתידי שמור). בודק קבוצת מפתחות CBOR פנימית אחרת; התוכן השונה של ה-fixture יוצר באופן בלתי-תלוי qub_id שונה (recipient_pubkey עצמו אינו חלק מהתמונה המקדימה של §4.1).
longer-body גוף ~4 KiB — מפעיל קידומות אורך CBOR רב-בתיות גם בתוך המעטפה הפנימית וגם בתוך הטקסט המוצפן החיצוני.

מימושים חייבים להפיק expected_wrapper_hex זהה-בתים עבור הקלטים המוקלטים. יצירה מחדש של ה-fixture מצריכה 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 + canonical-CBOR הליבתיים (qub-core), מראה TypeScript + מאחד ANS-104 (workers/api/src/crypto/), הכותב היחיד LogDO + חנות צמתים R2 ממוינת לפי קואורדינטות, ה /upload ניסיון להוספת יומן, העוגן היומי + קרונס של סינון ובאנדלר 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] placeholder); (ב) מפתח החתימה על הקבלה והתאם פין מפתח ציבורי (RECEIPT_SK אופציונלי ו LogProfile.receipt_pubkey כעת ריק); ו-(ג) מאגר GitHub של self-published-heads + טוקן (§16.6). עד שהעוגנים/סיכות הפרופיל יסופקו, מאמת עצמאי מדווח על מצב ההוכחה ביושר במקום לטעון לאימות מעוגן ומסומן במלואו. העיצוב הוא אך ורק מצטבר ויש אין שינוי ב־ SealedQub / QubEnvelope פורמט חוט.

16.1 נימוק ורמות עמידות

נתיבי הפרסום הנוכחיים מבדילים בין אישור לבין אישור ארוויו: הם גוזרים ומחתימים עסקה בודדת, שומרים את הפריט ואת מצב ההגשה המדויק ב-R2, ואז מפרסמים באופן אסינכרוני. יומן השקיפות מוסיף שכבת סידור מעוגנת באופן עצמאי לתת-הקבוצה הכללית. /upload בקשות שֶׁל LogDO הוספה מצליחה:

שכבה שם אחריות מתי
T1 R2-אישור סינכרוני ראשון רצפת עמידות — בתים מוחתמים ומצב הפרסום המדויק נכתבים לאחסון עמיד לפני החזרת ההצלחה. מומש לאורך מסלולי הפרסום הנוכחיים.
T2 הכללת יומן שקיפות באצוות מחויבות רק לצירוף, עמידה למניפולציה + סידור כולל ברגע שהיא נכללת ומעוגנת. מפיק נוכחי: מצליח LogDO מצרף מ /upload; התגובה מכילה את זוג הקבלה. לא אוניברסלי.
T3 נצחיות פר-קוב של Arweave עסקת Arweave בודדת עבור ה-qub. כרגע מוכן לכל פרסום שמתקבל ומפורסם באופן אסינכרוני; העסקה המדויקת החתומה נשארת בתיבת היציאה שניתן לרוקן עד שהיא נמסרת.

הרמות מתארות תכונות שונות של ראיות ועמידות, לא את התוכנית המסחרית הנוכחית. הקוד הנוכחי עדיין מתזמן עסקת Arweave נפרדת עבור כל פרסום מתקבל; הוא אינו מציג את T3 רק כהרחבה בתשלום. תקרות מפתח-API/חשבון נותרות בקרות יישום נפרדות.

עמידות יושר. כתיבת T1 היא סינכרונית, כך שתגובה מוצלחת מייצרת עמידות ברמת היישום בלי להמתין לשער Arweave. היא לא מקימה בעצמה חותמת זמן עצמאית. עסקה אישית מאומתת מספקת את הגבול העליון של זמן הבלוק שלה. עבור תגובה הנשאת את זוג הקבלה המלא של T2, העיגון המאומת הבא יכול לספק את הוכחת היומן המתוארת למטה. אם הזוג חסר, אף משטח לא יכול לרמוז שהקובייה הזו כבר קיימת ביומן השקיפות. לעיגון ולפרסום אין SLA מספרי ברמת הפרוטוקול.

16.2 מבנה עלה לוג (שתי צורות מחויבות)

כניסת יומן היא LogLeaf, מקודד כ-CBOR קנוני בכתב-יד תחת פרופיל §3.1 (אורך מוגדר, ללא תגים, ללא מספרי נקודה צפה, מספרים בשל הצורה הקצרה ביותר, טקסט ב-NFC, שדות אופציונליים נמחקים כאשר אינם קיימים, מפתחות מסודרים לפי אורך הבייטים המקודדים בסדר עולה ואחר כך לפי סדר הבייטים). §3.1 parse → re-encode → compare השומר הקנוני מוחל בנתיב הקידוד לפני הגיבוב (לא רק בפענוח), כך ששתי מימושים לא יכולים לחלוק על הבייטים הסופיים בגלל הבדל ברוחב של מספר שלם או סדר המפתחות. כל המספרים השלמים הם u8 / u64 / i64; כל העקיצות הן מחרוזות בייט של 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 ארבע u64 נדרש אינדקס עלים עולמי המבוסס על 0; המיקום שאליו מחויב הוכחת ההכללה.
kind חמישה u8 נדרש 0x01 מתועד-מסוגל (מוגדר, לא מופק כרגע) או 0x02 טען (חתימת לקוח / העלאה עיוורת בבייט).
ref ארבע bstr[32] נדרש זיהוי עלה. מתועד → גולמי qub_id. טען → the עיוור תעודת זהות SHA3-256(qub_id ‖ log_blind_secret) (סעיף 16.2.1).
chash שש bstr[32] נדרש כתובת תוכן SHA3-256(stored_bytes) — הקשר התוכן היחיד שהעובד תמיד יכול לחשב בכנות, בשני המסלולים.
unlock_at עשר i64 נדרש עותק (מאומת) או נטען (נטען); ואומת > 0 לפני שהוא נכנס לעלה.
received_at שניים עשר i64 נדרש שעון קיר לעובד ב-R2-ack. לא ראייתי (נתמך על ידי המפעיל; סעיף 16.6). נוכח לתיאור עצמי, לעולם לא כהוכחה. מאומת > 0.
body_hash עשר bstr[32] kind=0x01 רק נשמט ב 0x02 — לעובד אין זאת לפי סעיף 13.
drand_round שניים עשר u64 kind=0x01 רק נשמט ב 0x02.

א kind=0x02 עלה לא עובר על דבר בכוונה body_hash גם לא drand_round: זה מעיד על המחויבות והסידור של טקסט מוצפן אטום בכתובת-תוכן chash, טוען qub_id ו unlock_at — לא הטקסט הפשוט או הסיבוב שלו. הרגליים של הטקסט הפשוט/סיבוב עבור qub מוצמד מגיעות מ-§11 הקיים .qub-אימות חבילה, לא מהרשומה (§16.11). drand_chain_version אינו בעלה (הוא נמצא בתוך העטיפה בדרך הברירת מחדל); גרנולריות השרשרת נמצאת על העוגן (§16.7). דיסציפלינת המקודד: לדחות אפס-לאפס כולו ref או chash, ודחה לא חיובי unlock_at / received_at, שמשקף את outcome_at > 0 שומר משמר cbor.rs.

16.2.1 סתרון פרטי-qub

הרשומה חייבת לא להפוך לאורקל של ספירה שהעטיפה החיצונית בסעיף §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 שאלה 4). העיוור מגן על חוסר הקישוריות של העלה, לא על סודיות הטקסט הגולמי (המעטפת בסעיף 13 מחזיקה בכך באופן עצמאי). על log_blind_secret פשרה, עבור כל qub_id האויב כבר מחזיק או יכול לשחזר (כל קיוב ששורט או כתובת ה-URL שלו ברשותו, בנוסף לכל קובץ בעל אנטרופיה נמוכה או ציבורי qub_id) זה מחשב מחדש את העלה ref ב אחת האש ומקשר אותו — זו קישור ישיר של אוכלוסייה ידועה, לא כוחנות על פני מרחב בלתי ידוע. סווג log_blind_secret כתלויה/סוד ברמת סיביל באותה שכבת אחזקה כמו סודות שרת אחרים, ומסתובב קדימה בלבד (סיבוב מחדש מטשטש את העלים העתידיים; הוא אינו יכול לנתק באופן רטרואקטיבי כאלה שכבר עוגנו).

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 hash, §16.6) שמורים ומופרדים מאלו. הם בתים יחידים ולכן אינם יכולים להתנגש עם מפרידי התחום האסקי הקיימים באורך 10 בתים (QUB_ID_V2, וכו'). העץ הוא RFC 6962 שמאלה מלא לא מאוזן עץ (כל חלוקה פנימית בגדול ביותר של שתיים שהוא קטן משמעותית ממספר העלים בתת-העץ), שמאפשר להוכחות הכללה ועקביות לשתף אלגוריתם מסלול ביקורת אחד. המפרט ההתייחסותי מכיל קוד פסבדו מפורש של הפקת שמאלה/ימינה ומצמיד וקטור בדיקה שאינו חזקה של שתיים (עם 5 עלים) אז מקרה הקידום בקצה הימני — שמוסתר על ידי וקטור בעל ארבעה עלים — מתבצע.

16.4 שרשור גיבוב (פנימי)

הלוגDO שומר שרשרת רשומות פנימית עבור עקביות קריסה בלבד. הוא מעולם לא פורסם ומעולם לא היה מול המאמת:

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 הוא ה-Hash המצטבר של עץ מרקל על העלים 0 .. tree_size_i.

16.6 ראש עץ חתום דרך עוגן Arweave

עסקת העוגן של ארוויו הוא/היא/זה ראש עץ חתום ו מחליף חתימת מפעיל לעצם ראש העץ: העוגן היומי אינו צריך מפתח qub כי הטרנזקציה של 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. ה-hash שלו הוא 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 — לצד הקבועים של quicknet שכבר קיימים DrandTimelockProvider::quicknet() — ומופץ עם הקובץ הבינרי של המוודא. המוודא חייב גם לאמת את העסקה ב-Arweave נתונים → קשירת tx_id מקומית במקום לבטוח בשער /raw/ תגובה. זה סוגר את חור ההתחמקות של הארנק הנוכלי: "מובסס על Arweave" חסר משמעות עד שהמאמת מצמיד איזה ארנק.

סיבוב הוא הרחבה לניהול לפי סעיף 15, לא שימוש חוזר (נפתר — סעיף 16.15 שאלה 3). §15.2 שטח הפרופיל מציין כעת רק sig_algs / שרשרות drand / גרסאות wrapper / סוגי תוכן, ורשימת הטריגרים של §15.3 אינה כוללת אף אחד מהם — LogProfile / anchor_owner הוא/היא/זה לא עדיין במשטח של סעיף 15. לפיכך, ניהול הסיבוב חייב להיות מובנה: סעיף 15.3 מורחב (מטה) להוסיף את ה LogProfile זַרְזֵר, וסיבוב הוא עם סימן LogProfile באמפ נשלח בעדכון אימות. A מתוכנן סיבוב נושא חותם צולב יוצא → נכנס; א מונע על ידי פשרות הסיבוב לא יכול להתבצע (המפתח היוצא אינו מהימן/לא זמין בדיוק אז) ומסתמך מחדש על הדחיפה המנוהלת לפי §15, עם בדיקת הפיצול של העוגן הקודם (להלן) שמגבילה את הנזק בזמן הביניים.

חלון הִתְעוֹרְרוּת עמימות (פרמטר אמון מדרגה ראשונה). עלה חסין להטעיה רק כאשר עוגנו המכסה הוא ארוויומאושר. החלון הוא received_at → anchor confirmation (קדנס + סופיות של Arweave, ללא התחייבות לעיכוב פרוטוקול). לפני אספקת שורש האמון, היישום הנוכחי מספק את שלמות הפעולה של qub בתוספת כל המטא-דאטה הלא חתומה שנמצאת; הוא אינו מספק את ההבטחה המתוכננת של אי-כחישה. שלושה פריטי אחריות מגדירים את העיצוב המושלם (מודל העד הוא ההחלטה של §16.15 שאלה 2):

  1. אישור חותם (תלוי באספקה) — האנלוג של SCT חוזר כאשר הוספת יומן של העלאה מצליחה (§16.10). הוא הופך לבלתי ניתן להכחשה רק כאשר sig_b64url אינו ריק ו קשר המפתח הציבורי/בעל העוגן התואם נעוץ במאמת. סיכת פרופיל הייצור הריקה הנוכחית אינה יכולה לתמוך בפסק הדין הזה. בקרה זו אינה חלה על זוג קבלה שהושמט או על קבלה לא חתומה.
  2. מתודולוגיית מסך פורסמה + הליכת שרשרת קודמת — העוגן prev השרשרת נצעדת ראש→בראשית; מזלג (שני עוגנים באחד) size עם שונה root, או שבור prev) הוא הוכחה הניתנת לפרסום להתנהגות בלתי הולמת. גילוי רב-ערכיות הוא התחייבות תפעולית מודעת, לא הנחה שקטה.
  3. ראשי אוטופובלים כפולים — כל ראש חדש {sth_hash, tree_size} מפורסם ל-qub בבעלות ייעודית מאגר GitHub ציבורי, שניתן להוסיף אליו בלבד (הרגל הנשיאה החזקה שניתן להבחין אם פרסומים פורסמו בעצמו), עם פוסט חברתי כהוכחה מיטבית בלבד. פרסום שנכשל חייב לשלוח הודעה (ולא להיכשל בשקט). ממומש (שלב 8) כ publishHead חכה על עוגן קרון (workers/api/src/utils/heads-publish.ts): א PUT אל ממשק ה-API של התוכן ללא sha הוא רק לצירוף (a 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 מוצהר על ידי המפעיל ו אין תביעה יכולה להישען עליה — זה אף פעם לא מוצג כהוכחה או כהסקת סתירה על שום מוצר / משפטי / API / משטח הצגת הוכחה. זמן גוש העוגן של Arweave T הוא חותמת הזמן היחידה ללא אמון (גבול עליון ל"נרשם על ידי"). כל בדיקת שפיות של מנטר ב received_at חייב להשוות מול T, לא נגד הנשלט על ידי המפעיל anchored_at שדה STH; בדיקה כזו היא מגן נגד תקלת שעון של מפעיל ישר רק, לא ביקורת אחריות נגד מפעיל זדוני (§16.15 Q5).

16.7 פורמט עסקאות עוגן וקצב

ה AnchorBundle זהו גוף העסקה של Arweave ב-CBOR הקנוני, שכתוב באמצעות המאגד §16.8: ver:u8, sth:bstr (קנוני SignedTreeHead בתים), prev_anchor:bstr (בתים גולמיים של מזהה העסקה הקודמת; לא נכלל בבריאה) chain_hash:tstr (שרשרת הדרנד בתוקף — קוויקטנט), ו זרם עלה-CBOR של האצווה נכנס seq הזמנה לכן העוגן הוא עצמאי: המסך נגזר מחדש root מהגוף ללא תלות ב-qub. (אם זרם העלים יהפוך לגדול בנפח גבוה, ייתכן שגרסה עתידית תבצע התחייבות רק על טווח עלים באמצעות הפניה; צויין, לא אומץ בגרסה 1.)

תגי Arweave מכוונים להיות ניתנים לספירה — יומן זה מכוון להימצא, בניגוד ל-qubs פרטיים: 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 הוא הסמכות היחידה.

קֵדֶנְס יומי כברירת מחדל, נבדק מחדש עם נפח (הטריגר לגודל מקצר אוטומטית את הקצב האפקטיבי בעומס). המפיק הנוכחי אינו מממש קרס כוח-אחיזת חותם בתשלום. ה ארנק אנקור מיועד ובעל מהירות נמוכה, נפרד מארנק ההעלאה — זה חייב להיות שלו JWK משלי (מפתח נפרד, לא תפקיד לוגי בארנק ההעלאה) כך שהפרת ארנק העלאה לא יכולה לזייף עוגנים — עם תקציב קשיח ליום לעסקאות עוגן. עמדת המשמורת מצוינת באופן ברור: a מקשי חם בטווח מצומצם עם מפסק מעגל הדוק ואיזון נמוך, לא "קר" — ארנק שחותם אוטומטית מדי יום לא יכול להיות קר, והמפרט לא מתחזה אחרת.

16.8 ANS-104 בונדלר

מקודד DataItem פנימי לפי ANS-104 ומחתים deep-hash, בערך 300 שורות, רק Web Crypto, ללא תלות ב-npm (שני ערכות הפיתוח Turbo נכשלות ה 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

חתימה היא ארוויו deepHash — רקורסיבי SHA-384 לפענח (דרישת הכבל של Arweave, crypto.subtle.digest("SHA-384")) מעל ["dataitem", "1", sig_type, owner, target, anchor, encoded_tags, data] — אז RSA-PSS מעל ה-hash העמוק עם JWK של הארנק דרך crypto.subtle; id = base64url(SHA-256(signature)). ה-SHA-384 כאן הוא מבודד כפרימיטיב של Arweave-לכבל בלבד, אף פעם לא פרימיטיב של qub trust (§15 מתעד את הגדר; גיבוב האמון של qub הוא SHA3-256 לאורך כל הדרך).

נתיב הקוד ANS-104 משרת את מנגנון הגיבוי/ניקוז המושהה וכותב AnchorBundle DataItems. נתיב הפרסום הרגיל יוצר תחילה עסקה חתומה בדיוק ב-Arweave ושומר את ה-JSON שלה בתיבת יציאה עמידה; פרסום ישיר הוא אופטימיזציה של השהייה, ונתיב הניקוז מנסה שוב את אותה עסקה לפני החלת הפתרון האלטרנטיבי של הבאנדלר. סכמת חתימה (נפתרה — §16.15 שאלה 8): v1 חותם עם RSA-PSS (סוג חתימה 1) שימוש חוזר במנגנון JWK של ארווירויב הקיים (ללא שמירת מפתחות ארוכי טווח חדשים, במענה ל"תזת סוד אחד פחות"); Ed25519 נדחה לנתיב §15 של העברת PQ.

החשיש העמוק המגולגל ביד הוא הקוד בסיכון הגבוה ביותר ובכיסוי הטבעי הנמוך ביותר ב-W5, כך שהגייטינג שלו הוא בלתי ניתן למשא ומתן (סעיף 16.15 שאלה 8):

  1. התושבת רב-שפתית tlog_v1.json (Rust + TS, הסעיף 14.5 wrapper_v1.json הדפוס) כולל hash עמוק, בתים של DataItem + מזהה, hashes של עלים, שורש עם 5 עלים + מסלול ביקורת, hash של STH, הוכחת הכללה, והוכחת עקביות — ב גם את הכיוונים של החתימה וגם של האימות (כיוון האימות חשוב כי בדיקת tx → tx_id המקומית בסעיף §16.6 מושכת את ה-hash העמוק לכל מאמת עצמאי, ולא רק לכותב).
  2. סבב אינטרופ חד-פעמי דרך איסוף ANS-104 כהתייחסות, שנצרך כ נתוני בדיקה סטטיים בלבד — לעולם לא תלות ריצה של npm (העמדה של Web-Crypto בלבד / ללא סקריפטים להתקנה נשארת).
  3. נתיב ה-deep-hash + RSA-PSS חייב לעבור סבב חזרה דרך ה אותו crypto.subtle פרימיטיבים לשימושים בייצור, כך שהמקודד הפנימי תואם בבייט.
  4. מתמשך מוניטור אחר קבלת החבילה מאשר שכל DataItem של עוגן / גיבוי אכן משיג קבלת Arweave, עם אזעקה + מפסק מעגל — כי ה-deep hash גם משמש את תור הגיבוי למקרה אי-זמינות של Arweave, כך שהחזרה שקטה הייתה ממלאת את התור הזה בפריטים שהרשת דחתה במהלך ההפסקה המדויקת שהיא נועדה לכסות.

16.9 הוכחות הכללה ועקביות

שניהם הם RFC 9162, SHA3-256, המוגשים כ-CBOR קנוני.

הוכחת הכללה — 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 }.

הוכחת עקביות — 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 של הוספה עושה רק זאת; סגירה באצווה פועלת מחוץ לנתיב על האזעקה. כשל בהעברת הוספה/יישום הוא כרגע כישלון רך: התגובה עדיין יכולה להצליח בלי log_seq, receipt, או anchor_status. למרות הערת יישום, כיום לא מחובר שום התאמת יומנים אוטומטית לאחר מכן.
  5. החזר את האישור. כלול { log_seq, anchor_status: "pending", receipt } רק כאשר הפונקציה append החזירה את הטופל המלא והמצליח. 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), לא לפי היומן; מה שהיומן מוסיף על עסקה פר-קוּב יחידה הוא סדר שמאפשר לזהות זיופים, זמן התחייבות עליון ללא צורך באמון, והתנגדות לְעוּלוּת.

שני הטענות יוצאות מן הכלל, על פי סעיף 11: מחבר ללא sig_alg ≥ 0x01, כוונה, ותזמון של תת-עוגן-גרנולריות. אף אחד מהם לא מאפשר שטענה כלשהי להתבסס על received_at.

תקרת תביעה (מגבלת השקה מחייבת — נפתרה בסעיף 16.15 שאלה 1). למען טענה (kind=0x02) עלה, הטענה הממוקדת לעיל היא ה תקרה על כל מוצר, שיווק, תנאים או משטח הצגת הוכחות עשוי להצהיר. אין משטח שרשאי לומר או לרמוז כי יומן מוכיח את התוכן או את סבב הפתיחה של העלאה עיוורת-בייט — היומן מוכיח סידור + התחייבות זמן עליונה ללא אמון של טקסט מוצפן אטום. הוכחת התוכן והסבב מגיעה בלעדית מ-§11 הקיים .qub-אימות חבילה, שהוא בלתי תלוי ביומן. פרסום ללא הוספה/קבלה מוצלחת אינו מחזיק כלל בטענה ביומן.

16.12 גרסאות ותיאום W3

יש לא SealedQub בליטת חוט ולכן אין העלאת גרסת פרוטוקול (§12.2): היומן הוא סיידקאר שמתחייב לשדות ובייטים קיימים, ולכן הוא אינו נכנס להיסטוריית גרסאות הפרוטוקול של §12.3. אופציונלי ב-W3 drand_chain_version נשאר ללא מגע ונשאר האופציונלי היחיד SealedQub תחום. היומן במקום זאת מציג את מרחבי הגרסאות העצמאיים שלו — LOG_VERSION_1, ANCHOR_FORMAT_1, InclusionProof.ver — משקף את §12.5 של עצמאות גרסת העוטף (העוטף נושא בת גרסה שהיא בלתי תלויה בגרסת הפרוטוקול, וגרסאות היומן עוקבות אחרי ההפרדה הזו).

מסירת ההוכחה נלקחת כברירת מחדל, עם אפשרות לנסיעה נלווית. הוכחה לא יכולה להתקיים בזמן החותם (העוגן עדיין לא נכתב), ולכן בזמן החותם .qub החבילה נשארת ללא הוכחות. המאמת של W7 מושך GET …/proof פעם, או במצב אופליין מלא משחזר את ההוכחה מהציבור AnchorBundle באמצעות שאילתה של Arweave על Log-Id. ה .qub חבילת (W7) שומרת על אופציונלי inclusion_proof חבר — חסר בחותם, מאוכלס על ידי ייצוא חוזר לאחר עוגן לארכוב קר — בעקבות אותה דפוס "רשותי, מושמט כברירת מחדל, מצרפי" כמו של W3 drand_chain_version.

16.13 שמירה

חלונות השימור עבור זנב פתוח של LogDO, המצע המשרת הוכחות R2, מוני מפסקי עוגן ותור גיבוי של bundler מוגדרים ב docs/DATA-RETENTION.md. עיקרון: אחסון החום לכל כניסה של יומן (LogDO) ניתן לשחזור לאחר עיגון; חומר הביקורת שלו — המכוון לפי מפתח קואורדינטות (level, index) חנות קשר מרקל + ה seq-גופי עלים מוסדרים (§16.9) + עוגני Arweave — הם קבועים. השבתת עלה קר מה-DO אף פעם לא מבטלת הוכחה שהונפקה, מכיוון שההוכחה מתייחסת לאחסון הצמתים הקבוע R2 ולעוגן ה-Arweave, לא ל-DO (וקטור הבדיקה wiped-DO שבסעיף §16.9 מוכיח זאת).

16.14 וקטורי בדיקה

W5 שולח את המתקן הרב-לשוני tlog_v1.json (§16.8) פלוס וקטורים שעובדו: a kind=0x01 ו־ kind=0x02 עלה → leaf_hash; השורש המצטבר בעל 5 עלים; הוכחת הכללה אחת; הוכחת עקביות אחת; אחת 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 כבר מספק מהחבילה). עשה לא דרוש חותם שרת עבור qubs המאומתים ביומן (שזה יכריח טקסט רגיל לעבור דרך העובד ויהרוס את תעלת ההשמדה הקריפטוגרפית). כל מעגל קצר שמתאר את עצמו שייך ב .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. חסימת עלה Private-qub — נפתרה. המשך לעיוור עבור קיובס פרטיים (ref = SHA3-256(qub_id ‖ log_blind_secret)), גולמי qub_id לציבור הקיובס (כבר בסעיף 16.2.1), chash כעניבה עצמאית. log_blind_secret הוא סוד ברמה של קורלציה/סיבל, סובב-קדימה בלבד (§16.2.1).
  5. received_at — הוחלט. שמור את זה בעלה, מחויב אך במפורש לא ראייתי; מעולם לא הופיע כראיה או כהשלמת מחלוקת על כל משטח. כל בדיקת שפיות של המעקב משווה מול זמן הבלוק של ארוויי T, לא הנשלט על ידי המפעיל anchored_at (סעיף 16.6).
  6. זמני-הוכחה מדרגיים — רזולוציית עיצוב, לא ניתוב נוכחי. העיצוב שנבדק מייעד זמני בלוק עוגן לרמה הממוזגת והוכחת שעה מדויקת ל-T3 בתשלום, ללא SLA מספרי עבור הראשון. הנתיבים הנוכחיים לא מחוברים להבחנה מסחרית זו: הם מתוזמנים לעסקה נפרדת עבור כל פרסום מתקבל, וכיסוי הרישום נשאר מותנה כפי שמצוין בסעיפים §16.1/§16.10. עותק המוצר חייב לתאר את היישום, לא את חלוקת הרמה העתידית הזו.
  7. עץ מצטבר על עובדים — נפתר. עץ RFC 9162 מצטבר יחיד + LogDO של כותב-יחיד עם מטמון גבול (מרווח נוח מול תקרת ה-DO של ~1k כתיבות לשנייה; דחה שרתות Merkle-of-shard-roots עד להתקרבות לתקרה). המפתח המקואורדינט (level, index) מחסני צומת R2 + וקטור מבחן עלים-קרה נמחקים מיושמים (§16.9). < 300 ms נשאר מטרה של עיצוב/תפעול, ולא הבטחת פרוטוקול (§16.10).
  8. סכימת חתימה ANS-104 + deep-hash — נפתרה. RSA-PSS (סוג חתימה 1, שימוש חוזר במפתח ה-JWK היעודי של הארנק-עוגן); Ed25519 נדחה למסלול §15 PQ. ה- SHA-384 המיוצר באופן ידני של hash עמוק מותנה במכשיר הבדיקה cross-impl דו-כיווני, בדיקת אינטראופרביליות של bundler התייחסות סטטי בלבד, ה- shared-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 ללא ריפוד.

מפתח אורך מקודד טיפוס נוכחות משמעות
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, לאחר שהיומן נשלח (§17.5).
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 אטום—ללא העלאת גרסת פורמט החבילה. ה-CLI העצמאי qub-verify צורך אותו דרך זרוע --anchor, ועד להקצאת ארנק העוגן (§16, סטטוס) מדווח שהוכחה מאוכלסת עם בעלים מציין-מקום היא הכללה בלבד ולא מאומתת-מול-עוגן במלואה.