Προδιαγραφή Πρωτοκόλλου qub

qub είναι ένα πρωτόκολλο για κρυπτογραφικές χρονικές δεσμεύσεις: ένα σύστημα για τη σφράγιση λέξεων σε μια μελλοντική ημερομηνία και την απόδειξη, όταν αυτή η ημερομηνία φτάσει, του τι ακριβώς ειπώθηκε και πότε.

Τρία πρωτεύοντα στοιχεία το κάνουν να λειτουργεί. drand είναι ένας αποκεντρωμένος φάρος τυχαιότητας — η ημερομηνία αποκάλυψης επιβάλλεται από τη φυσική, όχι από την καλή πίστη οποιουδήποτε μέρους. Η μόνιμη δημόσια αποθήκευση είναι ένα ανθεκτικό σε παραποίηση δημόσιο κατάστιχο — κανένα μέρος δεν μπορεί να επεξεργαστεί ή να διαγράψει ένα qub αφού αυτό σφραγιστεί. ML-DSA-65 είναι μια μετα-κβαντική ψηφιακή υπογραφή — κάθε qub συνδέεται με ένα ζεύγος κλειδιών του οποίου το μυστικό δεν εγκαταλείπει ποτέ τη συσκευή του συντάκτη.

Μαζί, αυτά τα πρωτεύοντα στοιχεία παράγουν μια δήλωση που είναι χρονοκλειδωμένη, ανθεκτική σε παραποίηση και αποδιδόμενη — μια απόδειξη της οποίας η αξία αυξάνεται καθώς βελτιώνεται η ικανότητα του κόσμου να κατασκευάζει το παρελθόν.

Το υπόλοιπο αυτού του εγγράφου είναι η κανονιστική προδιαγραφή που απαιτείται για διαλειτουργικές υλοποιήσεις.


Προδιαγραφή Πρωτοκόλλου qub

Πεδίο Τιμή
Έκδοση 1.0 (έκδοση πρωτοκόλλου 0x01, έκδοση εξωτερικού περιτυλίγματος 0x01)
Ημερομηνία 2026-05-01
Κατάσταση Προσχέδιο
Αναθεωρημένο μέχρι 2026-05-01

Το παρόν έγγραφο είναι η κανονιστική προδιαγραφή πρωτοκόλλου για το σύστημα χρονικής δέσμευσης qub. Καθορίζει δομές δεδομένων, κανόνες σειριοποίησης, τύπους παραγωγής και διαδικασίες επαλήθευσης που απαιτούνται για διαλειτουργικές υλοποιήσεις.

Εμβέλεια: το επίπεδο του πρωτοκόλλου είναι σκόπιμα γλωσσικά ουδέτερο — το σώμα του qub είναι αδιαφανές απλό κείμενο / markdown / bytes συμφώνου, και η απόδοση με συναίσθηση τοπικότητας είναι ευθύνη του θεατή (εφαρμογή web qub.social, iframe <qub-embed>, πελάτες MCP, κ.λπ.).


1. Σύμβαση και Συμβολισμοί

Συμβολισμός Σημασία
u8, u64, i64 Μη προσημασμένοι/προσημασμένοι ακέραιοι καθορισμένου εύρους bit
[u8; N] Πίνακας bytes σταθερού μήκους N bytes
Vec<u8> Πίνακας bytes μεταβλητού μήκους
Option<T> Τιμή τύπου T, ή απούσα
String Συμβολοσειρά κειμένου UTF-8, κανονικοποιημένη σε NFC
`
SHA3-256(x) Κατακερματισμός NIST SHA3-256 της συμβολοσειράς bytes x (FIPS 202)
ceil(x) Συνάρτηση οροφής: μικρότερος ακέραιος ≥ x
CBOR Concise Binary Object Representation (RFC 8949)
big-endian Πρώτα το πιο σημαντικό byte

Όλοι οι ακέραιοι σε κατασκευές προεικόνας κωδικοποιούνται ως πίνακες bytes σταθερού εύρους big-endian (i64 → 8 bytes, u8 → 1 byte) εκτός αν προδιαγράφεται διαφορετικά.

Όλες οι χρονοσημάνσεις είναι δευτερόλεπτα 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,              // 0x01 = public (only value in MVP)
    content_type:   u8,              // 0x01 = text (only value in MVP)
    plaintext:      Vec<u8>,         // UTF-8 qub body
    sender_label:   Option<String>,  // Decorative display name; not authenticated
    status:         DraftStatus,     // Composing | Sealed | Uploaded | Failed
}

2.2 QubEnvelope (Αποκρυπτογραφημένο Ωφέλιμο Φορτίο)

Σειριοποιείται με κανονικό 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>,     // V1.1 — όταν η πραγματικότητα εκδίδει ετυμηγορία (verdict-uplift-plan §3.1)
    sender_label:        Option<String>,  // Decorative; not authenticated in MVP
    reply_to:            Option<[u8; 32]>,// Parent qub_id for reply chains; not in qub_id preimage; not signed (see §9.3)
    body:                Vec<u8>,         // Content payload (UTF-8 for text, CBOR for pact)
    body_hash:           [u8; 32],        // SHA3-256(body) (see §4.2)
    sig_alg:             u8,              // Signature algorithm (see §9.2)
    author_signature:    Option<Vec<u8>>, // Set when sig_alg != 0x00
    author_pubkey:       Option<Vec<u8>>, // Set when sig_alg != 0x00
    cosigner_pubkey:     Option<Vec<u8>>, // Set for cosigned pact bilateral agreements
    cosigner_signature:  Option<Vec<u8>>, // Set for cosigned pact bilateral agreements
}

Βάση (μη υπογεγραμμένο qub κειμένου): version = 0x01, content_type = 0x01, sig_alg = 0x00, όλα τα πεδία Option απόντα.

Άλλες διαμορφώσεις v1: content_type = 0x03 (σώμα συμφώνου, βλέπε §6.1)· sig_alg = 0x01 (ML-DSA-65) με author_signature και author_pubkey παρόντα (βλέπε §9.3)· cosigner_pubkey και cosigner_signature παρόντα μαζί για συν-υπογεγραμμένα σύμφωνα (βλέπε §9.7)· reply_to ορισμένο στο qub_id του γονικού qub για qubs αλυσίδας απάντησης (βλέπε §9.3 για τις επιπτώσεις στην εμβέλεια υπογραφής).

2.3 SealedQub (Κανονικό Μορφότυπο Σύρματος)

Σειριοποιείται με κανονικό CBOR (§3). Εγγράφεται σε μόνιμη αποθήκευση. Αυτό είναι το αντικείμενο on-chain.

SealedQub {
    version:           u8,              // Protocol major version (0x01 for v1)
    qub_id:            [u8; 32],        // Same as QubEnvelope.qub_id
    visibility:        u8,              // 0x01 = public; v1 viewers reject other values
    unlock_at:         i64,             // Unix seconds UTC
    outcome_at:        Option<i64>,     // V1.1 — εμφανίζεται στο CTA παρακολούθησης ετυμηγορίας
                                        //   πριν την αποκάλυψη· καθρεφτίζει το QubEnvelope.outcome_at·
                                        //   δεσμεύεται στο qub_id μέσω της προεικόνας §4.1.
    drand_chain_id:    String,          // drand chain hash (hex string)
    drand_round:       u64,             // Target drand round number
    tlock_ciphertext:  Vec<u8>,         // tlock-encrypted QubEnvelope CBOR bytes
    recipient_pubkey:  Option<[u8; 32]>,// Reserved field; accepted by canonical CBOR
                                        //   but not interpreted by the v1 reference viewer
    title:             Option<String>,  // Plaintext title surfaced on the viewer
                                        //   countdown before reveal. Bound to qub_id
                                        //   via title_hash (§4.1). 1..=100 NFC code
                                        //   points, no control characters.
}

2.4 RevealedQub (Κατάσταση Εφαρμογής Θεατή)

Δεν σειριοποιείται σε 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>,       // V1.1 — μεταφέρεται από το QubEnvelope.outcome_at / SealedQub.outcome_at· τροφοδοτεί το μπλοκ παρακολούθησης ετυμηγορίας στη σελίδα αποκάλυψης (verdict-uplift-plan §5.1)
    drand_chain_id:      String,
    drand_round:         u64,
    sender_label:        Option<String>,
    title:               Option<String>,    // Carried forward from SealedQub.title
    reply_to:            Option<[u8; 32]>,
    body:                Vec<u8>,
    body_hash:           [u8; 32],
    body_hash_verified:  bool,
    author_signature:    Option<Vec<u8>>,
    author_pubkey:       Option<Vec<u8>>,
    signature_verified:  Option<bool>,
    cosigner_pubkey:     Option<Vec<u8>>,
    cosigner_signature:  Option<Vec<u8>>,
    cosigner_verified:   Option<bool>,
}

3. Προφίλ Κανονικού CBOR

Όλη η σειριοποίηση SealedQub και QubEnvelope ΠΡΕΠΕΙ να συμμορφώνεται με αυτό το προφίλ. Δύο υλοποιήσεις δοθείσας της ίδιας λογικής δομής ΠΡΕΠΕΙ να παράγουν πανομοιότυπα bytes.

3.1 Κανόνες Κωδικοποίησης

Κανόνας Προδιαγραφή
Πρότυπο RFC 8949 §4.2.1 (Core Deterministic Encoding Requirements)
Διάταξη κλειδιών χάρτη Ταξινόμηση κατά κωδικοποιημένο μήκος bytes πρώτα (μικρότερο πριν το μεγαλύτερο), μετά λεξικογραφικά (byte-προς-byte για κωδικοποιήσεις του ίδιου μήκους)
Κωδικοποίηση ακεραίων Συντομότερη μορφή: 0–23 στο αρχικό byte· 24–255 σε 2 bytes· 256–65535 σε 3 bytes· κ.λπ.
Κωδικοποίηση μήκους Μόνο καθορισμένα μήκη. Όχι πίνακες, χάρτες, συμβολοσειρές bytes ή συμβολοσειρές κειμένου αόριστου μήκους (επιπλέον πληροφορία = 31 απαγορεύεται).
Ετικέτες Όχι ετικέτες CBOR (κύριος τύπος 6 απαγορεύεται).
Κινητής υποδιαστολής Όχι floats (τιμές κύριου τύπου 7 0xF9–0xFB απαγορεύονται).
Συμβολοσειρές κειμένου Κωδικοποιημένες σε UTF-8, κανονικοποιημένες σε NFC (Unicode Normalization Form C).
Συμβολοσειρές bytes Ακατέργαστα bytes. Όχι κωδικοποίηση base64 στο επίπεδο CBOR.
Διπλά κλειδιά Απόρριψη με σφάλμα. Οι αναλυτές ΔΕΝ ΠΡΕΠΕΙ να αποδέχονται σιωπηρά διπλά κλειδιά χάρτη.
Απλές τιμές Επιτρέπονται μόνο true (0xF5), false (0xF4) και null (0xF6).
Προαιρετικά πεδία Τα απόντα προαιρετικά πεδία παραλείπονται εξ ολοκλήρου από τον χάρτη CBOR (δεν κωδικοποιούνται ως null). Τα παρόντα προαιρετικά πεδία περιλαμβάνονται με την ταξινομημένη σειρά κλειδιών.

3.2 Επαληθευμένες Κανονικές Σειρές Κλειδιών

Αυτές οι σειρές κλειδιών είναι κανονιστικές. Οι υλοποιήσεις ΠΡΕΠΕΙ να εκπέμπουν κλειδιά ακριβώς με αυτή τη σειρά. Οι ισχυρισμοί εντοπισμού σφαλμάτων ΘΑ ΕΠΡΕΠΕ να επαληθεύουν την ταξινόμηση σε μη-εκδόσεις παραγωγής.

QubEnvelope (έκδοση 0x01, μη υπογεγραμμένο, όλα τα προαιρετικά πεδία απόντα):

"body"                (5 encoded bytes)
"qub_id"              (7 encoded bytes)
"sig_alg"             (8 encoded bytes)
"version"             (8 encoded bytes)
"reply_to"            (9 encoded bytes)   ← only if present (reply chains)
"body_hash"           (10 encoded bytes)
"unlock_at"           (10 encoded bytes)
"created_at"          (11 encoded bytes)
"outcome_at"          (11 encoded bytes)  ← μόνο εάν είναι παρόν (μηχανισμός ετυμηγορίας V1.1)
"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 byte κεφαλίδα + μήκος συμβολοσειράς (για συμβολοσειρές κάτω των 24 bytes). Ταξινομήστε πρώτα κατά συνολικό κωδικοποιημένο μήκος, μετά λεξικογραφικά για κλειδιά ίδιου μήκους.

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)  ← μόνο εάν είναι παρόν (μηχανισμός ετυμηγορίας V1.1)
"visibility"        (11 encoded bytes)
"drand_round"       (12 encoded bytes)
"drand_chain_id"    (15 encoded bytes)
"recipient_pubkey"  (17 encoded bytes)  ← only if present
"tlock_ciphertext"  (17 encoded bytes)

PactTerms (σώμα συμφώνου, 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 Αναφορά Κωδικοποίησης Bytes

Τύπος Κωδικοποίηση CBOR Παράδειγμα
Κατακερματισμός SHA3-256 (32 bytes) 0x58 0x20 + 32 bytes body_hash, qub_id
Χρονοσημάνσεις (i64) Κύριος τύπος 0 (θετικός) ή 1 (αρνητικός), συντομότερη κωδικοποίηση Δευτερόλεπτα Unix
Έκδοση (u8, τιμή 1) 0x01 (ένα byte)
Τύπος περιεχομένου (u8, τιμή 1) 0x01 (ένα byte)
sig_alg (u8, τιμή 0) 0x00 (ένα byte)
Υπογραφή ML-DSA-65 (3.309 bytes) 0x59 0x0C 0xED + 3.309 bytes author_signature, cosigner_signature
Δημόσιο κλειδί ML-DSA-65 (1.952 bytes) 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 bytes. Ένα μοναδικό byte γεμίσματος 0x00 προστίθεται για να φτάσει τα 10 bytes για ευθυγράμμιση. Οι υλοποιήσεις ΠΡΕΠΕΙ να χρησιμοποιούν ακριβώς αυτά τα 10 bytes: [0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00].

Κωδικοποίηση outcome_at: Η V1.1 επέκτεινε την προεικόνα από 92 σε 100 bytes ώστε να ενσωματώσει το προαιρετικό πεδίο outcome_at στη δέσμευση. Το απόν outcome_at κωδικοποιείται ως 8 μηδενικά bytes· οι επικυρωτές του πρωτοκόλλου απορρίπτουν outcome_at <= 0 παντού, ώστε αυτή η ένδειξη να μην μπορεί να συγκρουστεί με νόμιμη τιμή. Βλέπε §3.2 (μορφότυπο σύρματος) και το ενσωματωμένο tasks/verdict-uplift-plan.md για τον μηχανισμό ετυμηγορίας που υποκινεί αυτό το πεδίο.

Κωδικοποίηση drand_round: Η V1.2 επέκτεινε την προεικόνα από 100 σε 108 bytes ώστε να ενσωματώσει το drand_round (τον γύρο-στόχο drand, §4.3) στη δέσμευση, και αναβάθμισε τον διαχωριστή πεδίου σε QUB_ID_V2. Αυτό δεσμεύει τον γύρο χρονοκλειδώματος στην ταυτότητα του qub: μια πύλη δεν μπορεί να επαναδεσμεύσει το κρυπτογραφημένο κείμενο σε διαφορετικό (π.χ. ήδη παρελθόντα) γύρο από αυτόν που υπονοεί το εμφανιζόμενο unlock_at. Η διαδικασία ξεκλειδώματος (§8) επιπλέον επαληθεύει ότι ο γύρος που είναι ψημένος μέσα στη στροφή (stanza) του κρυπτογραφημένου κειμένου 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 = ceil((unlock_at - chain_genesis_time) / chain_period_seconds)
Παράμετρος Πηγή Παράδειγμα
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

Η λειτουργία ceil() επιλέγει τον πρώτο γύρο drand του οποίου ο χρόνος αποκάλυψης είναι ≥ unlock_at. Αυτό διασφαλίζει ότι το qub δεν γίνεται αποκρυπτογραφήσιμο πριν τον επιλεγμένο χρόνο ξεκλειδώματος.

Οριακή περίπτωση: εάν το (unlock_at - chain_genesis_time) διαιρείται ακριβώς από το chain_period_seconds, το αποτέλεσμα είναι ακριβώς αυτός ο γύρος — το qub ξεκλειδώνει ακριβώς στον χρόνο αποκάλυψης αυτού του γύρου.

Επαλήθευση: το unlock_at ΠΡΕΠΕΙ να είναι στο μέλλον κατά τον χρόνο σφράγισης. Το unlock_at ΔΕΝ ΠΡΕΠΕΙ να είναι περισσότερο από 10 χρόνια από το created_at (για τον περιορισμό του κινδύνου εξάρτησης drand σε μακρύ ορίζοντα· η διεπαφή χρήστη ΘΑ ΕΠΡΕΠΕ να προειδοποιεί για ημερομηνίες ξεκλειδώματος πέρα από 2 χρόνια).


5. Newtypes Μορφότυπου Σύρματος

Τα newtypes μορφότυπου σύρματος παρέχουν ασφάλεια κατά τον χρόνο μεταγλώττισης ενάντια στη σύγχυση των bytes CBOR με JSON, ακατέργαστο απλό κείμενο ή άλλες κωδικοποιήσεις bytes.

Τύπος Περιέχει Παράγεται από Καταναλώνεται από
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 Σύμφωνο (διμερής συμφωνία, σώμα CBOR) 100 KB Το σώμα είναι κανονικό CBOR PactTerms (§6.1). Συν-υπογραφή ανά §9.7.
0x04 Ετυμηγορία (αυτο-αξιολόγηση δημιουργού, σώμα CBOR) 8 KB Το σώμα είναι κανονικό CBOR VerdictBody (§6.2). Εκπέμπεται μόνο από την πρόθεση verdict της πλευράς του συστήματος. Η γονική σχέση βρίσκεται στην ετικέτα Arweave Parent-Tx-Id, όχι στο σώμα. Βλέπε verdict-uplift-plan §3.4.

Οι θεατές ΠΡΕΠΕΙ να απορρίπτουν άγνωστους τύπους περιεχομένου με σαφές μήνυμα σφάλματος ορατό στον χρήστη. Οι θεατές ΔΕΝ ΠΡΕΠΕΙ να επιχειρούν να αποδώσουν άγνωστους τύπους ως κείμενο.

6.1 Σώμα Συμφώνου (content_type = 0x03)

Ένα σώμα συμφώνου είναι η κανονική κωδικοποίηση CBOR μιας τιμής PactTerms:

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

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

Οι κανονικές σειρές κλειδιών CBOR και για τους τρεις χάρτες δίνονται στην §3.2. Το συνολικό σειριοποιημένο CBOR του συμφώνου ΔΕΝ ΠΡΕΠΕΙ να υπερβαίνει τα 100 KB (ταιριάζει με την §6).

Διακριτής σχήματος. Η πρώτη γραμμή στα terms για ένα σύμφωνο structured/v1 ΠΡΕΠΕΙ να είναι { key: "pact_schema", value: "structured/v1" }. Οι γραμμές χωρίς αυτό το σήμα είναι "προσαρμοσμένα" σύμφωνα και δεν λαμβάνουν δομημένη επικύρωση ή απόδοση που γνωρίζει το σχήμα.

Παγωμένες υποδοχές αναγνώρισης. Τα σύμφωνα structured/v1 φέρουν ακριβώς τέσσερις γραμμές αναγνώρισης κάτω από αυτά τα κλειδιά:

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

Το value για κάθε μία είναι μία από οκτώ παγωμένες αγγλικές συμβολοσειρές που επιλέγονται από το ζεύγος (role, kind), όπου role ∈ { seller, buyer, provider, client } και kind ∈ { standard, capacity }. Οι ίδιες οι συμβολοσειρές είναι κανονιστικά δεδομένα πρωτοκόλλου — οι υπογραφές ML-DSA-65 και των δύο μερών δεσμεύονται στα ακριβή bytes μέσω του body_hash. ΔΕΝ είναι τοπικοποιημένες· το υπογεγραμμένο σώμα είναι γλωσσικά ουδέτερο. Οποιαδήποτε αλλαγή διατύπωσης απαιτεί νέα έκδοση σχήματος (structured/v2).

Οι οκτώ συμβολοσειρές, η αναζήτησή τους (acknowledgement_for(role, kind)) και η λογική για κάθε μία είναι καρφιτσωμένες από την υλοποίηση αναφοράς. Οι συμμορφούμενες υλοποιήσεις ΠΡΕΠΕΙ να εκπέμπουν byte-πανομοιότυπες τιμές αναγνώρισης· τα golden-fixture τεστ SHA3-256 body-hash που καλύπτουν και τους τέσσερις συνδυασμούς ρόλων εντοπίζουν οποιαδήποτε απόκλιση.

Σειρά εμφάνισης θεατή. Οι συμβολοσειρές αναγνώρισης περιέχουν φράσεις όπως "described above", οι οποίες προϋποθέτουν ότι οι γραμμές περιγραφής / εμβέλειας αποδίδονται πριν τις αναγνωρίσεις. Οι θεατές ΠΡΕΠΕΙ να αποδίδουν τον πίνακα terms με τη σειρά CBOR· η αναδιάταξη σπάει τη σημασιολογία του πεζού λόγου.

Επικοινωνία αντισυμβαλλόμενου. Όταν το contact του Party B είναι μια έγκυρη διεύθυνση email, η υπηρεσία ανεβάσματος qub αποστέλλει αυτόματα ένα email πρόσκλησης ανασκόπησης / συν-υπογραφής κατά τον χρόνο προετοιμασίας και δεσμεύει την τελική συν-υπογραφή στην επαλήθευση αυτής της ίδιας διεύθυνσης (§9.7). Τα σύμφωνα των οποίων η επικοινωνία Party B απουσιάζει μπορούν ακόμα να συν-υπογραφούν, αλλά μόνο μέσω εξωτερικού καναλιού — η υπηρεσία αρνείται αιτήματα συν-υπογραφής που δεν μπορούν να παράγουν ταιριαστό δείκτη επαλήθευσης email 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 (ταιριάζει με τη γραμμή του μητρώου παραπάνω).

Απαρίθμηση έκβασης. Το byte του πρωτοκόλλου είναι ουδέτερο ως προς την πρόθεση· οι τέσσερις επιλογές Right / Partial / Wrong / Unfalsifiable καλύπτουν τον χώρο εκβάσεων κάθε πρόθεσης που φέρει ετυμηγορία. Οι ετικέτες ανά πρόθεση («Το προέβλεψα σωστά» / «Την τήρησα» / «Παραδόθηκε» / «Επιβεβαιώθηκε» για το Right, κ.ο.κ.) είναι ζήτημα απόδοσης από την πλευρά του θεατή, που επιλύεται έναντι της πρόθεσης του γονικού qub — το πρωτόκολλο παραμένει ουδέτερο ως προς τη γλώσσα και την πρόθεση. Τιμές εκτός του 1..=4 ΠΡΕΠΕΙ να απορρίπτονται κατά την αποκωδικοποίηση.

Γονική σύνδεση. Ένα qub ετυμηγορίας ΔΕΝ μεταφέρει τη γονική αναφορά στο σώμα του. Το αναγνωριστικό συναλλαγής Arweave του γονικού qub εκπέμπεται ως η ετικέτα αποθήκευσης Parent-Tx-Id κατά τον χρόνο ανεβάσματος (§7, επίπεδο ετικετών αποθήκευσης). Αυτό κρατά το σώμα ως αυτοτελή υπογεγραμμένη δήλωση αυτο-αξιολόγησης· η αλυσίδα ελέγχου («σωστά για το τι;») θεμελιώνεται μέσω της αναζήτησης ετικετών Arweave.

Ασφάλεια του URL τεκμηρίωσης (κανονιστική). Όταν το evidence_url είναι παρόν, οι επικυρωτές (πλευρά σύνθεσης, πλευρά πρωτοκόλλου, άκρο Worker) ΠΡΕΠΕΙ να επιβάλλουν:

  1. Μόνο HTTPS. Η συμβολοσειρά ΠΡΕΠΕΙ να ξεκινά με την ακολουθία bytes https://. Κάθε άλλο σχήμα — http, ftp, javascript, data, file κ.λπ. — απορρίπτεται.
  2. Ανώτατο όριο μήκους. ≤ 2.048 bytes (πρακτικό όριο URL περιηγητή).
  3. Έλεγχος NFC + εχθρικών κωδικών. Ο ίδιος κανόνας με το title και το reflection — κωδικοί παράκαμψης κατεύθυνσης (bidi) / μηδενικού πλάτους / μπλοκ ετικετών / BOM / C0 / C1 απορρίπτονται. Ο ορισμός ταιριάζει με το Rust crate::handle::contains_hostile_text_codepoint και το TS workers/api/src/utils/unicode.ts::isHostileCodepoint (διατηρούνται σε βηματισμό).
  4. Χωρίς κενά, χωρίς χαρακτήρες ελέγχου ASCII. Κενά / DEL / bytes κάτω από 0x20 οπουδήποτε στο URL απορρίπτονται — κλείνει τον φορέα έγχυσης \n/\t που ο κανόνας bidi δεν καλύπτει.
  5. Μη κενό τμήμα κεντρικού υπολογιστή. Όλα όσα βρίσκονται μεταξύ του https:// και του πρώτου /, ? ή # ΠΡΕΠΕΙ να είναι μη κενά.

Καμία ανάκτηση από την πλευρά του διακομιστή. Ο Worker ΔΕΝ ΠΡΕΠΕΙ να διαμεσολαβεί, να ανακτά ή να προεπισκοπεί το URL. Το πρωτόκολλο αποθηκεύει μια συμβολοσειρά· η απόδοση συμβαίνει από την πλευρά του θεατή με rel="nofollow noopener noreferrer" target="_blank" και ορατό όνομα κεντρικού υπολογιστή που εμφανίζεται δίπλα στο κείμενο του συνδέσμου.

Αναστοχασμός. Προαιρετικό κείμενο αναστοχασμού γραμμένο από τον δημιουργό («τι άλλαξε, τι μάθατε»). Ίδια επικύρωση NFC + εχθρικών κωδικών με το title. Κενή είσοδος ή είσοδος μόνο με κενά συμπτύσσεται σε απουσία κατά τον χρόνο κατασκευής.

Έκδοση σχήματος. Η v1 υποστηρίζει μόνο verdict_version = 0x01. Μελλοντικές αναθεωρήσεις σχήματος αυξάνουν αυτό το byte και προσγειώνονται μαζί με μια νέα έκδοση πρωτοκόλλου ανά §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.
 3. Compute body_hash = SHA3-256(body).
 4. Set created_at = current Unix seconds UTC.
 5. Select drand chain. Load chain_genesis_time and chain_period_seconds, and
    compute drand_round = ceil((unlock_at - chain_genesis_time) / chain_period_seconds).
    (Computed here, before qub_id, because drand_round is bound into the qub_id
    preimage — §4.1, V1.2.)
 6. Compute qub_id (see §4.1), folding in drand_round from step 5.
 7. Construct QubEnvelope with all fields.
 8. Serialise QubEnvelope using canonical CBOR → bytes B.
    Assert: serialised output matches canonical profile (§3).
 9. Compute C = tlock_encrypt(B, drand_round, drand_chain_public_key).
10. Construct SealedQub with tlock_ciphertext = C, and matching qub_id, version,
    unlock_at, drand_chain_id, drand_round.
12. Serialise SealedQub using canonical CBOR → SealedQubCbor.
12a. Generate K = 32 random bytes (CSPRNG) and N = 12 random bytes (CSPRNG).
     Compute W = wrap_sealed_qub(SealedQubCbor, qub_id=qub_id, key=K, nonce=N)
     per §13. The bytes uploaded to permanent storage are the OuterWrapper CBOR W,
     never the bare SealedQubCbor. K leaves the device only as the URL
     fragment in step 16.
13. Display seal-time disclosure. User confirms.
14. Validate upload eligibility via the qub upload service (bot-detection, entitlement, rate limits).
15. Submit W (the OuterWrapper bytes) to the qub upload service; the service
    signs and uploads to permanent storage. The service is byte-blind to the inner
    SealedQubCbor and never receives K.
16. Receive arweave_tx_id from the service. Construct delivery URL as
    `<origin>/c/<arweave_tx_id>#<base64url(K)>` (or `<origin>/s/<short_code>#<base64url(K)>`
    when a short code is allocated). Browsers do not transmit URL fragments
    to servers, so K is never observed by qub.social or any storage gateway.

Επίπεδο ετικετών αποθήκευσης (εκτός ζώνης). Η υπηρεσία ανεβάσματος qub επισυνάπτει ένα σκόπιμα μικρό σύνολο ετικετών συναλλαγών αποθήκευσης μαζί με το περιτυλιγμένο ωφέλιμο φορτίο. Το Content-Type=application/octet-stream απαιτείται κανονιστικά. Η υπηρεσία αναφοράς επιπλέον επισυνάπτει τρεις προαιρετικές ετικέτες όταν ο δημιουργός επιλέγει να τις εμφανίσει: Intent (πρόθεση σύνθεσης επικυρωμένη με allowlist — π.χ., quote, reply, commitment), Author (αποτύπωμα δημόσιου κλειδιού του δημιουργού §9.3 ως 64-χαρακτήρων μικρών γραμμάτων hex) και Parent-Tx-Id (αναγνωριστικό συναλλαγής αποθήκευσης του γονικού qub για αλυσίδες απάντησης, 43-χαρακτήρων base64url).

Η ετικέτα Author είναι opt-in ανά qub: η εφαρμογή δημιουργού αναφοράς την επισυνάπτει μόνο όταν ο χρήστης ενεργοποιεί ρητά τη δημόσια απόδοση κατά τον χρόνο σφράγισης. Όταν ο διακόπτης είναι απενεργοποιημένος — η προεπιλογή — δεν γράφεται ετικέτα Author και το qub είναι μη-αποδιδόμενο στην αλυσίδα: τίποτα στη μόνιμη αποθήκευση δεν συνδέει το ανέβασμα με το handle ενός δημιουργού, email ή άλλα qubs. Όταν ο διακόπτης είναι ενεργοποιημένος, το αποτύπωμα Author αναλύεται στο επιλεγμένο @handle του δημιουργού μέσω της αλυσίδας πιστοποίησης §9.5. Οι σχέσεις αλυσίδας απάντησης και το Intent δεν είναι αναγνωριστικά. Το εξωτερικό περιτύλιγμα (§13) προστατεύει το εσωτερικό σώμα από τη συσχέτιση κρυπτογραφημένου κειμένου — αποτρέποντας έναν συλλέκτη από το να αναγνωρίσει και να αποκρυπτογραφήσει μαζικά ανεβάσματα σχήματος qub αφού δημοσιευθεί ο γύρος drand τους.

Η υπηρεσία αναφοράς σκόπιμα ΔΕΝ επισυνάπτει ετικέτες App-Name, App-Version ή Type: οποιοδήποτε τέτοιο φίλτρο μονής τιμής θα επέστρεφε το σύνολο του σώματος qubs σε ένα ερώτημα GraphQL, το οποίο είναι ασύμβατο με την εμβέλεια εμπιστευτικότητας μόνο-σώματος του περιτυλίγματος.

Ένας συμμορφούμενος επαληθευτής ΔΕΝ ΠΡΕΠΕΙ να εξαρτάται από οποιαδήποτε ετικέτα αποθήκευσης για την §11 επαλήθευση τρίτου μέρους· ο κατακερματισμός σώματος / qub_id / υπογραφή δεσμεύονται μόνο στο εσωτερικό CBOR, ποτέ στο σύνολο ετικετών.


8. Πρωτόκολλο Ξεκλειδώματος

Η πλήρης ακολουθία ξεκλειδώματος. Κάθε βήμα είναι κανονιστικό.

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

9. Υπογραφή Συγγραφικής Πατρότητας

9.1 Λογική

Τα qubs αποθηκεύονται σε μόνιμη αποθήκευση. Οι υπογραφές συγγραφής πρέπει να παραμείνουν μη-πλαστογραφήσιμες επ' αόριστον, και γι' αυτό η v1.0 χρησιμοποιεί το μετα-κβαντικό σχήμα ML-DSA-65 (FIPS 204) και όχι ένα κλασικό σχήμα του οποίου η ασφάλεια μπορεί να υποβαθμιστεί εντός της μόνιμης διάρκειας ζωής του qub.

9.2 Μητρώο Αλγορίθμων

sig_alg Σχήμα Μέγεθος Κλειδιού Μέγεθος Υπογραφής
0x00 Χωρίς υπογραφή (μη υπογεγραμμένο)
0x01 ML-DSA-65 (FIPS 204) 1.952 bytes 3.309 bytes

Οι θεατές ΠΡΕΠΕΙ να απορρίπτουν άγνωστες τιμές sig_alg.

9.3 Κατασκευή Υπογεγραμμένης Προεικόνας

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

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

signature = Sign(author_secret_key, sig_input)

Διαχωριστής πεδίου: το "QUB_AUTHOR_SIG_V1" είναι 17 ASCII bytes: [0x51, 0x55, 0x42, 0x5F, 0x41, 0x55, 0x54, 0x48, 0x4F, 0x52, 0x5F, 0x53, 0x49, 0x47, 0x5F, 0x56, 0x31]. Χωρίς γέμισμα.

Τελευταίο byte: το 91ο byte της προεικόνας ΠΡΕΠΕΙ να είναι 0x00. Η υλοποίηση αναφοράς εκθέτει αυτό ως τη σταθερά ORG_ID_PRESENT_INDIVIDUAL = 0x00 στο crates/qub-core/src/signing.rs· οι θεατές που ανακατασκευάζουν το sig_input για επαλήθευση ΠΡΕΠΕΙ να εκπέμπουν το ίδιο byte.

Εμβέλεια υπογραφής — τι καλύπτεται και τι όχι. Το sig_input δεσμεύεται σε τέσσερα πεδία του φακέλου: version, qub_id, body_hash, unlock_at (συν τον σταθερό διαχωριστή πεδίου και το byte 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 Άμεση είσοδος
content_type Μεταβατικά, μέσω της προεικόνας qub_id
created_at Μεταβατικά, μέσω της προεικόνας qub_id
outcome_at Μεταβατικά, μέσω της προεικόνας qub_id
drand_round Μεταβατικά, μέσω της προεικόνας qub_id (V1.2)
body Μεταβατικά, μέσω του body_hash = SHA3-256(body)
author_pubkey — (έμμεσα) Το κλειδί που επαλήθευσε την υπογραφή είναι ο συγγραφέας, εξ ορισμού
sender_label Κείμενο μόνο για εμφάνιση· μεταβλητό χωρίς σπάσιμο υπογραφής
reply_to Δείκτης νηματοποίησης· μεταβλητό χωρίς σπάσιμο υπογραφής
cosigner_pubkey / cosigner_signature Υπογράφονται ανεξάρτητα πάνω στο ίδιο sig_input (βλέπε §9.7)
drand_chain_id, tlock_ciphertext, visibility Εξωτερικά πεδία του SealedQub, όχι μέσα στον φάκελο — καλύπτονται από τις δικές τους δομικές αναλλοιότητες (συνέπεια γύρου / αλυσίδας) αλλά όχι από την υπογραφή του συγγραφέα. (Το drand_round δεσμεύεται πλέον μεταβατικά μέσω της προεικόνας qub_id — βλέπε παραπάνω.)

Επιπτώσεις ασφαλείας μη-πιστοποιημένων πεδίων.

Οι υλοποιήσεις που εμφανίζουν 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 (same formula as §9.3).
6. Verify(author_pubkey, sig_input, author_signature).
7. If verification succeeds → display "signed by [key fingerprint]."
8. If verification fails → display "signature verification failed."

Η επαλήθευση υπογραφής είναι η πιο δαπανηρή λειτουργία (ειδικά το ML-DSA-65). ΘΑ ΕΠΡΕΠΕ να εκτελείται αφού έχουν περάσει όλοι οι φθηνότεροι έλεγχοι (κατακερματισμός, qub_id, unlock_at).

9.5 Πιστοποιήσεις Ταυτότητας

Οι πιστοποιήσεις ταυτότητας — η αντιστοίχιση του author_pubkey σε αξιώσεις ταυτότητας αναγνωρίσιμες από ανθρώπους όπως ένα handle qub, διεύθυνση email, κοινωνικό handle ή διαπιστευτήριο passkey — είναι μια προοδευτική ενίσχυση από την πλευρά του θεατή και δεν απαιτούνται για την επαλήθευση της υπογραφής. Οι θεατές που αναλύουν πιστοποιήσεις σε ταυτότητα εμφάνισης ΠΡΕΠΕΙ να εφαρμόζουν την προτεραιότητα:

handle > email > social > fingerprint

Το εφεδρικό αποτύπωμα είναι το hex μικρών γραμμάτων του SHA3-256(author_pubkey)· είναι πάντα διαθέσιμο για οποιοδήποτε υπογεγραμμένο qub. Οι θεατές ΜΠΟΡΟΥΝ να το συντομεύουν για εμφάνιση — ο θεατής αναφοράς αποδίδει qub: ακολουθούμενο από τα πρώτα και τα τελευταία τέσσερα bytes (qub:<8 hex>…<8 hex>).

Ένας συμμορφούμενος επαληθευτής μπορεί να ολοκληρώσει κάθε έλεγχο της §9.4 χωρίς να επικοινωνήσει με το API του qub, χωρίς κανένα δίκτυο πέραν της μόνιμης αποθήκευσης και drand και χωρίς αναζήτηση από την πλευρά του διακομιστή. Η ανάλυση πιστοποίησης είναι ένα ξεχωριστό βήμα μέγιστης προσπάθειας που εκτελείται μόνο μετά την επιτυχή επαλήθευση της υπογραφής.

9.6 Επίπτωση Μεγέθους

Ed25519 ML-DSA-65
Υπογραφή 64 bytes 3.309 bytes
Δημόσιο κλειδί 32 bytes 1.952 bytes
Σύνολο ανά qub 96 bytes 5.261 bytes
Επίπτωση κόστους αποθήκευσης (στα ~$5/MB) ~$0,0005 ~$0,026

Για ένα qub κειμένου 500–2.000 bytes, το ML-DSA-65 περίπου τριπλασιάζει το αποθηκευμένο μέγεθος. Το απόλυτο κόστος είναι αμελητέο.

9.7 Επαλήθευση Συν-υπογραφέα (Διμερείς Συμφωνίες Συμφώνου)

Για διμερείς συμφωνίες (content_type = 0x03), ένα δεύτερο επίπεδο υπογραφής αποδεικνύει ότι και τα δύο μέρη συναίνεσαν στους ίδιους όρους.

Πεδία φακέλου:

Και τα δύο πεδία ΠΡΕΠΕΙ να είναι παρόντα μαζί ή και τα δύο απόντα. Εάν ακριβώς ένα είναι παρόν, οι θεατές ΠΡΕΠΕΙ να αναφέρουν σφάλμα ακεραιότητας.

Διαδικασία επαλήθευσης:

1. If cosigner_pubkey absent and cosigner_signature absent → no cosigner. Done.
2. If exactly one is present → integrity error.
3. Verify cosigner_pubkey != author_pubkey (prevent self-cosigning).
   Fail → display "cosigner pubkey must differ from author."
4. Reconstruct sig_input using the same formula as §9.3.
5. Verify(cosigner_pubkey, sig_input, cosigner_signature).
6. Success → display "co-signed by [cosigner fingerprint]."
7. Failure → display "co-signature verification failed."

Ιδιότητες:

Πύλη δέσμευσης email (επιχειρησιακή). Όταν ένα προετοιμασμένο σύμφωνο φέρει επικοινωνία email του Party B (§6.1), η υπηρεσία ανεβάσματος qub ΠΡΕΠΕΙ να αρνηθεί το αίτημα συν-υπογραφής εκτός αν υπάρχει βραχύβιος δείκτης επαλήθευσης email που ταιριάζει τόσο το staging id όσο και τον κατακερματισμό κανονικοποιημένου-email αυτής της επικοινωνίας. Ο δείκτης γράφεται από το /api/v1/auth/verify όταν το token magic-link φέρει 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 λεπτά) μετά την έκδοση. Αυτή είναι μια επιχειρησιακή πύλη anti-πλαστοπροσωπίας, ΟΧΙ μέρος της απόδειξης qub on-chain — ένας τρίτος επαληθευτής που επαναλαμβάνει την §11 χρειάζεται μόνο μόνιμη αποθήκευση και drand, χωρίς αναζήτηση από την πλευρά του διακομιστή. Ο δείκτης υπάρχει μόνο από την πλευρά του διακομιστή και δεν είναι ποτέ μέρος του υπογεγραμμένου σώματος.

Επίπτωση μεγέθους (συγγραφέας ML-DSA-65 + συν-υπογραφέας):

Συστατικό Μέγεθος
Υπογραφή συγγραφέα 3.309 bytes
Δημόσιο κλειδί συγγραφέα 1.952 bytes
Υπογραφή συν-υπογραφέα 3.309 bytes
Δημόσιο κλειδί συν-υπογραφέα 1.952 bytes
Συνολικό κρυπτογραφικό overhead 10.522 bytes
Επίπτωση κόστους αποθήκευσης ~$0,05

10. Απόδοση και Καθαρισμός Markdown

Αυτή η ενότητα είναι κρίσιμη για την ασφάλεια. Ο θεατής αποδίδει qubs κειμένου (content_type = 0x01) χρησιμοποιώντας ένα περιορισμένο υποσύνολο Markdown.

10.1 Επιτρεπόμενα Στοιχεία

10.2 Απαγορευμένα Στοιχεία

Στοιχείο Χειρισμός
Ακατέργαστο HTML (<div>, <script>, κ.λπ.) Αφαιρείται εξ ολοκλήρου. Κανένα HTML δεν περνά.
Εικόνες (![alt](url)) Αφαιρούνται. Η σύνταξη εικόνας αφαιρείται από την έξοδο.
Σύνδεσμοι ([text](url)) Η διεύθυνση URL αποδίδεται ως ορατό απλό κείμενο. Δεν συνδέεται αυτόματα. Δεν είναι κλικαρίσιμη χωρίς ρητή ενέργεια του χρήστη.
Επικίνδυνα σχήματα URL javascript:, data:, vbscript:, file: — αφαιρούνται.
Iframes, embeds, objects Αφαιρούνται.
Οντότητες HTML Αποκωδικοποιούνται σε χαρακτήρες εμφάνισης μόνο εάν είναι ασφαλείς.

10.3 Υλοποίηση

Οι υλοποιήσεις ΠΡΕΠΕΙ να χρησιμοποιούν έναν αυστηρό αναλυτή allowlist, όχι blocklist. Η προτεινόμενη προσέγγιση:

  1. Ανάλυση Markdown με χρήση pulldown-cmark (ή ισοδύναμου).
  2. Διάσχιση του AST και απόρριψη οποιουδήποτε κόμβου δεν είναι στο allowlist (§10.1).
  3. Για κόμβους συνδέσμων: εκπομπή της διεύθυνσης URL ως ορατό κείμενο, όχι ως κλικαρίσιμο στοιχείο <a>.
  4. Μετατροπή του φιλτραρισμένου AST σε τυποποιημένη ενδιάμεση αναπαράσταση (π.χ., enum MarkdownNode με μόνο ασφαλείς παραλλαγές). Το ακατέργαστο HTML είναι δομικά μη αναπαραστάσιμο σε αυτό το IR.
  5. Απόδοση από το τυποποιημένο IR στο επίπεδο προβολής στόχου (π.χ., αντιδραστικά στοιχεία προβολής, κόμβοι DOM). Καμία συνένωση συμβολοσειρών HTML ή innerHTML σε κανένα σημείο.

Οι προσεγγίσεις blocklist είναι εύθραυστες επειδή νέες επεκτάσεις Markdown ή ιδιοτροπίες αναλυτή μπορούν να εισάγουν μη-φιλτραρισμένα στοιχεία. Η προσέγγιση τυποποιημένου AST κάνει το XSS δομικά αδύνατο — δεν υπάρχει παραλλαγή που μπορεί να φέρει αυθαίρετο HTML.

10.4 Όρια Μεγέθους και Δομής


11. Επαλήθευση Τρίτου Μέρους

Οποιοδήποτε τρίτο μέρος μπορεί να επαληθεύσει ένα δημόσιο qub χωρίς τη συνεργασία του qub. Η διαδικασία επαλήθευσης:

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

Τι αποδεικνύει η επαλήθευση:

Απόδειξη Τι καθιερώνει
Δέσμευση Το κρυπτογραφημένο κείμενο υπήρχε κατά τη χρονοσήμανση του μπλοκ αποθήκευσης.
Ακεραιότητα Το σώμα απλού κειμένου ταιριάζει με τον δεσμευμένο κατακερματισμό και δεν έχει αλλοιωθεί.
Χρονισμός Το περιεχόμενο ήταν μη αναγνώσιμο μέχρι τον γύρο drand, ο οποίος αντιστοιχεί στον επιλεγμένο χρόνο ξεκλειδώματος (υπό τις παραδοχές ασφαλείας tlock και drand).

Τι ΔΕΝ αποδεικνύει η επαλήθευση:

Μη-απόδειξη Γιατί
Συγγραφή Το sender_label είναι διακοσμητικό. Χωρίς sig_alg0x01, οποιοσδήποτε θα μπορούσε να σφραγίσει αυτό το περιεχόμενο.
Πρόθεση Το qub αποδεικνύει περιεχόμενο και χρονισμό, όχι τι σήμαινε υποκειμενικά ο δημιουργός.
Χρονισμός προ-γεγονότος Η συμπερίληψη μπλοκ αποθήκευσης μπορεί να καθυστερήσει σε σχέση με το πραγματικό ανέβασμα κατά λεπτά. Η χρονοσήμανση δέσμευσης είναι ο χρόνος μπλοκ, όχι η στιγμή που ο χρήστης πάτησε «σφράγιση».

12. Διαχείριση Εκδόσεων

12.1 Έκδοση Πρωτοκόλλου

Το πεδίο version (u8) και στο SealedQub και στο QubEnvelope αναγνωρίζει την κύρια έκδοση του πρωτοκόλλου.

12.2 Ιστορικό Εκδόσεων

Έκδοση Τιμή Περιγραφή
v1 0x01 Δημόσια qubs κειμένου (content_type 0x01), διμερείς συμφωνίες συμφώνου (0x03, σχήμα structured/v1, συγγραφέας + συν-υπογραφέας ML-DSA-65), tlock, SHA3-256

12.3 Συμβατότητα προς τα εμπρός

Ένας θεατής v1 που συναντά QubEnvelope με άγνωστα προαιρετικά κλειδιά χάρτη CBOR (κλειδιά που δεν είναι στην κανονική σειρά §3.2) ΘΑ ΕΠΡΕΠΕ να αγνοεί αυτά τα κλειδιά και να προχωρά στην επαλήθευση χρησιμοποιώντας τα γνωστά πεδία. Αυτό επιτρέπει μελλοντικές μικρές προσθήκες (π.χ., νέα μεταδεδομένα) χωρίς να απαιτείται αύξηση κύριας έκδοσης.

Ένας θεατής v1 που συναντά sig_alg = 0x01 (ML-DSA-65) αλλά χωρίς υποστήριξη επαλήθευσης ML-DSA-65 ΘΑ ΕΠΡΕΠΕ να εμφανίσει το περιεχόμενο του qub με μια ειδοποίηση "η υπογραφή υπάρχει αλλά δεν είναι επαληθεύσιμη", και όχι να απορρίψει το qub εξ ολοκλήρου. Η σημερινή υλοποίηση αναφοράς απορρίπτει κάθε τιμή sig_alg εκτός από 0x00 και 0x01 επειδή το μητρώο v1 δεν περιέχει άλλο έγκυρο αλγόριθμο — η αυστηρή απόρριψη και το soft-fail είναι παρατηρητικά ταυτόσημα μέχρι να εγγραφεί τρίτος αλγόριθμος. Η παραπάνω συμπεριφορά soft-fail γίνεται κρίσιμη μόλις η §9.2 δεχθεί νέα εγγραφή, και ο θεατής αναφοράς θα ενημερωθεί ώστε να κάνει soft-fail σε εκείνο το σημείο.

12.4 Έκδοση Εξωτερικού Περιτυλίγματος

Το OuterWrapper που περιγράφεται στην §13 φέρει το δικό του byte version, ανεξάρτητο από τα SealedQub.version και QubEnvelope.version. Οι δύο χώροι έκδοσης εξελίσσονται ξεχωριστά: μια μελλοντική μετα-κβαντικά ασφαλής συμμετρική αντικατάσταση αυξάνει το byte περιτυλίγματος χωρίς να αγγίξει την εσωτερική έκδοση πρωτοκόλλου, και μια μελλοντική προσθήκη στο επίπεδο πρωτοκόλλου (π.χ., νέο πεδίο φακέλου) αυξάνει την εσωτερική έκδοση χωρίς να αγγίξει το byte περιτυλίγματος.

OUTER_WRAPPER_VERSION_* Τιμή Αλγόριθμος Κατάσταση
OUTER_WRAPPER_VERSION_1 0x01 AES-256-GCM με 12-byte nonce, 16-byte ετικέτα πιστοποίησης, AAD δεσμευμένο στο qub_id Προεπιλογή v1
0x020xFF Δεσμευμένο Μελλοντικό

Οι θεατές ΠΡΕΠΕΙ να απορρίπτουν άγνωστες εκδόσεις περιτυλίγματος με σαφές σφάλμα. Το πρωτόκολλο σκόπιμα διατηρεί τον χώρο εκδόσεων περιτυλίγματος στενό μέχρι να εμφανιστεί συγκεκριμένος οδηγός μετάβασης (π.χ., οδηγία NIST που ευνοεί διαφορετικό AEAD)· μια θέση 0x02 θα κατανεμηθεί στην ίδια αναθεώρηση που εισάγει τον αλγόριθμο.


13. Εξωτερικό Περιτύλιγμα Κρυπτογράφησης

13.1 Λογική

Τα επίπεδα πρωτοκόλλου (QubEnvelope → tlock → SealedQub) κάνουν ένα σφραγισμένο qub χρονοκλειδωμένο: το σώμα είναι μη αναγνώσιμο μέχρι το unlock_at και τη δημοσίευση της υπογραφής γύρου drand. Μετά το ξεκλείδωμα, ωστόσο, η υπογραφή γύρου είναι δημόσια και το κανονικό σχήμα CBOR του SealedQub είναι αναγνωρίσιμο, οπότε ένας συλλέκτης που έχει ευρετηριάσει συναλλαγές μόνιμης αποθήκευσης θα μπορούσε να αποκρυπτογραφήσει μαζικά ολόκληρο το σώμα qubs.

Το εξωτερικό περιτύλιγμα κρυπτογράφησης κλείνει αυτό το κανάλι παρεμβάλλοντας ένα επιπλέον συμμετρικό επίπεδο AEAD μεταξύ του κανονικού SealedQubCbor και των bytes που εγγράφονται στη μόνιμη αποθήκευση. Το κλειδί 256-bit K ζει μόνο στο τμήμα fragment της URL παράδοσης και στις συσκευές των χρηστών· οι περιηγητές δεν μεταδίδουν τμήματα URL fragment σε διακομιστές, οπότε το qub.social, κάθε πύλη αποθήκευσης και κάθε CDN μπροστά από οποιοδήποτε από αυτά είναι παρατηρητικά τυφλά στο K. Κάθε qub στη μόνιμη αποθήκευση είναι επομένως ένα αδιαφανές κρυπτογραφημένο κείμενο του οποίου το απλό κείμενο είναι μη ανακτήσιμο χωρίς την URL που ο δημιουργός επέλεξε να μοιραστεί.

Καθαρό αποτέλεσμα:

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
  ↓ AES-256-GCM(K, nonce, AAD=qub_id) (§7 step 12a, this section)
OuterWrapper CBOR bytes              ← uploaded to permanent storage (§7 step 15)

Η σφράγιση και το ξεκλείδωμα στο επίπεδο του πρωτοκόλλου (§7, §8) δεν αλλάζουν κάτω από το όριο του περιτυλίγματος· το περιτύλιγμα προσαρμόζεται στη θέση κλήσης του seal() και αποσπάται στη θέση κλήσης του unlock().

13.3 Δομή Δεδομένων OuterWrapper

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

Αναλλοίωτα πεδίων.

Κωδικοποίηση CBOR. Κανονικό CBOR ανά §3, με τον ίδιο κανόνα ταξινόμησης κλειδιών (ταξινόμηση κατά κωδικοποιημένο μήκος bytes σε αύξουσα σειρά, μετά λεξικογραφικά). Τα τέσσερα κλειδιά είναι:

Κλειδί Κωδικοποιημένα bytes Σειρά
nonce 6 1
qub_id 7 2
version 8 3
ciphertext 11 4

Το πρώτο byte του CBOR OuterWrapper είναι επομένως η κεφαλίδα χάρτη καθορισμένου μήκους για χάρτη 4 εισόδων (0xA4).

13.4 Δέσμευση AAD στο qub_id

Το περιτύλιγμα δεσμεύει το qub_id ως πρόσθετα πιστοποιημένα δεδομένα AEAD. Αυτή είναι η κρίσιμη δομική άμυνα έναντι τριών κατηγοριών επίθεσης:

Επίθεση Άμυνα
Μετακίνηση κρυπτογραφημένου κειμένου κάτω από διαφορετικό πεδίο qub_id στο περιτύλιγμα Αναντιστοιχία AAD → αποτυγχάνει η πιστοποίηση AEAD
Ανάμιξη του τμήματος URL του qub A με τα bytes μόνιμης αποθήκευσης του qub B Αναντιστοιχία AAD → αποτυγχάνει η πιστοποίηση AEAD
Αλλοίωση του πεδίου qub_id του περιτυλίγματος μετά το ανέβασμα Αναντιστοιχία AAD → αποτυγχάνει η πιστοποίηση AEAD

Η μεταφορά του qub_id στο απλό κείμενο του περιτυλίγματος δεν αποδυναμώνει ουσιαστικά την ανοσία απαρίθμησης — το qub_id το ίδιο είναι κατακερματισμός SHA3-256 της προεικόνας §4.1 χωρίς ανακτήσιμη προεικόνα από τη σύνοψη, και ένας απαριθμητής που έχει ήδη συλλέξει τα bytes του περιτυλίγματος δεν μαθαίνει τίποτα από το ορατό 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
    C := AES_256_GCM_encrypt(key=K, nonce=N, msg=S, aad=Q)
    // C includes the 16-byte authentication tag at the end
    return canonical_cbor_encode(OuterWrapper{
        version:    0x01,
        qub_id:     Q,
        nonce:      N,
        ciphertext: C,
    })

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

Συμπύκνωση τρόπου αποτυχίας. Λάθος K, λάθος nonce, αναντιστοιχία AAD και αλλοιωμένο κρυπτογραφημένο κείμενο όλα παράγουν το ίδιο σφάλμα DECRYPT_FAILED. Αυτή είναι σκόπιμη ιδιότητα AEAD: η διάκριση του τρόπου αποτυχίας θα δημιουργούσε ένα πλευρικό κανάλι που ένας απομακρυσμένος επιτιθέμενος θα μπορούσε να εξετάσει στέλνοντας κακοδιαμορφωμένα περιτυλίγματα και χρονομετρώντας την απάντηση. Οι υλοποιήσεις αναφοράς ΠΡΕΠΕΙ να συμπυκνώνουν όλες τις αποτυχίες AEAD σε ένα μόνο σχήμα σφάλματος.

13.6 Υλικό Κλειδιού και Διανομή

Το κλειδί περιτυλίγματος K είναι μια 256-bit ομοιόμορφη τυχαία τιμή που παράγεται ανά-qub από ένα CSPRNG. Οι υλοποιήσεις αναφοράς το πηγάζουν από:

Διανομή: το K ΠΡΕΠΕΙ να κωδικοποιείται ως URL-safe base64 (RFC 4648 §5, χωρίς γέμισμα) και να προσαρτάται στην URL παράδοσης ως το συστατικό fragment:

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

Το τμήμα fragment δεν μεταδίδεται ποτέ σε κανέναν διακομιστή από συμμορφούμενο περιηγητή. Τα κανάλια ανάκτησης (ευρετήριο ιστορικού από την πλευρά του διακομιστή, opt-in αυτόματη αποστολή email) που διατηρούν την πλήρη URL παράδοσης — συμπεριλαμβανομένου του τμήματος fragment — πέραν της συσκευής του χρήστη είναι ρητός συμβιβασμός έναντι της προεπιλεγμένης κρυπτο-τεμαχιστικής θέσης και ΠΡΕΠΕΙ να ελέγχονται από ρητή συγκατάθεση του χρήστη.

Απώλεια τμήματος fragment. Εάν ένας χρήστης χάσει το τμήμα fragment της URL και δεν έχει κανάλι ανάκτησης, το qub είναι μη αναγνώσιμο. Αυτός είναι ο κρίσιμος συμβιβασμός του σχεδιασμού και ΠΡΕΠΕΙ να αποκαλύπτεται στον χρήστη κατά τον χρόνο σφράγισης. Το MVP ενισχύει την αποκάλυψη χρόνου σφράγισης με ρητό κείμενο «αποθηκεύστε αυτή τη URL» και κανάλι ανάκτησης με επαληθευμένο email για χρήστες που επιλέγουν.

13.7 Εκτός Εμβέλειας για αυτή την Ενότητα

13.8 Δημόσια qubs (παράλειψη περιτυλίγματος)

Το εξωτερικό περιτύλιγμα είναι προαιρετικό στο επίπεδο παράδοσης. Ένας δημιουργός μπορεί να σφραγίσει ένα qub ως δημόσιο, οπότε το κανονικό SealedQubCbor εγγράφεται στη μόνιμη αποθήκευση απευθείας, χωρίς επίπεδο OuterWrapper και χωρίς κλειδί K:

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

Ένα δημόσιο qub είναι χρονοκλειδωμένο αλλά όχι ελεγχόμενο από σύνδεσμο: παραμένει μη αναγνώσιμο μέχρι να δημοσιευθεί ο γύρος drand του (το επίπεδο tlock δεν αλλάζει), αλλά μετά το ξεκλείδωμα οποιοσδήποτε έχει το arweave_tx_id μπορεί να το αποκρυπτογραφήσει — δεν απαιτείται τμήμα fragment της URL, επειδή δεν υπάρχει K. Αυτός είναι ο σκόπιμος συμβιβασμός για επιφάνειες που πρέπει να καθοδηγεί ο διακομιστής: τα email ειδοποίησης αποκάλυψης, οι ενσωματώσεις τρίτων και ο πλουσιότερος SEO μετά την αποκάλυψη χρειάζονται όλα έναν σύνδεσμο που λειτουργεί χωρίς ένα μυστικό που ο διακομιστής δεν κατέχει ποτέ (§13.6).

Συνέπειες που ένας παραγωγός ΠΡΕΠΕΙ να λάβει υπόψη:

Το ιδιωτικό (περιτυλιγμένο) παραμένει η προεπιλογή· το δημόσιο είναι ρητή επιλογή του δημιουργού ανά 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  = 4695445  (= (1736294400 - 1595431050) / 30, drand mainnet params §14.2)
  body         = "Hello, future."  (UTF-8, 14 bytes)
  title        = absent

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

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

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

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

Οι υλοποιήσεις ΠΡΕΠΕΙ να παράγουν πανομοιότυπες τιμές body_hash και qub_id για αυτή την είσοδο. Αυτό το δοκιμαστικό διάνυσμα ΘΑ ΕΠΡΕΠΕ να είναι το πρώτο unit test που γράφεται. Οι κανονικές τιμές παραπάνω υπολογίστηκαν από την υλοποίηση αναφοράς και ΠΡΕΠΕΙ να ταιριάζουν bit-προς-bit. Ιστορικές διατάξεις προεικόνας (προ της εκκίνησης — κανένα ζωντανό qub δεν εξαρτιόταν από αυτές): το qub_id της V1.0 με προεικόνα 92 bytes ήταν 3d9fc2390eab043d38a1669ed3b71be76f9eefe872b9569ab1aaa027b88392b0· το qub_id της V1.1 με προεικόνα 100 bytes (μετά την ενσωμάτωση του outcome_at_or_zero) ήταν b0d032898ad629795150fdcb3f84e518f59ed05b7a2a82bc24ebdb87f52144ed. Η V1.2 ενσωματώνει το drand_round και αναβαθμίζει τον διαχωριστή πεδίου σε QUB_ID_V2.

14.2 Αντιστοίχιση Γύρου Ξεκλειδώματος

Input:
  unlock_at           = 1735689600
  chain_genesis_time  = 1595431050
  chain_period_seconds = 30

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

drand_round = 4675285

14.3 Κανονικό CBOR Round-Trip

Οι υλοποιήσεις ΠΡΕΠΕΙ να επαληθεύουν ότι serialize(parse(serialize(qub))) == serialize(qub) για όλες τις έγκυρες εισόδους. Αυτό είναι ένα property test, όχι ένα μεμονωμένο διάνυσμα.

14.4 PactTerms CBOR (content_type 0x03)

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

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

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

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

Τα κανονικά bytes CBOR και ο SHA3-256 body_hash υπολογίζονται από την υλοποίηση αναφοράς. Οι υλοποιήσεις ΠΡΕΠΕΙ να παράγουν byte-πανομοιότυπο CBOR για αυτή την είσοδο.

Οι υλοποιήσεις ΠΡΕΠΕΙ επίσης να επαληθεύουν ότι serialize(parse(serialize(pact))) == serialize(pact) για όλες τις έγκυρες εισόδους PactTerms (property test).

14.5 Διανύσματα Εξωτερικού Περιτυλίγματος Πολλαπλών Γλωσσών

Το εξωτερικό περιτύλιγμα (§13) έχει ξεχωριστό κανονικό fixture στο crates/qub-core/tests/vectors/wrapper_v1.json. Κάθε περίπτωση καρφιτσώνει μια τετράδα (key, nonce, qub_id, sealed_cbor) ως αδιαφανείς εισόδους hex και ισχυρίζεται μια συγκεκριμένη έξοδο expected_wrapper_hex. Και οι δύο υλοποιήσεις αναφοράς καταναλώνουν το ίδιο αρχείο JSON:

Το fixture αυτή τη στιγμή καρφιτσώνει τρεις περιπτώσεις:

Περίπτωση Κάλυψη
basic-text-public Μικρότερο ρεαλιστικό σχήμα SealedQub· χωρίς προαιρετικά πεδία. Καθιερώνει το κανονικό σχήμα περιτυλίγματος για ένα τυπικό qub v1.0.
with-recipient-pubkey SealedQub με recipient_pubkey ορισμένο (μονοπάτι Phase 2). Διαφορετικό σύνολο εσωτερικών κλειδιών CBOR, διαφορετικό qub_id.
longer-body Σώμα ~4 KiB — εξασκεί προθέματα μήκους CBOR πολλαπλών bytes τόσο μέσα στον εσωτερικό φάκελο όσο και στο εξωτερικό κρυπτογραφημένο κείμενο.

Οι υλοποιήσεις ΠΡΕΠΕΙ να παράγουν byte-πανομοιότυπο expected_wrapper_hex για τις καταγεγραμμένες εισόδους. Η αναγέννηση του fixture απαιτεί QUB_REGEN_VECTORS=1 cargo test -p qub-core --test wrapper_vectors και προορίζεται για σκόπιμες αλλαγές μορφής.


15. Διακυβέρνηση Κρυπτογραφικού Προφίλ (Μελλοντικά)

Αυτή η ενότητα είναι πληροφοριακή για την v1 και γίνεται κανονιστική την πρώτη φορά που ένας δεύτερος αλγόριθμος εισέρχεται σε οποιοδήποτε από τα κρυπτογραφικά πρωτεύοντα του qub.

15.1 Τρέχουσα Στάση

Το πρωτόκολλο v1 δεσμεύει ακριβώς έναν αλγόριθμο ανά πρωτεύον:

Οι επαληθευτές αυτή τη στιγμή κωδικοποιούν σταθερά τα μήκη κλειδιών και υπογραφών ανά πρωτεύον. Καμία επιφάνεια ευελιξίας δεν εκτίθεται από το μορφότυπο σύρματος.

15.2 Επιδιωκόμενο Σχήμα

Όταν ένας δεύτερος αλγόριθμος εισέλθει στο πρωτόκολλο, ο επαληθευτής θα διαμορφωθεί για ένα ονομασμένο CryptoProfile (π.χ., ExqubV1) που θα παραθέτει το ακριβές σύνολο επιτρεπόμενων τιμών ανά πρωτεύον — sig_algs, αλυσίδες drand, εκδόσεις περιτυλίγματος, τύπους περιεχομένου. Το προφίλ είναι σταθερό κατά τον χρόνο επαλήθευσης, ποτέ διαπραγματεύσιμο in-band. Οποιαδήποτε τιμή εκτός του ενεργού προφίλ απορρίπτεται.

Αυτό εγγυάται ότι η προσθήκη ML-DSA-87 ή η ενεργοποίηση του Ed25519 δεν μπορεί να αποδυναμώσει αναδρομικά υπάρχουσες διαμορφώσεις επαληθευτή: ένας επαληθευτής v1 παραμένει επαληθευτής v1 ακόμη και αφού δημοσιευθεί ένα προφίλ v2.

15.3 Συνθήκες Ενεργοποίησης

Προωθήστε την §15 σε κανονιστική κατάσταση όταν προταθεί οποιοδήποτε από τα ακόλουθα:

Μέχρι τότε η §15 είναι ένα placeholder που σταθεροποιεί το σχήμα μετάβασης ώστε μελλοντικά PRs να προσγειώνονται σε γνωστό στόχο αντί να επανα-διαπραγματεύονται την επιφάνεια διαπραγμάτευσης από την αρχή.