corrections

This commit is contained in:
Evgeny @ SimpleX Chat
2026-08-17 12:52:32 +01:00
committed by Evgeny Poberezkin
parent 0bd32d7e11
commit e750e453b5
3 changed files with 50 additions and 39 deletions
@@ -5,13 +5,18 @@ sequenceDiagram
participant S as Server<br>that has A's send queue<br>(B's receive queue)
participant B as Bob
A ->> R': NEW: create new queue<br>(allow SKEY)
A ->> S: SEND: QADD (R'): send address<br>of the new queue(s)
A ->> R': NEW: create new queue (SKEY allowed)
A ->> S: SEND: QADD (R')
S ->> B: MSG: QADD (R')
B ->> R': SKEY: secure new queue
B ->> R': SEND: QTEST
R' ->> A: MSG: QTEST
A ->> R: DEL: delete the old queue
B ->> R': SEND: send messages to the new queue
R' ->> A: MSG: receive messages from the new queue
B ->> R: SEND: messages (also scheduled on R')
R ->> A: MSG: messages
B ->> R': SKEY: authorize B as sender
B ->> R': SEND: confirmation (establishes R' secret)
R' ->> A: MSG: confirmation (A secures R')
B ->> R': SEND: held copies (deduped), then new messages
R' ->> A: MSG: messages
B ->> R: SEND: remaining tail, then QEND
R ->> A: MSG: QEND
B ->> R': SEND: QEND
R' ->> A: MSG: QEND
A ->> R: DEL: delete the current queue
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 29 KiB

+35 -29
View File
@@ -1,10 +1,10 @@
---
Proposed: 2026-08-09
Protocol: agent-protocol v8
Diagram: ./diagrams/duplex-messaging/queue-rotation-fast.svg
Diagram: ../protocol/diagrams/duplex-messaging/queue-rotation-fast.svg
---
# Fast queue rotation (redundant delivery)
# Fast queue rotation
## Problem
@@ -14,33 +14,38 @@ cannot move away from a failed server.
## Solution
When both agents support v8, rotation is redundant delivery rather than a switch. The initiator adds
a new queue with `QADD`; from that point the peer writes every message to both the current queue and
the new queue, and secures the new queue in parallel. Once the new queue is secured the peer writes
new messages to it alone, while the current queue delivers whatever was already scheduled on it and a
final `QEND`, and is then removed. The recipient already drops duplicate messages, so the order of
delivery and which queue delivers a given message do not matter, provided every message arrives on at
least one queue. There is no boundary and no flip.
Both the current rotation and v8 add a queue, deliver to both queues while the rotation is in
progress, and remove the old queue; the recipient drops duplicates in both. The main difference is where
the new queue's secret is established. In the current rotation it is established over the current
queue, by `QKEY`, so it cannot complete when the current server is down. In v8 the peer establishes the
new queue's secret over the new queue itself — a confirmation it sends on R' — so establishing the
secret no longer depends on the current queue, and the rotation completes even when the current server
is down.
Rotation away from a dead current server works because every message is also scheduled on the new
queue: the recipient reads it there. A dead new queue does not stop delivery either, because the
current queue keeps delivering until the new one is secured.
v8 also starts writing to both queues earlier: from the moment the queue is added, including the
current queue's not-yet-delivered backlog. So the initiator adds the new queue with `QADD`; from that
point the peer writes every message to both the current queue and R'. Once R' is secured the peer
writes new messages to it alone, while the current queue delivers whatever was already scheduled on it
and a final `QEND`, and is then removed. Because the recipient drops duplicates, neither the order of
arrival nor which queue carries a message matters, provided each message arrives on at least one queue
— with one exception, the confirmation, which is always the first message on R'. A dead new queue does
not stop delivery either, because the current queue keeps delivering until R' is secured.
Roles: A initiates (its receiving queue rotates; A receives on the new queue R'). B is the peer (B
holds the sending queue to A, secures R', and delivers to both).
Sequence (as drawn in the diagram):
Sequence:
A -> R' : create new queue (messaging mode, SKEY allowed)
A -> S -> B : QADD(R') (over A's sending queue; A's current server untouched)
B : from now, schedule every message on both the current queue and R'
B -> both : messages (current queue and R', duplicates dropped by A)
B -> R' : SKEY (B secures R')
B -> R' : confirmation (empty; establishes R' secret; first message on R')
B : R' secured — new messages now go to R' only
B -> current : remaining tail, then QEND(current)
B -> R' : QEND(current), then new messages
A : on QEND, delete the current queue; keep receiving on R'
A -> R' : create new queue (messaging mode, SKEY allowed)
A -> S -> B : QADD(R') (over A's sending queue; A's current server untouched)
B : from QADD, schedule every message and the current backlog on both queues
B -> current : deliver the scheduled messages (R' holds its copies while securing)
B -> R' : SKEY (authorize B as sender)
B -> R' : confirmation (empty; establishes R' secret; first message on R')
B -> R' : deliver R''s held copies (A dedups), then new messages to R' only
B -> current : deliver the remaining tail, then QEND(current)
B -> R' : QEND(current)
A : on QEND, delete the current queue; keep receiving on R'
## Confirmation
@@ -58,10 +63,11 @@ additionally encrypted with the double ratchet, so rotation does not advance the
`QEND` names a queue to remove and is delivered on both queues. On receipt the recipient deletes the
named queue; on send the peer removes its sending queue of that address. `QEND` is a general
queue-removal message — the peer can remove either queue with it — so rotation is the addition of a
queue (`QADD`) followed by the removal of a queue (`QEND`), with no distinct switch step. Delivering
`QEND` on the removed queue is best effort; the copy on the surviving queue removes it and reaches the
recipient even when the removed server is dead.
queue-removal message — the peer can remove either queue with it — so on the wire a rotation is the
addition of a queue (`QADD`) and the later removal of the replaced one (`QEND`), each an ordinary
operation on the queue set rather than a `QTEST`-style completion. Delivering `QEND` on the removed
queue is best effort; the copy on the surviving queue removes it and reaches the recipient even when
the removed server is dead.
## Per-queue secret
@@ -75,7 +81,7 @@ Fast rotation runs only when the connection's agreed agent protocol version is 8
chooses it. Otherwise the `QKEY`/`QUSE` exchange is used. `QEND` is defined at version 8 and is only
sent during fast rotation, so peers below version 8 never receive it.
new A / new B : fast (QADD, confirmation, redundant delivery, QEND)
new A / new B : fast (QADD, confirmation on R', QEND)
new A / old B : slow (old B returns QKEY; new A keeps the QKEY/QUSE handling)
old A / new B : slow (agreed version below 8; new B returns QKEY)
old A / old B : slow
@@ -87,5 +93,5 @@ confirmation on R' followed later by `QEND`.
The initiator keeps reading messages on the new queue and removes the current queue when `QEND`
arrives there, without waiting for the current server. Nothing is lost, because every message is
scheduled on the new queue as well; the only cleanup that a dead current server delays is the deletion
scheduled on the new queue; the only cleanup that a dead current server delays is the deletion
of its queue, which is retried a bounded number of times and then abandoned.