commit b80321f7a9f34b27ab3fc721b64c84bd5448d6d3
parent 5843cfe532e52a4094d5eccd820b1e42c6239454
Author: Iván Ávalos <avalos@disroot.org>
Date: Tue, 18 Aug 2026 16:17:43 +0200
dd92: document the block size bound and what a restore re-derives
Diffstat:
1 file changed, 44 insertions(+), 0 deletions(-)
diff --git a/design-documents/092-incremental-backup-sync.rst b/design-documents/092-incremental-backup-sync.rst
@@ -2310,6 +2310,18 @@ about, however abruptly it is shut down.
A wallet that has not set up backup yet has no encryption key to protect the
increments with, so recording is a no-op rather than an error.
+An increment that another record depends on must not reach the group later
+than the record itself. The denomination of a coin is the case that
+matters: a restored coin only counts towards the balance once the
+denomination it names is in the database, since that is where the
+availability row takes its currency and value from. Denominations are not
+written by a transaction of their own, so recording a coin records its
+denomination with it -- once per denomination, however many coins of it a
+withdrawal makes -- and the two travel in the same block, where the
+denomination section is applied before the coin section. Leaving the
+denomination to the full collection pass instead would let a coin reach
+the other wallets of the group up to a day ahead of it.
+
The backup cycle
~~~~~~~~~~~~~~~~
@@ -2331,6 +2343,19 @@ modified`` if the upload did in fact land. Increments are thus neither lost
nor backed up twice, and a cycle that has packed a block always retries it
before packing new increments, so the linked list stays ordered.
+A cycle packs at most one block, and bounds its size. The server refuses
+an upload beyond its ``storage_limit_in_megabytes`` with ``413``, and a
+block over that limit is not a transient failure: the wallet would re-upload
+the very same block on every cycle and never get past it. The pack
+therefore stops well below any plausible server limit and leaves whatever
+does not fit in the pending buffer, which the next cycle takes -- a wallet
+handing over a long history (the full collection pass on a well-used
+device) sends it as a run of blocks rather than as one oversized one, and
+reports progress rather than backing off between them. A ``413`` that
+happens anyway is answered by putting the block's increments back and
+packing the next one smaller, since retrying it unchanged can never
+succeed.
+
A cycle also pulls the account's linked list before packing new increments,
applying any blocks it has not seen before (see "Restore process" below), so
that new blocks are appended after the current end of the list.
@@ -2435,6 +2460,25 @@ recovering wallet sees exactly the blocks any other wallet in the group
uploaded and applies them with the same merge rules, so all devices converge
on the same state.
+Two things a restored record cannot simply carry are worked out again on
+the restoring device:
+
+* A coin that arrives before the denomination it names cannot be counted,
+ because the availability row cannot be written without it. Applying a
+ denomination therefore recounts the coins of that denomination that are
+ already in the database, so a coin whose denomination travels in a later
+ block -- or in a block written by another wallet -- still reaches the
+ balance instead of being dropped from it for good.
+* A pending withdrawal's transfer instructions -- the exchange's credit
+ accounts, and the transfer options the user actually pays with -- are
+ derived from the exchange, the instructed amount and the reserve key
+ pair, and an option registered with a prepared-transfer service carries
+ an expiry. A restoring wallet derives them again whenever the ones it
+ restored are absent or expired, and does so *before* it queries the
+ reserve: until the transfer has been made the reserve does not exist at
+ the exchange yet, so a wallet that waited for the reserve status would
+ never get as far as showing the user something to pay with.
+
Restore schedule
----------------