1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
|
# Federation: a design
A design, not what the code does: none of this is built. It says how a swarm can be
kept by parties that don't trust each other, for a first use: the krebs registry of
users, their hosts, and the hosts' names, addresses and keys
([krebs/kartei](https://github.com/krebs/kartei)), today plain files in a git
repository that one maintainer merges into.
Claims about the code as it is are marked *(checked)* where they were read in the
code, *(docs)* where they are taken from the docs, and *(assumed)* otherwise; the last
section lists them. How sure each part of the design is, is said with the promises.
## Optional
A swarm is federated if its root carries the federated policy (below). Everything here
holds under that policy, and nothing else changes:
* A swarm started as today has no such policy and stays as it is: its rules, its
users with their capabilities, its remakes by the members' votes, its tokens, its
messages.
* The code is apart where it would cost. Judging reads the policy as it does today and
takes the federated rules under the federated one only; the resolved registry is
kept there only; envelopes carry their new fields there only; no message changes
for a swarm that isn't federated.
* Shown at every stage by the existing tests passing untouched, and by the cost of
judging a change (`bench_screening`) staying where it is for a swarm without a
policy and with today's.
## The parties and the adversary
A **party** is a user and the nodes that carry for it. The user holds SSH keys and
signs; its nodes commit what it signed and sync with the others' nodes. One user
starts the swarm: the **root**. Nobody owns the swarm, and any party may be malicious,
alone or together with others, the root too.
What an adversary can do:
* run nodes, and once it is in, bring in users of its own;
* craft any change: under any actor, with any dependencies (so "made long ago", as
the document sees it), any operations;
* send different changes to different peers, send none, or pass on only some;
* delay or drop messages between any nodes for as long as it likes, short of forever
between honest nodes;
* choose its node keys, and so where its nodes fall among the members;
* steal a key of another party (a user's key, a node's key), as a case of its own.
What it can't: forge an Ed25519 signature, or find two changes with one hash.
Out of scope: keeping the registry from being read (every party reads all of it),
hiding who talks to whom, and a party's own machine taken over whole (then the
adversary is that party).
## What is promised
To an honest node, whatever the others do, with any number of them malicious (3 to 5
but for what the voters can do to a user, said with each and below):
1. **Only valid changes.** Every change it holds passed the rules, judged as its
writer saw the document (at its dependencies).
2. **The same document.** Two honest nodes holding the same changes hold the same
document, and resolve it (Resolving, below) to the same registry.
3. **Signed by its user.** Whatever the resolved registry shows in a user's namespace,
or as held by a user, was signed with a key that was that user's when it signed, or
is as the root change has it. A key is a user's by what its own keys signed, or by
a decision that gave it first keys anew (7).
4. **One holder.** The resolved registry gives each name of each kind to one user at
most, and each user name to one user.
5. **First come, first served, once the root saw it.** A claim is **settled** once a
node of the root committed a change after receiving it, with no other claim of
that name around. From then on nobody else ever holds that name while the claim
stands: every other claim of it, whenever and with whatever dependencies it shows
up, is late. Two things can still happen to it. Users on the lines its user stands
on (those it came in by) can get it **contested**, so that nobody holds the name;
that shows, with the changes that did it. And a claim made after another user
released the name stands as long as that release does (Cuts). A claim stands
while its user does: the names of a user nobody vouches for any more wait for it,
for as long as the policy says, as the voters' nodes saw it, unless a decision
frees them or gives them away before (Unvouching).
6. **Nobody waits for anybody.** An honest user's valid write needs no vote and no
other party's consent; it counts at once, and reaches every honest node its nodes
reach.
And for what the swarm decides together, with the fractions the design recommends
and fewer than a third of the voters present malicious, then or later (the voters are
the users an epoch's root change names):
7. **Decisions by two thirds.** A proposal (a remake, a new root, first keys for a
user that lost its own, the names of a user that is out, a number, the policy) is
decided only once more than two thirds of the voters present, and more than half
of all the voters, voted for it and their nodes held those votes while they ran.
So none is decided that no honest voter voted for, nor from votes that had run
out, and of the sides of a partition one at most decides. Of the decisions of one
matter every node acts on the same one, the last. A remake changes nothing in
what counts: every node builds the next epoch itself, from the changes it holds.
Nobody is moved against its will: a decision is refused by choice.
What is not promised:
* **One history at all times.** Both sides of a partition go on. A name claimed on
both sides goes to the side the root was on, or is contested until one side
releases it.
* **That a name is never contested.** Before a claim is settled, a claim made out of
its sight contests it. After, the users on the lines its user stands on can, by
saying they saw another first.
* **Protection from a stolen key** before its user notices: what was signed with it
counts until a key above it cuts it off; then all of it stops counting (Cuts). A
remake in between makes it final.
* **Agreement in the classic sense, for what users write.** There is no quorum there,
so no n ≥ 3f+1: promises 1 to 4 and 6 hold against any number of malicious parties,
and 5 from when the root saw the claim. Only decisions take two thirds, and only
the voters make them.
* **A name against the voters.** Two thirds of them give a user's first keys anew.
Meant for a user that lost them, it hands a name to whoever they say: in the open,
over the days a vote takes. The same two thirds free the names of a user that is
out, or give them to another, and decide a policy that covers a name no more. And
a third of them, stamping as they like, make a user that is out gone at once.
* **A name through a long partition, for a user that is out.** Unvouched on one side
of a partition that outlasts `gone`, it is gone, though someone vouched for it in
time on the other.
* **That everyone follows a decision.** A party refuses one it won't bear, and goes
on without it, with those who think alike: two epochs, by choice (Refusing a
decision).
* **A user put out by the swarm.** Nobody is put out by a vote: a user is in while
one user that counts vouches for it (Unvouching).
* **Decisions against a third of the voters present.** A third or more of them,
voting both ways, get two proposals of one matter decided, and choose which is the
later; and they show a voter silent, or a user gone, before its time. Fewer do the
first across a partition that outlasts the absence period and splits the honest
voters about evenly, by being present on both sides: two remakes are then two
epochs (Decisions). Nor against the past: the keys of two thirds of those who were
voters once, stolen later, make a decision "of then". A node that was up holds it
back; one that joins later can't tell, until a remake put that past out of reach.
* **Decisions without the voters.** With half an epoch's voters or fewer around,
nothing more is decided there; what users write goes on. Until the first remake
the root is the only voter. Those left go on by a fork.
* **A swarm that never splits.** A node that forks its own actor (two changes with one
number) splits the honest nodes that took one from those that took the other, until
one side drops its own (Partitions). It is found, proven and named; it isn't
prevented.
How sure: 1 to 4 follow from how changes are judged today plus rules of the same
shape, and from Kleppmann's result that a hash-linked change graph whose validity
depends on a change and its history alone converges whatever the faulty nodes do
(*Making CRDTs Byzantine Fault Tolerant*, 2022): high, given the conditions under Why
it holds. 6: high. 5 is this design's own construction, argued below: medium. 7 is the
two-thirds argument carried over to votes that take days, with lengths of time read
from the nodes' stamps: medium, less for the time. A user gone after being out for
long is read the same way. 5 and 7 want the tests named under Attacks, and someone
trying to break them.
## The design
It builds on the strict policy ([`access.md`](access.md), Values signed by their
users): values as envelopes their users sign, which every node checks against the
registry as the writer saw it. What it adds: users come in by being vouched for, and
go by being unvouched; each has its namespace; names of declared kinds are held by one
user each; what a key signed can be cut off after the fact; and the swarm decides by
its voters' votes.
### The policy, in the root
A federated swarm's policy is `{"version": 2, "values": "signed", "users": "vouched"}`,
with what the rules read beside it in `_continuum/access`: the kinds of names
(`claims`), the paths in namespaces whose values are signed (`userSigned`, as today),
and the **numbers**. All of it is in the swarm's root, its genesis change, with the
root user, its keys and its nodes:
* Nothing is ever written before the policy, so nothing is judged by none.
* Every node is given a root and takes no other: the genesis root (`--genesis-root`),
or, in a swarm that was remade, the ID of a later epoch. The root is what a party
trusts, not what its seeds say.
* A node that doesn't know the policy doesn't sync at all. (Today it receives
without judging *(docs)*, which here would let anything in.)
The policy changes by a decision: each number by itself, the rest as a whole (The
policy changes by a decision).
**The numbers.** The quantities of this design are numbers of the policy. The design
recommends; the genesis policy sets each, and a decision changes any of them to any
value:
| Number | Recommended | What it says |
|---|---|---|
| `window` | 7 days | how long a vote runs |
| `absent` | 30 days | after how long seen silent a voter is absent |
| `gone` | 30 days | after how long seen out a user is gone; none: its names wait until a vouch or a decision |
| `uncovered` | 90 days | how long what a policy no longer covers stays aside before it is dropped; 0: at once; none: until its user takes it away |
| `archiveRetention` | 90 days | how long a node answers for an epoch it left, at least |
| `baseAge` | 10 minutes | how much of what a voter's node holds a remake's base may lack |
| `witness` | 1 minute | how long the root's node lets a claim wait before it writes |
| `limits.keys` | 64 | keys a user has at once |
| `limits.nodes` | 64 | nodes carrying for a user at once |
| `limits.users` | 16 | users a user created that stand; the root: no limit |
| `limits.vouches`, `limits.bans` | 64 | vouches, and bans, of a user that stand |
| `limits.proposals` | 8 | proposals of a user that are open |
| `limits.name` | 255 | bytes of a name |
| a kind's `limit` | none | names of the kind a user holds |
| `fractions.decide` | 2/3 | a decision takes more than this share of the voters present |
| `fractions.all` | 1/2 | and more than this share of all the voters |
| `fractions.seen` | 1/3 | the share of the voters whose nodes must have seen a thing for long: a voter silent, a user out |
Nothing constrains them: a number that makes no sense has the result it has (an
`absent` of nothing leaves nobody to decide, a `fractions.decide` of nothing lets
one voter decide for all). Who won't bear a decision refuses it (Refusing a
decision). More than a share is strictly more; a share of the voters, for what was
seen for long, is the fewest voters that are that share at least, and one at least.
Where this text says two thirds, half and a third, it means the three fractions as
recommended. What is promised of decisions is argued for those; a swarm that sets
others has what its numbers carry (Why it holds). What a node does for itself alone
(how fast it takes a party's changes) is its own setting.
### Users: statements, keys, nodes
`_continuum/users/<name>/<digest>` holds the **statements** about and by a user, each
an envelope kept under its SHA-256, never replaced or deleted: the register only
grows. A statement is signed by one user and says one thing:
| Statement | Signed by | Says |
|---|---|---|
| `create` | another user (the creator) | the name, its first keys, the nodes that carry for it |
| `vouch` | another user | "I vouch for this user too" (no keys) |
| `accept` | the user | a vouch for it that it stands on (Vouching) |
| `unvouch` | the signer of a `create` or `vouch` | that one taken back (Unvouching) |
| `key` | the user | one more key |
| `retire` | the user | a key, and the cut after which what it signed doesn't count |
| `node` | the user | a node that carries for it; or one that stops, with a cut |
| `release` | the user | a claim, a vote, a proposal or an acceptance of its own given up; or itself |
| `ban` | the user | a user that doesn't count for it, with a cut (Bans) |
| `propose`, `vote` | a voter (a fork: any user) | Decisions |
* **A user is its creation.** The `create` statement's digest is the **user ID**, and
every envelope the user signs names it, with the name, the swarm and the epoch it
was signed in. Two creations of one name are two users with two sets of keys; which
has the name is decided below, and a key listed in both signs for the one its
envelope names.
* **Keys form a tree.** A user's first keys are those its creation names. A `key`
statement adds one, signed with a key the user has, which is **above** the new one,
as are the keys above itself. Any number over time; at once as many as
`limits.keys` says. Use: the first keys stay offline, a key added below them is on each
device.
* **A node carries for a user** that named it. A node commits changes only while it
carries for a user, and an envelope is taken only in a change committed by a node
that carries for its signer (a `node` statement also in one by the node it names;
what a decision is made of, in any node's: Decisions). So every change is some
party's: its node's signature proves the node ([`access.md`](access.md),
signatures required), and the user's statement binds the node to the user. At once
as many nodes a user as `limits.nodes` says.
* **A user without a node** names another party's, which carries for it too if it
agrees (it commits the `node` statement). That node's changes witness for both,
its alive stamp is the presence of both, and it takes decisions up for both. The
user trusts that party with those; nothing is signed in its place.
* **Names** of users are 1 to 64 of `A-Z a-z 0-9 . _ @ -`, as today, less the
top-level keys the swarm uses otherwise (`_continuum`). Which of them may be
created, and by whom, the kind `user` says (Claims).
### Vouching, lines, the root
A user creates whom it likes, within a limit (`limits.users`), and vouches for whom it
likes, within another. The new user signs, claims and creates in turn from its
creation on: in any change that has the creation in its history.
Users and what they signed for each other form a graph, the root at its top. A
**line** of a user is a chain of creations and vouches that count, from the root down
to it. A user is **vouched for**, and in, while it has a line. So rings away from the
root hold nobody, and a vouch by a user whose own lines all pass through the one it
vouches for adds none. When its creator goes (unvouched, cut off, out), a user that
another vouched for stays in, by that line, without doing anything. When the last
goes, it is out (Unvouching).
A user **stands on** some of its lines: those through its creation, and those through
a vouch it accepted (`accept`, naming the vouch; `release` to leave it again). The
users on the lines it stands on are whom it answers to for what came first:
* their nodes are the witnesses for and against its claims (below);
* a creator's limit bounds how many users come in through it, and each shows below
it, for all to see.
A vouch the user didn't accept keeps it in all the same, and gives the voucher no
say about its names: nobody gets one by vouching unasked. Leaving a line takes back
nothing its nodes had shown by then: what they committed counts as far as the user's
own nodes, or the root's, had seen it without the release.
Bringing someone in means handing over, out of the document, what a party needs to
connect and to start (the root, some seeds, the swarm key where there is one), and
writing the `create`. Who vouched for whom says nothing about who connects to whom
(Reaching the others).
**What the root is.** A user like any other, but for two things. It is in the root
change, so nobody vouched for it, and its name can't be created again. And it is on
every line, so its nodes' changes are what settles claims. It can't write in
another's namespace, nor take a settled name. It can contest any name, in the open.
Who the root is can be decided anew (The root's seat).
### Lengths of time
The document orders what was written: a change's history is what its writer had
seen. It measures no time. The nodes' alive stamps do, each by its own node's clock
*(checked: `alive`, Unix ms)*. So a length of time is read between two stamps of one
node, and what that node had seen at each, from the stamps' histories.
A thing has been **seen for** a length of time, at a set of changes, once a third of
the voters (`fractions.seen`; the voters: Decisions) each have a node with two alive
stamps that far apart or more, the thing so at both and at every stamp of that node
between. It is read from the changes alone.
A node that is up renews its stamp about daily, whatever else is written or isn't
*(docs)*. So nobody does anything to be seen, and there are always stamps to read
time from. While no node writes at all, no time passes: nothing is seen for longer
by it.
No clock is compared with another. A third, because decisions are argued against
fewer than a third of the voters being malicious: so few show nothing by themselves,
whatever they stamp. And no more than a third, because everyone it takes must be
there: two thirds may have left, and the rest still see it.
### Unvouching
A user takes back a `create` or a `vouch` it signed: `unvouch`, naming it. Nobody else
can, but the root for what a root before it signed (The root's seat). What it does,
exactly:
* **From where.** From the unvouch on, as far as the user had been seen. What the
unvouched user signed stays if it is in the history of the unvouch, or of a change
that a node of the unvoucher or of the root committed without the unvouch in its
history. Everything else it signed doesn't count through this vouch, whenever it
shows up. So an unvouch crafted with old dependencies undoes nothing that its
signer's own nodes, or the root's, had seen: no more than what reached neither.
* **Another vouch keeps the user.** With another line, the user stays in, with
everything it has. It stands on that line if it accepted the vouch.
* **Else the user is out, from there.** Nothing of it counts: its namespace doesn't,
its nodes witness nothing, and what they commit in sight of the unvouch is
refused. Its names **wait** for it, its user name among them: its claims stand,
so nobody else claims them, and nobody holds them.
* **Those it brought in go with it,** from the same point: a user all of whose lines
ran through it is out too, with the same consequences, unless another user that
counts vouches for it, before or after.
* **A vouch brings a user back whole,** with its names and what it signed in
between, as long as it isn't gone.
* **Out for long is gone.** A user is **gone** once it has been seen out for `gone`
(Lengths of time): a third of the voters each have a node with two alive stamps
that far apart, the user out at both and at every stamp of that node between.
Then its claims and its creation stand no more: whoever comes first claims its
names, and no vouch brings it back. It comes in again by being created anew. Gone
never goes away.
* **Or by a decision.** While a user is out, the voters free its names or give them
to another (Decisions), at once and whatever `gone` says. Where the policy sets no
`gone`, the names wait until a vouch or a decision.
* **What it did before stays done.** A name it released and another claimed stays the
other's; a user it created and another vouched for stays.
* **Out of sight.** What the user signs with dependencies from before the unvouch (on
the other side of a partition, or crafted) is valid as written, so nodes hold it;
it is past the point, and doesn't count. A node takes changes of an unvouched
user's nodes only as part of another party's history, not from those nodes, so they
can't fill the document.
So whoever relied on a user that is out has that long to find another who vouches:
for itself, and for those it brought in. Until then nothing it holds is taken, but
by a decision.
**Only who signed takes back.** A vouch is its signer's word, and nobody else takes
it back: not the users above the signer either, who answer for the signer, whom they
brought in. One of them that wants a user out unvouches whom it brought in itself.
That takes all below along, and those worth keeping get another's vouch while their
names wait. Were it otherwise, the root, above everyone, would put out whom it
liked, and for good: every line passes it.
**Nobody is put out by a vote.** A user is in while one user that counts vouches for
it, and no decision changes that. Against a user that keeps within the rules and
harms all the same (squats names up to its limits, gets others' names contested)
there is: each party's ban, which changes what that party reads (Bans); its vouchers
unvouching it; and the root unvouching the user it created above it, with all below,
who then need other vouches. A vouch from anyone brings it back.
A user gives itself up by a `release` of its own creation: from that release on it is
gone, and its names are free at once.
### Namespaces
A user's namespace is the top-level key with its name. One rule: below `<name>`, only
envelopes signed by a user of that name, at the depths `userSigned` lists (for the
registry, `["*", "hosts", "*"]`: a host is one signed value). A value there is
replaced as today, by an envelope naming the one it replaces (`prev`). Nothing plain
is written anywhere but a node's own member entry and its own records of proposals.
Delegating (another user writing below `<name>/shared`, say) would be a statement in
the user's register granting a capability within its namespace: for later; the rule
has room for it.
### Claims: names held by one user
What must be unique across users (a host's name, an address, a top-level domain) is
**claimed**. The swarm declares the kinds, as data in the policy:
```json
{ "name": { "rules": [ { "match": "**.{user}.*", "by": "{user}" },
{ "match": "*.*", "by": "{voters}" },
{ "match": "*", "by": "{root}" } ] },
"ip4": { "rules": [ { "match": "10.243.*.*", "by": "*" } ], "limit": 256 },
"user": { "rules": [ { "match": "*", "by": "*" } ] } }
```
* **A name is written as it is used**: `toaster.tv.r`, `10.243.0.7`. It is one key in
the document, and a rule's `match` is written the same way. Both are split into
segments at the kind's `separator` (a dot, unless the kind names another) and
compared segment by segment: `*` fits any one segment, `**` one or more (once in a
rule), `{user}` one segment, taken as a user's name, anything else itself.
* **Structure, not meaning.** Continuum doesn't ask what a segment is:
`10.243.foo.bar` fits `10.243.*.*` and is claimed like any other. Nor does it know
that two spellings mean one thing (`::1`, upper case): names are compared as
written. What a name means is the application's, which claims one spelling and
reads no other. A name is `limits.name` bytes at most, and no path: it has no `/`.
* **The first rule whose `match` fits says who may hold the name** (`by`): a user, or
one of a list; anyone vouched for (`"*"`), first come, first served; the user whose
name stands where `{user}` does; whoever is the root (`"{root}"`); or whom the
voters give it to, by a decision (`"{voters}"`). No rule, no holder.
* A name a rule gives to `{user}` is **kept**: it is that user's without a claim,
since nobody else could claim it, and it is nobody's while there is no such user.
* So above: everything below `tv.<tld>` is tv's, for any top-level domain, whatever
tv claimed. A name right below a top-level domain (`wiki.r`) is given by the
voters. A top-level domain is the root's to claim. An address goes to whoever
comes first, 256 a user.
* The kind `user` is the users' names themselves: its rules say who may create which
(a swarm can keep names for one creator). Above, anyone creates a name of one
segment.
`_continuum/claims/<kind>/<name>/<digest>` holds the statements about one name, again
only growing: `claim` (by the user who wants it) and `release` (by that user, of its
claim: how a name is given up). A claim **stands** while it counts, isn't released,
its user isn't gone, and no decision freed the name or gave it away. A name a decision
gave is a claim of the user it names, made where the proposal is. A claim is
taken, as its writer saw the document, if a rule lets the user, the user is within the
kind's limit, and no claim of that name stands there. That is first come, first served
wherever one claim was made in sight of the other: the later one is refused by every
node.
A claim says nothing else. What the name is for is in the namespace: a host's entry
names its addresses and names, and they count while its user holds them.
### Two claims out of each other's sight
Two users claim one name at once, or on two sides of a partition, or one of them
pretends to: it crafts a claim whose dependencies are old, from before the other's. No
node can tell these apart, and both claims are valid as their writers saw the
document, so every node holds both. What decides is who can be shown to have seen
what. The witnesses for a claim are its **side**: the nodes of its user, of the root,
and of the users on the lines its user stands on (Vouching).
* A claim is **late** against another if a change committed by a node of its side
(one carrying for such a user when it committed it) has the other in its history
and not the claim itself: the claimant's side had seen the other while the claim
wasn't there yet.
* Of the claims of a name that stand, those not late against any other remain. If
one remains, its user holds the name, or has it wait while it is out. If none or
several do, the name is **contested**: nobody holds it, until releases leave one.
Being late never goes away (changes aren't taken back), and it is read from the
changes alone, so every node finds the same.
What this gives:
* Every node writes its member entry about daily (its alive stamp, *docs*), and such
a change has in its history everything the node had received. The root is on every
line. So once the root's node wrote after receiving a claim, while it had received
no other of that name that isn't released, every claim of the name surfacing later
is late against it, however old its dependencies look: the claim is settled. A node
tells (`settled`, below). The root's node writes when a claim reaches it that it
hadn't seen, `witness` apart at most, so a claim is settled within that long of
reaching it.
* A user that is up is bound by its own node: after its node wrote once with a claim
in its history, it never holds that name against it.
* To take a name from a claim, a rival needs the claim to be late and its own not to
be: a node of the claimant's side saying it saw the rival first, and nobody on the
rival's side having written after seeing the claim. After the root saw the claim,
the second can't be.
* Before a claim is settled, a claim out of its sight contests it. Honest users
claiming at once see "contested", and one releases.
A user's name is claimed the same way: the `create` statement is the claim, its side
the creator's side and the new user's own nodes. Two creations of one name out of
each other's sight are decided as above; while it is contested, no user has the
name, and neither namespace counts.
A user that doesn't have its name still holds what it claimed where anyone may claim:
those claims stand, so contesting a user's name frees none of them. What a rule gives
to a name (a kept name, a name only `tv` claims) is nobody's while the name is.
### Cuts: what stops counting
A **cut** is a set of changes, named by their hashes: "as far as I saw". A statement
is within the cut if a change that carries it is in the history of the cut. Whether
it is doesn't depend on who asks or when: a change's history is fixed by its hash.
* **Retiring a key** names the key and a cut. What the key signed within the cut
counts as before; what it signed outside doesn't count, for anyone, whenever it
shows up. A statement signed with a retired key and offered later with old
dependencies is outside the cut. That is how retiring reaches a node that was away:
it needs no order of arrival.
* **Who retires what.** A key above another retires it with any cut: a user whose
device key was stolen cuts it off from before the theft. A key retires itself only
from where it stands (its cut must hold everything it signed that the retiring
change's history holds): rotating, not rewriting. No key retires one beside or above
it, so a stolen device key can't cut off the keys above it, nor undo what it signed
before.
* **A node that stops carrying** is named with a cut the same way, by the key that
named it or one above: what it committed outside the cut is nobody's, as a witness
or as a carrier.
What stops counting stays in the document, and every node holds the same. It is left
out when resolving. In a namespace, where a value that doesn't count replaced one
that does, the one that does is read again (Resolving).
The price: a key above retiring one below with an early cut takes back what that key
signed since, a vouch or a release among them, further back than an unvouch reaches.
Whoever relies on another's statement relies on that; a second vouch is what makes a
user independent of its creator's keys. And a user's first keys have none of its own
above them: above those are the voters, who give a user first keys anew by a
decision (Decisions).
A cut reaches no further back than the epoch (Remakes).
### Bans: for those who follow
A user may state that another doesn't count for it, from a cut on: `ban`. A node
resolves with the bans of the users it **follows** (`--follow`; its own user by
default): what the banned user signed and what its nodes committed outside the cut is
left out, as a holder and as a witness. Bans are taken in the order they were made;
of two made out of each other's sight, the one by the user with the shorter line to
the root first, then by digest; and none by a user that a ban taken before leaves
out.
A ban changes what a node reads and writes out, never what it takes or refuses: every
node still holds every valid change, so banning splits nothing. It is what a party
does about a user it can't unvouch: one that squats, one above it that contests its
names, a root it no longer trusts and can't get replaced. Its registry goes on without
them. Parties following the same users read the same registry. A node refuses its own
user's write that only its bans make valid (a claim of a name the banned user holds):
the others would refuse it, and everything the node writes after.
This is the trust root a user has beyond the swarm's: the root every node is given,
and whom it follows. In a dispute, the nodes following a user read its side.
### Decisions
Some things are the swarm's to decide: its numbers and the rest of its policy, who the
root is, a user's first keys where it lost its own, names the rules leave to the
voters, when to compact. They are decided by votes of users, signed by them, over
days.
**Who votes.** The **voters** are the users the epoch's root change names. A swarm's
first names the root alone. A remake names those of the next epoch: its proposal
says who, all users (recommended) or those of a list. All users are those there
when the remake is proposed: the users that count in the proposal's history, and
still do at the base. So who votes is decided by those who vote, with each remake;
a user brought in later, while the votes run too, votes once a remake names it; and
until the first remake the root decides alone.
What to weigh in it. Every voter named counts toward what a decision takes: one that
never votes stands in the way like one voting against, until it is absent, and
toward the half of all even then. A swarm of many users of whom few take part gets
little decided with all of them named. And all users is whoever brought users in: a
user that created sixteen has seventeen votes in the next epoch. The list is fixed
with the proposal, so the voters see it before they vote for it
(`GET /registry/decisions`), and name one of their own where they don't like it.
A change in who votes takes a remake because a remake is what puts the votes of
before out of reach (below).
**Who is away.** A voter is **absent** once it has been seen silent for `absent`
(Lengths of time): a third of the voters each have a node with two alive stamps that
far apart, and nothing of the voter's nodes is there that the first of the two didn't
have in its history. It neither votes nor counts, until a node of it writes again.
The voters **present** are those not absent. Fewer than a third of the voters show
nobody silent, whatever they stamp; and those who left hold up nothing.
To be present a voter needs a node that is up, its own or one that carries for it;
the user does nothing. So a node left running keeps its voter present though nobody
votes for it any more. Naming the voters at a remake is what deals with those.
A decision also takes more than half of all the epoch's voters (`fractions.all`),
absent or not. So of the sides of a partition, however long it lasts, one at most
decides. Where half or fewer are left for good, they go on by a fork that names who
votes (Going apart).
**How.**
1. A voter **proposes**: a statement naming its matter (what it is about: one number,
the seat, one user's first keys) and what is to hold. Any number of proposals are
open at a time, on one matter too; `limits.proposals` of one voter's at most. A
proposal is open until it is decided, its proposer releases it, or a later one of
its matter is decided.
2. Voters **vote** for it: each a statement signed by the user, saying when the vote
runs out, `window` from signing unless the user says sooner. A voter has one vote
on a matter at a time; `release` takes one back, as far as it gets there first:
a node that holds the vote without its release counts it, and a proposal may be
decided with a vote whose release was on its way. A vote is meant when signed.
3. A voter's node **takes the proposal up** once it holds votes for it by more than
two thirds of the voters present (`fractions.decide`) and more than half of all,
none released, each still running by the node's clock. It records so, once, under
`_continuum/decisions/<proposal>/<node ID>`. One node a voter does: the first it
named of those that carry for it. The voters present are those present at the
proposal: who was there when it was made.
4. The proposal is **decided**, and is a **decision**, once the nodes of that many
voters took it up. That is read from the document, the same by every node, and
never goes away.
**At once.** Nothing waits for a vote's days to end. With the votes there, the nodes
take the proposal up as it reaches them, and it is decided as fast as they sync.
**The last holds.** Several proposals of one matter can be open, and voted on, at a
time, and more than one can come to be decided: voters change sides. Proposals of a
matter are counted from the first: each has the number after the highest it was made
in sight of. Of the decisions of a matter, the one with the highest number **holds**;
of two with the same number, made out of each other's sight, the one with the lower
digest. A decision closes the proposals of its matter with a lower number: nodes take
those up no more, and clients offer no vote on them.
A decision holds for what is made in sight of it: a change is judged, and resolved,
by the decisions that hold in its history. Every node acts on the decision that
holds, the same from the same changes, unless it refuses it (Refusing a decision).
**By any node.** A proposal, a vote and their release are taken in any node's change:
the signature is the voter's. So a voter votes at whatever node it reaches, or signs
a vote that someone carries to one. A vote kept back counts while it runs, as its
signer wrote, and no longer. What brings a vote from a person to a node (a client, a
page, paper) is the application's.
**Decisions don't come from the past.** Votes kept back until they ran out, or crafted
with old dependencies, are held while they run by no honest node, so their proposal
has only its makers' records. Those are enough only if they were two thirds of the
voters then: their keys, stolen later. Against that, a node that was up holds back a
proposal that comes to be decided more than the absence period after its votes ran
out, and shows it, for its operator to say: follow it, or refuse it. A node that
joins later takes decisions as it finds them; a remake puts everything before it out
of reach.
The votes are the users' own, signed with their keys, and take the days people take.
Taking up is their nodes', and takes as long as syncing does. One time is compared
across machines: a vote's end, written by the voter's client and read by the nodes'
clocks. A window of days bears clocks that are minutes apart.
**What is decided.**
* **A number**: any number of the policy, the fractions among them, to any value.
Each number is a matter of its own. A lowered limit takes nothing away: what a user
has stays, and it gets no more until it is below.
* **The policy**, the rest of it, as a whole (The policy changes by a decision).
* **The root's seat** (below).
* **A user's first keys**, for a user that lost them or had them stolen. The proposal
names the user, the first keys it is to have, and a cut for those it has: what
they signed outside the cut doesn't count, as with a key above them (Cuts). The
voters are above every user's first keys. A user that lost its keys comes in again
under another name, so that it has a voice and the voters know whom they give the
name back to. Decided, it signs under its name as before, with its names, its
lines and those it brought in, and releases the other name.
* **The names of a user that is out**, while they wait. The proposal names the user
and, for each name it deals with, its user name among them, that it is free or who
is to have it. A name freed is anyone's to claim from the decision on. A name
given is held by the user the decision names as by a claim: it counts toward that
user's limit, and that user releases it like one. A user name given brings its
namespace, where the new holder signs over what is there, and the names kept for
it. A user that is vouched for again has back what no decision dealt with.
* **A name the rules leave to the voters** (`"{voters}"`): given to a user, who holds
it as by a claim and releases it like one. Released, it is given anew.
* **A remake** (below).
A decision stays, whatever is cut off after: a vote signed with a key that is retired
later, from before, stays in it. The days a vote takes are the time to cut a stolen
key off.
### The root's seat
The root is whoever holds the seat: at first the user in the root change. A decision
gives the seat to any user. Where it holds:
* the new root is on every line, so its nodes' changes settle claims. It votes if
the epoch names it, like anyone;
* the old root is a user like the others, and votes on if it did. It keeps its name,
its namespace and what it holds, and stays on the lines of those it brought in;
* what a root signed as the root is the seat's: the new root may unvouch the users
the old one created, and the old root itself, which goes alone (those it brought
in stay, their lines running through the seat). The old root no longer may.
What the old root signs that the proposal's history doesn't hold is an ordinary
user's, whenever it shows up: a user it creates then is below it like any user's.
So a root that disappears is replaced by two thirds of the voters present, and so is
one that turns on them. (Before the first remake there is nobody to do it: a swarm
remakes once its root brought the first users in.) A root that comes back finds itself
a user like the others; it may be voted in again. While the seat's holder is away
nothing new is settled: claims count, and can be contested, until there is a root
again.
### Remakes
A federated swarm compacts by a remake its voters decide. A remake throws history
away and names who votes next, and does nothing else: what the next epoch holds
isn't voted on. It is the registry as it resolves, which every node works out
itself.
* **The base.** A remake is proposed and voted on like anything else; its proposal
names who votes in the next epoch. Its **coordinator** is a node of the proposer,
the first it named. Once it holds the votes it names the base, in its record of
the proposal: the base is that record's history.
* **Taking it up.** A voter's node takes a remake up with its base: once the votes
are there, and the base holds all the node holds but the last `baseAge`, so that a
remake can't leave out what the swarm has. It builds the snapshot first, and
records the ID of the epoch it got: the remake is decided for the ID that many got
(Continuum changing under a swarm). One base a proposal, the first to reach the
node; and one remake at a time, another only when the first's votes ran out with
it undecided. A coordinator that names two bases, or none, gets no remake: it is
proposed again.
* **Every node builds the snapshot** itself once the remake is decided, from the base
as it holds it, and switches after the switch delay, as today. The snapshot holds
the registry as it resolves at the base: the policy that holds, with its numbers;
who the root is, and who votes (as the proposal says); the users that count, with
their names (or that they are contested), IDs, keys, nodes, creators, vouchers and
the vouches they accepted; the names held or contested, each marked settled or not;
the values in the namespaces that count, as the envelopes they are; the bans that
stand; the members' entries, as today. A user that is out and not gone is in it with
all it has, and with how long it has been seen out, which counts on.
* **What it leaves out**: all history, and with it everything that is over. A claim
released, and its release. A key retired, a node stopped, an unvouch, each with its
cut: what they left out is out for good. A user gone. A proposal, its votes and
the nodes' records: what holds is in the policy, the seat and the registry. So
after a remake the document's size goes by what stands (users, their keys and
nodes, names held, values, members), not by what ever happened: a name claimed and
released a thousand times leaves nothing. Within an epoch nothing is removed: a
release is a statement like any other, signed, and a document keeps its history.
The remake is what deletes.
* **What the policy no longer covers** is moved aside, with how long it has been
seen there, or left out once it was dropped (The policy changes by a decision).
* **Carried over.** A node writes again, as they are, the statements of its user
that the base lacks. An envelope names the epoch it was signed in. A claim signed
in an earlier epoch is in sight of the claims the snapshot holds settled, and is
refused where the name is held; it is out of the sight of those it holds unsettled,
and contests them, until what nodes commit from then on shows one late.
* **What a remake makes final.** A cut reaches no further back than the snapshot.
What a key signed that counted at the base counts for good, though the key be cut
off later from before; so for an unvouch. The days a vote takes are the time to cut
off a stolen key and to sort out what is contested. After, what a thief did stands
until it is undone by hand.
* **A node that was away** comes back as said below (Coming back after remakes).
* **A node that joins** is given a root to start from, by whoever brings its user in:
the genesis root, or the ID of a later epoch.
* **No other kinds.** `replace` and `import` put content nobody signed: they start a
new swarm.
For the registry the document is small: a statement is under a KiB, a node's alive
stamp with its signature about 300 bytes, so a hundred nodes write some 10 MiB a
year. A remake a year is plenty.
### Coming back after remakes
A node that was away holds an epoch the swarm left, one remake back or several. Its
peers name their epoch in the handshake and don't sync with it; `GET /admin/epochs`
shows the epochs they name, and which of them the users on its lines are in. A
federated node never rejoins because peers advertise an epoch, however many do. It
gets there in one of two ways:
1. **By the archives, on nobody's word.** A peer answers for an epoch it left as long
as it keeps that epoch's document: `archiveRetention` at least, longer if its
operator says, and every epoch on a node kept as an archive. Peers say in the
handshake which epochs they keep. From such a peer the node syncs the rest of the
epoch it is in, judging every change; sees the remake decided; builds the
snapshot itself; and is in the next epoch, whose ID is the snapshot's hash.
So on, epoch by epoch, as far as peers keep them: with an archive within reach,
from any epoch to the swarm's, and for a node that joins, from the genesis root.
Where an epoch holds two remakes decided, or the walk ends in another epoch
than the node's peers are in, the node stops and shows it, for its operator to
say which: an archive can keep changes back, and a decision of long ago can be
made up with keys stolen since. Keeping an archive is an application's; continuum
answers for what a node keeps.
2. **By an ID, on its operator's word.** Where no peer keeps the epoch any more, the
node can't check how the swarm got from there to here. Its operator gives it the
ID of the epoch to go to (`POST /admin/epochs/rejoin`), from a party it trusts:
as when the node first joined, whoever brought its user in. The node rejoins as
today: it downloads that epoch's snapshot from any peer, checked against the ID,
and syncs what was written since.
Either way, what follows:
* **What its user signed and the swarm never got.** A statement signed in the epoch
just before the one the node arrives in is carried over: written again as it is.
One signed further back isn't: an envelope is taken in its epoch and the next, no
later. Those go to the quarantine, and the client lists them for the user to sign
again. A claim signed again is a new claim, after what was claimed since.
* **What it finds of its user**, as the snapshot has it. There, with its keys, nodes
and names: the node carries for it as before, and writes its member entry anew. Or
out, its names waiting. Or gone, and so not in the snapshot: then no node takes the
node's changes, it shows why, and its user comes in again as anyone does.
* **As a voter** the user was absent, and is one in the epoch it arrives in if that
names it. A root that was away is still the root, unless a decision gave the seat to
another.
* **Its old document** goes to the archive, as today.
A node moves to an older epoch, or to another of the same age, only by its operator's
word.
### The policy changes by a decision
The policy but for its numbers is one matter. A proposal carries it whole: the
version, the kinds with their rules, `userSigned`. Like every decision it holds for
what is made in sight of it: a change is judged by the policy that holds in its
history. No remake comes with it. A remake throws history away, a change of policy
changes the rules, and neither needs the other.
* **A policy a node doesn't know.** A node knows a policy if it knows everything in
it (Continuum changing under a swarm). One that doesn't can't take the proposal
up, so a policy is decided only once the nodes of two thirds of the voters know
it. A node that doesn't know a policy that holds stops syncing, as under any
policy it doesn't know, says why, and goes on once it is upgraded. Or its operator
refuses the decision (below).
* **What the policy no longer covers** is **uncovered** where the decision holds: a
claim that no rule lets its user make any more (the kind is gone, or the rule, or
the name is now kept for another or left to the voters), a value at a path
`userSigned` no longer lists. It doesn't count. Every node shows it aside, under
`_continuum/uncovered/<user>/` at the path it had, and no longer where it was.
* **Only its user takes it away**, and nobody changes it: a value by an envelope
that puts nothing in its place (`null`), a claim by its `release`. Nothing else
is written there.
* **For as long as `uncovered` says.** Once it has been seen uncovered for that long
(Lengths of time) it is **dropped**: no node shows it any more, and nothing brings
it back. At 0 it is dropped at once. Until then, a later policy that covers it
again has it count again, where it was.
* **Where it is.** Within an epoch a rule moves nothing: a move is a change someone
has to write, and every node would have to write the same one. So what is
uncovered stays in the document where it was written, shown aside by every node
from where the decision holds. A remake moves it for good: the snapshot holds what
is uncovered and not dropped under `_continuum/uncovered/<user>/`, with how long
it has been seen there, and leaves out what was dropped.
* **Whole, not merged.** Two proposals of a policy are two policies, and the later
holds: what one changed isn't worked into the other.
### Going apart: a fork by choice
Whoever the swarm's decisions leave behind goes on apart, with those who want the
same: with a root that went before the first remake, with half the voters or fewer
left, with two thirds of them turned against the rest, or for a policy the others
won't have. A fork is proposed, and binds nobody:
* Any user proposes it: the policy to go on with (the one that holds, or another),
and, if it likes, who votes there. Where it names none, all users vote that there
are when it is proposed. Voters may vote for it: that shows who would go, and
decides nothing.
* The proposer's node names the base, as a remake's coordinator does, when its user
says so, votes or none.
* A node follows if its operator says so (`POST /admin/fork`, naming the proposal and
the base): it builds the snapshot at the base with what the proposal says, and goes
on in that epoch. A node that doesn't stays in the epoch it is in, which goes on as
it was.
* Both sides have the same history up to the base and the same swarm ID, and from
there different epochs. Their nodes don't sync, and a statement signed on one side
counts for nothing on the other: it names its epoch.
* On each side, the voters who went the other way are absent after the absence
period, and decisions go on without them where more than half of that side's voters
are there. A side with fewer names its own, by a fork of its own.
It moves nobody, and takes nothing along but the history: each party chooses its
side, and a side its root didn't come to seats one.
### Refusing a decision
A decision binds the nodes that follow it, and no node has to. By default a node
follows every decision. Its operator has it refuse:
* **By bounds** (`--bounds`, a file; `GET` and `PUT /admin/bounds`): for any number of
the policy, the least and the most the node goes along with. A decision that sets
a number outside them is refused as soon as it is decided. Bounds that the policy
breaks as it stands refuse nothing: the node shows them.
* **By hand** (`POST /admin/decisions/refuse`, naming the proposal): any decision,
whenever.
What refusing does:
* **Before.** While a proposal's votes run, a node whose bounds it breaks says so in
its member entry, so the voters see who would go (`GET /registry/decisions`).
* **The epoch without it.** A node that refuses a decision moves to the epoch built at
the history of the proposal: the registry as it resolves there, the policy and the
voters as they are there, and the digest of the proposal refused, which makes it no
other epoch. Every node that refuses the same decision builds the same epoch,
whenever it does. So those who think alike meet there without arranging it.
* **After**, as after any fork: the same swarm ID and the same history up to the
base, different epochs, no syncing between them. A node carries its user's own
statements over. On each side the voters who went the other way are absent after
the absence period, and a side with too few of them names its own. A node moves to
the other side by its operator's word.
* **A remake** is refused by staying: the epoch a node is in goes on as it was.
So every number is the swarm's to set, and each party's to bear or not. A decision
that lets one voter decide for all is followed by those who don't mind.
This is how Bitcoin changes its rules. Nobody decides for anyone: nodes run the rules
their operators chose. A change comes into force once enough of those who count
signal that they run it, at a height named beforehand. Nodes that won't have it keep
their rules, and with them their chain, the same up to there. And a transaction that
would count on both chains is kept to one by naming its chain. Here the signalling is
the nodes taking a proposal up, which takes knowing its policy; refusing is keeping
one's rules; the height is the base; and the epoch in every envelope keeps a
statement on its side.
### What every node judges
A change is valid if, in the document at its dependencies:
1. It has no dependencies and is the root the node was given (the genesis change, or
the epoch's snapshot). Else:
2. Its committer is a node that carries for a user there, one that isn't out and
hasn't stopped there, proven by the node's signature.
3. Apart from the node's own member entry and its own records of proposals, it
writes only envelopes, each naming the swarm, its epoch (this one, or for what is
carried over one before it), its path, its user and user ID, signed with a key
that is that user's there and not retired there, and the committer carries for
that user (but for a `node` statement, and for a proposal, a vote and their
release).
4. In a register (`_continuum/users/<name>`, `_continuum/claims/<kind>/<name>`): only
a new entry under its own digest. Under `_continuum/decisions/<proposal>`: only the
node's own record, once. Nothing replaced, nothing deleted, nor anything that
holds a register.
5. A `create`: its name has no creation there that stands (one not released, its user
not gone), a rule of the kind `user` lets the signer, and the signer is within its
limit. `key`, `node`, `vouch`, `ban`: within the limits; a `vouch` for a user that
isn't gone there. `accept`: by the user the vouch is for. `unvouch`: by the signer
of what it takes back. `retire`, a node stopping: by the key itself or one above,
the cut in the change's history, and for a key retiring itself as said under Cuts.
`release`: by the user of what it releases.
6. A `claim`: the kind is declared, a rule lets the signer, the signer is within the
kind's limit, and no claim of that name stands there.
7. In a namespace: at a listed depth, an envelope by a user of that name, replacing
what is there (`prev`); nothing within one, nothing replacing what holds one. At
a place the policy no longer covers: only its user's envelope that puts nothing
there.
8. `propose`, `vote`: by a voter present there (a fork: proposed by any user),
signed in this epoch; a proposal within its user's limit; a vote where the user
has none on that matter that isn't released.
9. `_continuum/access`, the swarm ID, the epochs: never.
Where a rule goes by a user's name (6, 7) and the name has several creations there, a
user that lost it, as far as the change's history shows (another remains, Two claims),
is refused; while none remains, each of them may write. So a user whose late creation
of a name is known can't scribble over the namespace, and one whose name a liar got
contested goes on writing, for those who ban the liar to read.
Judging reads the registers at the change's dependencies, and nothing else: no cut is
applied beyond a retiring or an unvouch that is there, and the policy with its
numbers, the root, the first keys and the names freed or given are as the decisions
that hold there leave them. A statement in a register was valid when it was taken, so
judging the next needs no look further back.
### Resolving
The **resolved registry** is a function of a set of changes that holds each change's
history (and, for reading, of the users followed). In five steps, each reading only
the steps before:
1. **Decisions.** Which proposals are decided, and which decision of each matter
holds (Decisions): the policy with its numbers, who the root is, the first keys
given, the names freed or given. They are read from statements valid as written
and from what the nodes recorded, so nothing below changes them.
2. **Who counts, and where.** The first root counts. A user counts at a statement if a
`create` or `vouch` for it counts whose signer counts there by a line that doesn't
pass through the user (so this ends), and the statement is within what every
`unvouch` of that one leaves. A statement counts if its user counts at it, its key
was the user's (added by statements that count, up to its creation, or given by a
decision), it is within every cut set for that key (by a `retire` that counts, or a
decision), and a node carrying for the user committed it (any node, for a proposal,
a vote and their release), within that node's cut if it has one. Bans of followed
users cut the same way. A statement past its user's limit, as the limit was where
it was made, doesn't count: of a user's creations, vouches or claims, those made in
sight of the others come first, then by digest. A user is **out** where no `create`
or `vouch` for it counts any more, and **gone** as said under Unvouching.
3. **Users' names.** A creation stands while it counts, isn't released, its user isn't
gone, and no decision freed the name or gave it away. Of the creations of a name
that stand: the one that remains (Two claims) has the name, or has it wait while it
is out.
4. **Names.** A claim stands the same way, where a rule of the policy that holds lets
its user: anyone vouched for where the rule says `"*"`, the user the voters named
where it says `"{voters}"`, else the user that has the name the rule gives. Of the
claims of a name that stand: one that has another in its history loses to it; of
the rest, the one that remains holds the name, or has it wait while it is out. A
kept name is held by the user that has the user name.
5. **Values.** A value in a namespace counts if its envelope does and its user has the
name. Where the document holds several at one place (written at once), those that
count, in Automerge's order. Where the one there doesn't count, the last that does,
back along `prev` (read in the document at the dependencies of the change that
replaced it).
A node keeps the resolved registry as it goes (a statement changes it around its own
name), and answers from it:
* `GET /registry/users/<name>`: the user that has the name, its keys and nodes, its
lines and which it stands on; or that it is contested, between whom; or out, since
where, and how long its names still wait; or gone.
* `GET /registry/claims/<kind>/<name>`: the holder, and whether the claim is settled;
or contested, between whom, with the changes that make each claim late and whose
nodes committed them. A user whose nodes showed both orders of two claims is
marked: it lied, or its nodes were apart.
* `GET /registry/decisions`: the root, the policy and its numbers, the voters present
and absent; what is proposed, who voted, until when, which nodes took it up and
which would refuse it; what is decided, which decision of each matter holds, and
what this node holds back or refuses.
* `GET /registry/uncovered/<user>`: what a policy no longer covers of that user's,
at the path it had, and how long it still stays.
* `GET /store`, as today, with a value that doesn't count marked, and the one that
counts in its place.
### Partitions, and coming back
Three things are "conflicting data", and are dealt with apart.
**Names claimed twice** are no partition: every node holds both claims and finds the
same holder, or contested. The users see it and one releases; nobody drops anything.
**Two epochs**, after a fork, a decision some refuse, or two remakes both decided,
are apart for good: each node is where its operator put it (by word or by bounds),
or where it got to first, and moves to the other side by being told to (it builds
that side's snapshot from the base it holds, and carries its own over).
**Changes one side refuses** are a partition nobody chose. A node is cut off from the
others when it holds a change they refuse, as everything it writes after builds on
it. That happens by three things, and each shows:
* **Its own node broke a rule**, by a bug or on purpose. The others refuse the change
and, its signature proving it its own, the node as a whole: `GET /peers` (`refused`,
with the change and why), the dashboard, `sync.refusedPeers` *(docs)*.
* **The two sides judge differently**: versions that differ, a bug in a rule. Each
refuses what the other takes; a node told so judges what it holds again and logs
that the policies disagree *(docs)*. Where they only resolve differently, their
stamps show it (Continuum changing under a swarm).
* **An actor forked.** A node committed two changes under one actor and number and
gave each to some. Automerge holds one change per actor and number, so a node
holding one can't take the other, nor anything built on it *(checked)*. Both carry
the same node's signature: a node offered the second holds proof, shows it, and
refuses the forking node from then on.
Coming back is the side's own choice, never forced: an operator has the node **drop**
what the others refuse (`POST /admin/refused/drop`, naming the peer whose word it
takes). The node's document becomes what it would be without those changes and what
was built on them, as a drop does today *(checked: `AppState::drop_taken`)*. Its own
user's statements among them go to the quarantine and are written again as they are:
an envelope doesn't name the change that carried it, so it is as valid in a new one.
Those that no longer fit stay there for the user to sign again: one replacing a value
that is gone, or a cut naming a change that was dropped.
Which side drops, for a fork of an actor? No rule can say: the forking node may have
ground the hash of the change it kept back, and a rule by hash would let it make
everyone drop a year's work. A node shows which of the two the users on its lines
hold, up to the root, and the operator follows them or doesn't. A node that just
joined and got the lone change from the forking node drops and loses nothing.
A side that doesn't want to come back stays as it is: the same swarm ID, another
history.
### Reaching the others
A node's neighbours come from the member list: a ring by key hash, and the pairs with
the lowest scores *(checked: `swarm::topology`)*. An adversary choosing its node keys
places its nodes around a victim, and then holds back what the victim writes, or what
it should receive: a claim that never reaches the root is never settled.
Who vouched for whom changes nothing in who connects to whom: a user's nodes needn't
reach its creator's. What a federated node has against being surrounded is its
operator's:
* it keeps as neighbours the other nodes of its user, and its seeds, whatever the
member list gives;
* it shows what would tell: a claim of its user that isn't settled long after
`witness`, and how old the last change of the root's nodes is that it holds.
Being surrounded costs time, not names: a claim that reached nobody is out of the
others' sight, and is contested or late like any such.
### The API, tokens, reading
* **Writing** through a node is handing it envelopes: it carries those of the users
it carries for, judged before they leave, as today. `scripts/continuum-signed`
signs them, votes among them.
* **A node's API users** (`--htpasswd`, a proxy's header) are its party's: they read,
and use the node's admin endpoints. The registry gives nobody anything on another
party's node.
* **Tokens** are taken only from the node itself and from nodes that carry for the
same user. (Today every node takes a token any member signed *(docs)*: a party's
node could let itself in anywhere.) Ended sessions are each node's, as in a swarm
without a registry.
* **Reading**: every party reads everything. The registry holds public keys, names
and addresses, nothing secret; a value only some should read is sealed for them, as
today.
* **Who may connect** is outside this design. Today it is the swarm key, a secret all
nodes share: it keeps those who aren't parties from connecting and reading, as far
as every party keeps it, and is no boundary between the parties. Nothing here
rests on it.
## Why it holds
**Promise 1**, by how a change is judged: every node judges a change before applying
it, at its dependencies, and refuses it with everything built on it *(checked:
`access::rules::judge_reading`, `access::screen`)*. The rules above read the document
at the change's dependencies alone.
**Promise 2.** Whether a change is valid depends on the change and its history; its
hash covers both. So honest nodes agree on every change, hold the same valid changes
once each has what the other has, and Automerge gives the same document for the same
changes. Resolving is a function of the changes and the follows; a node's clock enters
only into what its own node writes (its stamps, taking a proposal up), never into what
it reads from the document. What must hold for this, and doesn't everywhere today:
* the root given, never learned;
* no window without a policy (it is in the root), and no syncing under a policy the
node doesn't know;
* a batch judged at heads that differ from a change's dependencies only where nothing
the rules read changed in between: the registers must be among what screening takes
as read by rules *(checked: `screen::relevant` lists members, users, remakes, votes,
access and bans; claims must join)*;
* nothing a node keeps a bounded number of decides validity: a change pushed out of
the waiting ones comes again *(checked: `MAX_WAITING`)*;
* one change per actor and number. A forked actor breaks "hold the same changes"
until one side drops. Automerge names a change by actor and number where
Kleppmann's construction needs the hash.
**Promise 3.** A value counts only with an envelope whose signature checks against a
key that step 2 gives its user for the change that carries it. A key is a user's only
by statements signed with keys above it, up to its creation, signed by its creator,
and so on up to the root, in the root every node was given; or by a decision, which
is promise 7's. A node that carries
doesn't sign in the user's place: the envelope names its swarm, epoch, path, user and
user ID *(checked for swarm, path and user: `access::envelope::check`)*, so it counts
nowhere else and for nobody else. Across a remake: every node builds the snapshot
from changes it judged, so the snapshot holds only what counted.
**Promise 4.** Steps 3 and 4 name a holder only where exactly one claim remains.
**Promise 5.** Let c be a claim that counts and isn't released, and s a change the
root's node committed with c in its history and no other claim of the name that isn't
released (c is settled). Let c' be any other claim of that name. It isn't in the
history of s. The root's nodes are on every side, so s is a change by the side of c'
with c in its history and not c': c' is late against c, in every set of changes
holding s, for good. A late claim never holds a name while a rival stands, and c
stands until it is released, its user is gone (seen out for `gone` by a third of the
voters' own stamps, one of them honest), or a decision deals with it. So nobody takes
the name by a claim out of its sight. (A claim in its sight is one c was made after,
released then: c loses to it if that release stops counting, which is the release's
signer cutting off the key that signed it.) A new root changes nothing in this: the
old one stays on the lines of everyone it was on, and its changes stay what they are.
What is left to an adversary: making c late too, so that none remains. That takes a
change by a node of c's own side with c' in its history and not c. An honest node
that received c first never commits one: everything it commits after has c in its
history. So it takes a node of the claimant's side that lies, or that truly received
c' first: its own, or one of a user on a line it stands on, never one that vouched
for it unasked. If that node also committed changes with c and not c' (it was up), both
orders are in the document under its user's name, and the registry says so.
Before the root saw c: a claim c' out of its sight isn't late unless its own side
shows otherwise, so the two contest each other, or, where c's line saw c' first and
says so, c' holds. That is first come, first served as far as anyone can witness it.
What the assumptions are: signatures and hashes hold; an honest node's changes build
on all it has received (Automerge does that); the changes the root's nodes commit
count (their node keys aren't stolen, or are cut off once they are).
**Promise 6.** No rule for what users write reads a vote or waits for another party;
a write is refused only by what its own history holds, and counts from the start.
**Promise 7.** Let E be the voters present at a proposal, n of them, fewer than a
third malicious.
* *None that honest voters didn't want.* A decision was taken up by the nodes of more
than 2n/3 of E, so by more than n/3 honest ones. An honest node takes a proposal up
only while it holds votes for it by more than 2n/3 of E, more than n/3 of them
honest voters', none released there, each still running by its clock.
* *Not from the past.* So votes crafted later with old dependencies, or kept back
until they ran out, bring no honest node to take anything up. Their proposal is
decided only by its makers' own records: if they were two thirds of the voters
then, and write the records now. That is what "then or later" is for, and why a
node that was up holds such a decision back.
* *The same one everywhere.* Which decision of a matter holds is read from the
proposals: which was made in sight of which, and their digests. That is the same
from the same changes, and no node's records or clock enter into it.
* *Two of one matter.* A voter has one vote on a matter at a time. So two proposals
of one matter are both decided only where more than a third of E voted for both:
voters who changed sides in between, or malicious ones, who are too few. Where
voters changed sides the later holds, which is what they wanted.
* *One side of a partition.* A decision takes the nodes of more than half of all the
voters. Of the sides of a partition, one at most has those, however long it lasts
and whoever is counted absent. A malicious voter with nodes on both sides can tip
both: that is not promised against.
* *Seen for long.* A voter shown silent, or a user shown out, for a length of time
has a third of the voters with two stamps of one node that far apart: one of them
honest, whose clock went that far. Fewer than a third, stamping as they like, show
nothing.
* *A remake changes nothing.* The snapshot is a function of the base, which every
node holds and judged itself. The coordinator chooses the base, and an honest
voter's node takes up none that leaves out what it holds, and one a proposal: two
bases named for one remake each need more than two thirds, so neither or one is
decided. Who votes next is in the remake's proposal, for all to see before they
vote for it.
* *Nobody is moved.* A node refuses any decision by its operator's word or bounds,
and follows a fork by its operator's word alone.
* *With other fractions.* Write d, a and s for `fractions.decide`, `fractions.all`
and `fractions.seen`. A decision no honest voter voted for takes d of those present
malicious. Two proposals of one matter both decided, with nobody changing sides,
take more than 2d − 1 of them voting both ways, which at a half or less is nobody.
One side of a partition at most decides while a is a half or more. What was seen
for long holds against fewer than s of the voters.
**Unvouching** takes away no more than its signer could have kept from seeing: what
the unvouched user signed stays if a node of the unvoucher or the root committed a
change that has it in its history and not the unvouch. Those changes can't be taken
back, so the point only moves forward as more of them show up.
## Roads not taken
* **Agreement among the parties for every write** (PBFT and the like) would give one
order of claims, final at once. It needs a fixed set of voters of which fewer than
a third fail, halts the smaller side of a partition, and makes every claim wait for
a round. It works against what continuum is for: no node special, each working on
its own, both sides of a partition going on. Here only decisions take it, a few a
year.
* **A registrar**: every claim signed by the root before it counts. That is the
root's witnessing made a condition: one order, but nothing counts while the root is
away, and the root decides alone. Here the same witness only makes final what
already counts.
* **Deciding by a hash or a timestamp** of the claims: whoever crafts later chooses
the lower hash and the earlier time.
* **Deciding by seniority** (the user nearer the root holds the name): always a
holder, but a senior user whose line kept silent takes a junior's name by
backdating, and a root that lies takes any. Contested, with `release` to give a
name up, fails safer: a name is blocked in the open, not taken.
* **Votes by nodes**: a party runs as many nodes as it likes.
* **Voters by a rule** (the users the root itself created, whom nobody else can
multiply): who takes part and who doesn't is known to people, not to a rule. So
those who vote say who votes next.
* **Recommending two thirds of all the voters**, absent or not: then nothing is
decided once a third left for good. Leaving the absent out, down to half, lets
those who stay decide; the price is a malicious voter on both sides of a long
partition. A swarm that wants it otherwise sets `fractions.all` to two thirds.
* **Recommending a majority, not two thirds**: a third that votes both ways must not
get two proposals of one matter decided, to choose between them. Two thirds stand
that against fewer than a third; a majority stands it against nobody.
* **One decision a matter, each node taking up the first it sees and no other**: two
would never both be decided. But two made at once split the nodes between them,
neither is, and the matter stands still until the next epoch. Letting both be
decided, and the later hold, never stops.
* **A clock of the swarm** (the latest time a third of the voters have stamped) for
who is absent: it sets one node's clock against another's. Two stamps of one node
measure a length of time by one clock, and the document says what that node had
seen at each.
* **A policy that changes only by going apart**, each operator saying yes: nobody
would be bound, and a swarm would split at every change. A decision binds those
who don't refuse it.
* **Unvouching by anyone above**: a user in between could put out one further down
without taking along all below. But the root is above everyone, and would put out
whom it liked, for good.
* **The users on all of a user's lines as its witnesses**: whoever vouched for a user
unasked would get a say about its names.
* **Neighbours by who vouched for whom** (a node keeping its creator's nodes): a
claim would reach the root whatever the member list gives, and a user would have
to reach whoever brought it in.
* **Rules that know what a name is** (an address's parts as numbers, two spellings
of one address): continuum would have to know every kind. The structure of a name
is the same for all of them; what it means is the application's.
* **Numbers the design fixes**, the fractions above all: they would keep a swarm's
voters from deciding badly by rules none of them chose. Every number is the
swarm's; whoever won't bear a decision refuses it, and goes on with those who
think alike.
* **Users voting as soon as they are created**: whoever creates users carries every
decision.
* **Voters let in by a decision, without a remake**: nobody fills the votes, and
nobody waits for a remake. But the voters of before make a decision "of then"
later (the root alone, for the time it was the only one), which only nodes that
were up can tell. A remake is what puts that out of reach.
* **An unvouch undoing everything its user ever signed**: nothing to backdate, but it
takes back the user's releases, and no vouch brings the user back as it was. **Or
only what the user signs in sight of it**: the user goes on by never building on
it. **Or from where each node received it**: that is in no document, so two nodes
would resolve the same changes differently.
* **A user that was gone coming back by a vouch made in time**, out of the sight of
those who saw it gone: a vouch crafted with old dependencies then blocks any name
claimed in good faith since.
* **First keys given by those who vouch for the user**: fewer to convince, but a
user's creator takes its name where nobody else vouched.
* **A registry of only what is signed**, from the start: nothing unsigned ever
counts, but it is incomplete for as long as users take to come, and what reads it
needs the repository beside it meanwhile.
* **The root's clock** for who is absent and when votes run out: the root is the one
being replaced.
* **A document per user**, each written by one party, a claim counted across them (as
in Secure Scuttlebutt's feeds). A fork of an actor then spoils only the forker's
own document, not the swarm, which is the one thing this design can't give. It
needs what continuum doesn't have: many documents, and what one had seen of the
others recorded by hand. One Automerge document records that by itself.
## What members are trusted with, then
[`trust.md`](trust.md) lists what a member can do today. In a federated swarm:
| Today | Then |
|---|---|
| Write or delete any user data | Only envelopes, of users it carries for; nothing plain, nothing deleted from a register |
| Write as any user | An envelope needs the user's key |
| Edit the member and user registries | Its own member entry; users come and go by statements, which only add |
| Forge votes, commit a remake alone | Votes are users', signed; a decision takes two thirds of the voters; every node builds the snapshot itself |
| Make the swarm forget peers | A forget record of another's node is refused; whom a node talks to is its own |
| Advertise an epoch a rejoining node follows | A node follows a remake it sees decided, or an epoch its operator gave it |
| Be a joining node's first sync | Judged from the root on, by the policy in it |
| Say it is ready, be a catch-up source | Still believed: it can hold changes back, not add any. A joiner syncs with the others after |
| Sign a token every node takes | Taken by its own party's nodes only |
| Say whom it reaches, how stable it is | Still believed: it bends who syncs with whom; a node keeps its seeds and its user's other nodes whatever |
| Say when it was alive | Believed for itself only; it takes a third of the voters to show a voter silent, or a user out, for long |
"Member" stays the word for a node in the swarm; the unit that is trusted or not is
the party.
## Continuum, or the application
In continuum, as rules every node judges by and data that parametrizes them: users by
statements (creating, vouching and unvouching, lines, keys, cuts, nodes that carry),
the namespace rule, claims by kind with their rules and limits, late and contested,
bans, lengths of time read from the stamps, decisions by the voters (the numbers, the
policy, the root's seat, a user's first keys, names), remakes that every node builds,
going apart and refusing a decision, resolving and its API, the drop an operator asks
for. All of it is general: a swarm with other kinds, or none, uses the same.
In the application, for the registry: which kinds there are and their rules (data in
the root), what a host's entry holds, the client that signs (an extension of
`scripts/continuum-signed`), moving the repository in, and writing out the files the
consumers read. Whether a host's entry may use a name is the application's check
against the resolved registry, when it writes the files out. So are a node kept as
an archive of every epoch, and whatever brings a vote from a person to a node.
More policies later are more data where a rule has a parameter, and a new rule in
continuum, with a policy of its own, where it hasn't; either way a decision.
What changes in continuum besides the rules, under the federated policy only: policy
version 2; the envelope's payload names the user ID and the epoch; a node is given its
root, syncs under no policy it doesn't know, takes tokens from its party only, writes
no sessions into the document, keeps its seeds and its user's other nodes as
neighbours, answers for an epoch it left while it keeps it, takes no remake but one
its voters decided, rejoins no epoch because peers advertise it, and refuses what its
bounds say. On the wire: a handshake says which epochs its node keeps, and a version
speaks the one before its own too. [`trust.md`](trust.md), [`access.md`](access.md),
[`remakes.md`](remakes.md) and the glossary get the federated case: party, statement,
line, voter, claim, cut, late, contested, settled, out, gone, uncovered, seen for,
taking a proposal up, a decision that holds, bounds, and a ban of a user beside a ban
of a node.
## Continuum changing under a swarm
Parties upgrade when they like, so a federated swarm always runs several versions of
continuum. Given the same changes they must agree on three things: which are valid,
what the registry is, and, byte for byte, the snapshot a remake builds. Whether two
versions do can't be told by looking at them, and a version's number says what its
author remembered to say. So the design goes by what nodes compute:
* **Every stamp says what its node computes.** With its alive stamp a node records
the digest of the snapshot it would build at that change's history. A node that
holds exactly those changes builds its own and compares. Two digests for one
history prove that two nodes resolve differently, signed by both, at the first
input where they do: it shows (`GET /peers`, the dashboard) long before a remake
would have sent them to two epochs. In a quiet swarm nodes hold the same changes
most of the time.
* **A remake is taken up by its result.** A voter's node builds the snapshot from the
base before it takes the remake up, and records the ID of the epoch it got. The
remake is decided for the ID that the nodes of two thirds of the voters got. A
node that got another stays where it is, and says so.
* **A node checks itself before it writes.** Started with a version it didn't run
before, a node judges and resolves again what it holds *(docs: a node told that
policies disagree does so today)*, builds at the histories of the latest stamps it
holds of other parties' nodes, and compares. Where it differs it writes nothing,
and shows it, until its operator says to go on.
* **A change of the rules is a change of policy.** What a version judges or resolves
differently on purpose, it does under a policy of its own (a part of the policy
that versions before don't know), which the swarm decides. A change is judged by
the policy that holds in its history, so a version judges what was written before
as it was judged then, and carries the rules of every policy that held in an
epoch it can still walk. A node knows a policy if it knows everything in it: it
goes by what the policy holds, not by its number, and takes up none that has a
part it doesn't know.
* **Vectors.** Continuum carries recorded inputs with what must come of them: changes
and whether they are valid, sets of changes and the snapshot they resolve to. Each
test named under Attacks leaves some. A release adds vectors and changes none,
and every version passes all of them: a rule changed without a policy fails there,
before it is released.
What can't change under a swarm, whatever is decided: how an envelope is read and
what its signature covers (a snapshot carries envelopes as they were signed, from
epoch to epoch); a statement's digest; a change's hash, and what Automerge makes of
the same changes; a snapshot's bytes for the same base and policy. A new form of any
of them comes beside the old one, under a policy.
What changes freely: everything a node does for itself. Its API, its storage, how it
syncs and with whom. The wire is between two nodes, not the swarm's: a version
speaks its own and the one before, so that parties upgrade one after another
*(today every wire change parts the versions: `transport::wire::VERSION`)*.
And a fork it is when nodes that hold the same changes don't come to the same, and
neither gives way. It shows by a change one takes and the other refuses, by two
digests for one history, or by two IDs for one base: by what was computed, never by
a number. Which of the two is the swarm's no rule says. The parties see how many of
them say which, and upgrade, drop, or go apart.
## Attacks
Each with what honest nodes do, the promise it touches, and what shows it: `unit` in
the module named (`access::statements`, `access::claims`, `access::resolve`,
`access::decisions` are new), `sim` a simulation in `src/simtest/federation.rs`,
`argued` where no test can. Each test leaves vectors (Continuum changing under a
swarm).
Statements and users:
| # | The adversary | Honest nodes | Promise | Shown by |
|---|---|---|---|---|
| 1 | Signs a statement with a key that isn't the user's | Refuse | 3 | unit, statements: `a_statement_needs_its_users_key` |
| 2 | Puts another user's statement into a change of its own node | Refuse, its node not carrying for the signer; but for what a decision is made of | 3 | unit: `only_a_users_node_carries_it` |
| 3 | Replaces or deletes a statement, or what holds the register | Refuse | 2, 3 | unit: `a_register_only_grows` |
| 4 | Offers a statement again; or an envelope signed for another swarm, epoch, path or user ID | Nothing where it is there under its digest; refuse elsewhere | 3 | unit, exists for swarm and path: `what_an_envelope_must_be`; `signed_for_its_user_id_and_epoch` |
| 5 | Creates a user listing a victim's public key beside its own | The victim's envelopes name the victim's ID: they count for no other | 3 | unit: `a_key_in_two_creations` |
| 6 | Users vouch for each other in a ring away from the root; a user vouches for its creator | None is vouched for by it | 3 | unit, resolve: `no_line_no_user` |
| 7 | Creates a name in sight of its creation | Refuse | 4 | unit: `a_name_created_once` |
| 8 | Creates a name another has, with old dependencies | Hold both; late once the root saw the first; contested at worst before | 4, 5 | unit: `a_backdated_creation_is_late` |
| 9 | Gets a user's name contested to free its names | Its claims stand without the name | 5 | unit: `claims_stand_without_the_name` |
| 10 | Creates many users, each claiming many names (sybils); creates them out of each other's sight to pass the limit | Refuse past the limits in sight; out of sight, those past them don't count. The limits bound how fast users come, not how many: each shows below its creator, for all to see, to ban, and to go with it when it is unvouched | 6 | unit: `limits_on_creating`, `past_a_limit_out_of_sight` |
| 11 | Adds keys, nodes or statements without end | Refuse past the limits | 6 | unit: `limits_on_a_register` |
| 12 | Vouches for a user unasked, then says it saw a rival of its claim first | Its nodes are no witnesses: a user stands on no line through a vouch it didn't accept | 5 | unit: `a_vouch_unasked_gives_no_say` |
Unvouching:
| # | The adversary | Honest nodes | Promise | Shown by |
|---|---|---|---|---|
| 13 | Unvouches a user it didn't vouch for | Refuse | 3 | unit: `only_the_voucher_unvouches` |
| 14 | Unvouches with old dependencies, to undo what the user did since | What its own nodes or the root's had seen of the user stays | as said under Unvouching | unit: `an_unvouch_undoes_nothing_seen` |
| 15 | The unvouched user signs on, with dependencies from before, or across a partition | Hold it as part of another's history only; it doesn't count; after healing every node finds the same | 2, 3 | unit: `past_an_unvouch`; sim: `an_unvouch_across_a_partition` |
| 16 | A user is unvouched: what of those it brought in, and its names? | One that another vouched for stays in, without doing anything. The others are out from the same point; a vouch after brings them back whole. Their names wait | 4 | unit: `a_second_line_keeps_it`, `those_it_brought_in`, `a_vouch_brings_back` |
| 17 | An unvouched user's nodes keep sending | Refused as peers; nothing taken from them | 6 | sim: `nothing_from_an_unvouched_node` |
| 18 | Claims a name of a user that is out, or creates its user name | Refuse: the names wait until the user is gone, or a decision frees them or gives them away | 5 | unit: `names_wait_while_out`, `a_decision_frees_or_gives` |
| 19 | Has a user unvouched, then stamps its nodes `gone` apart at once, to free the names | Gone takes a third of the voters | 5 | unit: `gone_by_a_third_of_the_voters` |
| 20 | Vouches for a gone user with old dependencies, over a name claimed since | Gone stays: no vouch brings it back | 4 | unit: `gone_stays_gone` |
Namespaces:
| # | The adversary | Honest nodes | Promise | Shown by |
|---|---|---|---|---|
| 21 | Writes into another's namespace, signed by itself | Refuse, and the node as a whole | 3 | unit: `a_namespace_is_its_users`; sim, as `a_member_writing_against_the_rules` today |
| 22 | Writes plain values, or protocol data but its own member entry | Refuse | 3 | unit: `nothing_plain` |
| 23 | Puts an old envelope back | Refuse: it doesn't replace what is there | 3 | unit, exists: `what_an_envelope_must_be` |
| 24 | A user with a late creation of a name writes in the namespace | Refuse where its change's history shows it lost the name; else hold it and leave it out | 3 | unit: `a_user_that_lost_its_name` |
Claims:
| # | The adversary | Honest nodes | Promise | Shown by |
|---|---|---|---|---|
| 25 | Claims a held name in sight of the claim | Refuse | 4 | unit, claims: `first_come_first_served` |
| 26 | Claims a settled name with old dependencies, long after | Hold both; it is late, the holder holds | 5 | unit: `a_backdated_claim_is_late`; sim: `a_backdated_claim_loses` |
| 27 | Two honest users claim on two sides of a partition | Hold both; every node finds the same: one holder, or contested; a release leaves one | 2, 4 | sim: `a_partition_heals_to_one_answer`; unit: `a_release_ends_a_contest` |
| 28 | Claims `x.tv.r` as another; claims a top-level domain as another than the root; claims `wiki.r`, which the rules leave to the voters | Refuse: the kind's rule. A kept name needs no claim; a claim by a user that doesn't have the name its rule gives doesn't count; the voters' names are given by a decision | 4 | unit: `a_kinds_rules`, `a_kept_name`, `a_rule_goes_by_who_has_the_name`, `a_name_by_the_voters` |
| 29 | Claims past its limit, in sight or out of sight of its other claims | Refuse in sight; out of sight, those past the limit don't count | 4, 6 | unit: `a_limit_in_sight`, `past_a_limit_out_of_sight` |
| 30 | Claims `10.243.foo.bar`; claims one address in two spellings | Taken: a name is compared as written. The application claims one spelling and reads no other | 4 | argued (Claims) |
| 31 | A user on a line the claimant stands on says it saw a rival first, after the claim is settled | The name is contested; the registry names the changes and the user, and marks it if it showed both orders | not promised | unit: `a_line_contests`; sim: `both_orders_show` |
| 32 | A whole line kept silent, then claims with old dependencies | Before the claim is settled: contested. After: late | 5 | unit: `silence_before_settling` |
| 33 | Releases a claim with a stolen key, an accomplice claims | Once the key is cut off the release doesn't count: the accomplice's claim, in sight of the first, loses to it | 3, 4 | unit: `a_release_cut_off` |
Keys:
| # | The adversary | Honest nodes | Promise | Shown by |
|---|---|---|---|---|
| 34 | Steals a device key and signs with it | Take it until a key above cuts it off, then leave out what is outside the cut; a value it replaced is read again | 3 | unit: `a_retired_key_counts_within_its_cut`, `the_value_before_counts_again`; sim: `a_stolen_key_cut_off` |
| 35 | Signs with a retired key and old dependencies | Hold it (valid as written), leave it out | 3 | unit: `backdated_past_a_cut` |
| 36 | With a stolen device key, retires the keys above or beside it | Refuse | 3 | unit: `no_key_retires_one_above` |
| 37 | With a stolen device key, retires that key from long ago, to undo what its user signed | Refuse: a key retires itself from where it stands | 3 | unit: `a_key_retires_itself_forward` |
| 38 | Steals a user's first key | The name is the thief's too, until a decision gives the user first keys anew, with a cut for the stolen one | 7 | unit: `first_keys_by_a_decision` |
| 39 | Cuts its own key off early to take back a vouch or a release | It is taken back; a user with a second vouch stays | not promised | unit: `a_second_vouch_stands` |
| 40 | A key is retired on one side of a partition while it signs on the other | After healing, every node leaves out the same | 2 | sim: `a_cut_across_a_partition` |
| 41 | Steals a node's key: commits changes as a witness | They count until the node is stopped with a cut, then not | 5 | unit: `a_stopped_node_witnesses_nothing` |
Decisions, the root, remakes:
| # | The adversary | Honest nodes | Promise | Shown by |
|---|---|---|---|---|
| 42 | Forges a vote; votes as a user that isn't a voter, or one absent | Refuse | 7 | unit, decisions: `a_vote_is_a_voters` |
| 43 | Brings in many users to vote, also while a remake's votes run | They vote only if a remake names them: by a list, or as all there were when it was proposed, which those who vote saw | 7 | unit: `who_votes`, `all_users_as_proposed` |
| 44 | A remake that says all users, after one user brought in many | They vote in the next epoch: all users is whoever brought users in. The voters see the list before they vote, and name one of their own instead | not promised | unit: `voters_are_the_remakes` |
| 45 | Records taking a proposal up without the votes, or with released ones | Its record is its own: honest nodes record what they hold, and it takes two thirds | 7 | unit: `taken_up_with_the_votes` |
| 46 | Keeps votes back until they ran out, or crafts them with old dependencies, then shows them | No honest node takes the proposal up: it isn't decided. One decided by its makers' own records, long after (two thirds of their keys stolen), is held back by every node that was up, and shown | 7 | unit: `taken_up_while_the_votes_run`; sim: `a_decision_from_the_past` |
| 47 | Two proposals of one matter both decided: voters changed sides, or a third voted both ways | Every node acts on the later, the same from the same changes; the other shows as replaced | 7 | unit: `the_last_holds`; sim: `two_decided_one_holds` |
| 48 | Stamps its nodes far apart at once, to show the others silent | It takes a third of the voters to have seen a voter silent for long | 7 | unit: `seen_for_long_by_a_third` |
| 49 | Stays away to hold decisions up | Absent after `absent`: it doesn't count. Half the voters staying away do hold them up: the rest fork | 7 | unit: `absent_users_dont_count`, `more_than_half_of_all` |
| 50 | Asks the voters for another user's first keys, saying it is that user | It takes two thirds of them, and the days a vote takes, shown to all; the user signs on meanwhile | not promised | argued (Decisions) |
| 51 | Puts a voter's vote into a change of its own node; keeps one back; offers one again | Taken: the signature is the voter's. It counts while it runs; the register holds it once | 7 | unit: `a_vote_by_any_node` |
| 52 | A decision lowers a limit below what users have, or sets a number that makes no sense | Nothing is taken away, and no more is taken until below; the number has the result it has | 6 | unit: `a_lowered_limit_takes_nothing` |
| 53 | Opens proposals without end | Refuse past `limits.proposals` | 6 | unit: `limits_on_proposals` |
| 54 | The voters decide a policy that covers a user's names no more; another user writes over what is uncovered | It doesn't count where the policy holds, and shows aside. Only its user takes it away, nobody changes it; it is dropped once seen there for `uncovered` | not promised | unit: `what_a_policy_no_longer_covers`, `uncovered_is_only_taken_away` |
| 55 | Names a base for a remake that leaves out what the swarm holds | No honest voter's node takes it up | 7 | sim: `a_stale_base_is_not_taken_up` |
| 56 | A remake's coordinator names two bases, or none | A node takes the remake up with one base: one is decided, or neither. Then the remake is proposed again | 7 | sim: `two_bases_no_remake` |
| 57 | Runs most of the nodes; serves a snapshot of its own | Nothing reads a count of nodes; every node builds the snapshot itself | 7 | sim: `every_node_builds_the_snapshot` |
| 58 | Signs a claim "in the epoch before" after a remake, to pass as carried over | It contests only what the snapshot holds unsettled, until the root's node wrote in the new epoch; then it is late | 5 | unit: `carried_over_or_pretending` |
| 59 | A thief's statements are in the base of a remake | They stand: a remake makes final. The vote's days are the time to cut the key off | not promised | unit: `a_cut_reaches_to_the_snapshot` |
| 60 | Tells a node that was away of an epoch that isn't the swarm's | The node rejoins no epoch that peers advertise: it finishes the old one from archives and builds the next itself, or takes the ID its operator gave | 1 | sim: `away_over_a_remake`, `back_by_an_id` |
| 61 | A node back after several remakes offers what its user signed epochs ago | Refuse: an envelope is taken in its epoch and the next. Quarantined, for the user to sign again | 3 | unit: `carried_over_one_epoch_only` |
| 62 | A version builds another snapshot from a remake's base | Its node records another ID. The remake is decided for the ID two thirds got; the node stays, and says so | 7 | sim: `a_remake_by_its_result` |
| 63 | The old root comes back after its seat went; crafts users "from before" | It is a user like the others: what the proposal's history doesn't hold of it is an ordinary user's | 7 | unit: `the_old_root_comes_back` |
| 64 | Two thirds of the voters turn on the rest | They remake, seat a root, set any number (a share of the voters that lets one decide for all), change the policy, and give a user's first keys to another, or the names of a user that is out: a name taken, in the open. They forge no signature and move nobody to other rules; the rest ban, refuse, or fork | not promised | argued (Going apart, Refusing a decision) |
| 65 | Offers a statement signed on the other side of a fork | Refuse: it names its epoch | 3 | unit: `signed_for_its_user_id_and_epoch` |
| 66 | A fork naming its proposer the only voter | A node follows only by its operator's word, which names the proposal; what that says shows | 7 | sim: `a_fork_by_choice` |
| 67 | A decision breaks a node's bounds: the voters let one of them decide for all | The node refuses it and moves to the epoch built without it, the same for every node that refuses: they go on together | 7 | unit: `bounds_on_the_numbers`; sim: `those_who_refuse_meet` |
| 68 | Says it refuses a decision, or hands out the ID of an epoch without it, to draw nodes there | A node goes there by its own bounds or its operator's word only | 1 | unit: `refusing_is_ones_own` |
Nodes and syncing:
| # | The adversary | Honest nodes | Promise | Shown by |
|---|---|---|---|---|
| 69 | Sends different changes to different peers, each under its own number | Take all: changes made at once | 2 | sim: `two_histories_merge` |
| 70 | Forks its node's actor | The nodes that took each split; one offered both holds proof and refuses the node; a side drops by its operator's choice | not promised | sim: `a_forked_actor_is_proven`, `a_side_drops_a_fork` |
| 71 | Holds changes back, or passes on some | They come by other nodes; a joiner caught up with it syncs on | 6 | sim: `withheld_changes_arrive` |
| 72 | Places its nodes around a victim by choosing their keys | The victim's node keeps its seeds and its user's other nodes as neighbours; a claim not settled for long shows. It costs time, not names | 5, 6 | sim: `around_a_victim` |
| 73 | Hands a joining node another root, or changes from before the policy | Refuse the handshake; there is no before | 1 | sim: `a_joiner_takes_its_root_only` |
| 74 | Signs a token for any user with its node's key | Not taken by another party's node | 3 | unit: `a_token_of_ones_party` |
| 75 | Lies in its member entry (ready, reached, stable) | Sync and trees bend; nothing gets in that way | 6 | argued (Reaching the others) |
| 76 | Sends envelopes that don't check, without end | Refuse each (a signature costs about 30 µs to check *(docs)*); the node is refused as a whole after the first of its own | 6 | sim: `a_refused_node_costs_little` |
| 77 | Writes its own member entry without end; fills the waiting changes | Take them no faster than a budget per party: put off, never refused; a share of the waiting each | 6 | sim: `a_party_past_its_budget_waits` |
| 78 | Crafts a change Automerge won't take, or that writes in an object nowhere to be found | Refuse *(checked: the rules refuse the second today)*; only the message that brought it fails | 1 | unit: `a_change_writing_nowhere` |
Rules, and the swarm as it is:
| # | The adversary | Honest nodes | Promise | Shown by |
|---|---|---|---|---|
| 79 | A rule differs between versions (a bug) | Each side refuses what the other takes, or their stamps name two digests for one history: each logs and shows it. A node that finds it at its first start with a version writes nothing. One side upgrades and drops | not promised | sim: `a_side_drops_and_comes_back`, `two_digests_one_history`; unit: `a_node_checks_itself` |
| 80 | A policy version a node doesn't know: in the root, or decided | It doesn't sync, and takes no such proposal up: a policy is decided only with two thirds of the voters' nodes knowing it | 1 | unit: `no_sync_under_an_unknown_policy`, `an_unknown_policy_isnt_taken_up` |
| 81 | A root that lies and isn't replaced; a user nobody unvouches | The parties ban it from a cut on and read on without it | not promised | unit: `a_ban_for_those_who_follow` |
| 82 | A banned user's name claimed by one who bans it | The node refuses its own user's write: the others would | 1 | unit: `no_write_by_ones_bans_alone` |
| 83 | A partition that outlasts the absence period | One side at most decides: a decision takes the nodes of more than half of all the voters. With a malicious voter on both sides of an even split, both may: every node then acts on the later; two remakes are two epochs | 7, not promised | sim: `one_side_decides`; unit: `the_last_holds` |
| 84 | None: a swarm that isn't federated | Everything as today | Optional | the existing tests, untouched; `bench_screening` |
## The registry on it
```
_continuum/users/tv/<digest> statements about and by tv
_continuum/claims/ip4/10.243.0.7/<digest> tv's claim of 10.243.0.7
_continuum/claims/name/r/<digest> the root's claim of the top-level domain r
tv/hosts/toaster envelope: {"nets": {"retiolum": {
"ip4": "10.243.0.7", "ip6": "42:0:…",
"aliases": ["toaster.tv.r"],
"ed25519": "…" }}}
```
The kinds are those shown under Claims, each name written as the hosts use it.
`toaster.tv.r` is kept for tv by the kind's rule and needs no claim. A name that is
no user's, right below the top-level domain (`wiki.r`), is the voters' to give: a
proposal names it and a user, and decided, that user holds it. An IPv6 address is
generated from the net, the user's name and the host's today; a kind `ip6` with the
separator `:`, first come, first served like `ip4`, holds whether or not anyone
generates honestly, for addresses written out in full. The export reads an address
or a name only in the one spelling it writes.
**Moving in.** The root is made from the repository by a script
(`scripts/kartei-import`): the kinds and their rules; the maintainer as the root, with
its keys and nodes; and for each namespace of the repository its hosts as they are,
plain, their addresses as claims in the namespace's name, and the name kept for the
root to create (a rule of the kind `user` for each), so nobody else creates a user
over a namespace that is there. The root is trusted as it is (every party is given its
hash), so what it holds counts without signatures: a host from the root counts until
its user signs over it, and a claim from the root is held by whoever has its name.
The maintainer then creates each user as it comes with a key; from then on the user
signs its own, and votes from the next remake that names it. A namespace whose user
never comes stays as the root has it.
The limits in the genesis policy: 64 keys, 64 nodes and 256 addresses a user for a
start. The import may set keys and addresses to twice the most any user has in the
repository; a decision changes them after.
**Writing out.** `scripts/kartei-export` reads the resolved registry from a node its
operator trusts (its own) and writes the tree the consumers read
(`<user>/hosts/<host>/retiolum/{aliases,ip4,ip6,ed25519.key}`), leaving out what
doesn't count and any address or name its host's user doesn't hold. Run on each
change of the document (the store's stream), it replaces the merge button.
## Stages
Each leaves the system working, and is a few patches with its tests, which leave
their vectors. At every stage
the existing tests pass untouched (84).
1. **A federated swarm's frame.** `--genesis-policy` (a file, written into the
genesis change with the root, its keys and nodes); a federated node is given its
root, syncs under no policy it doesn't know, takes no remake yet, takes tokens
from its party only, keeps ended sessions to itself. Tests: 73, 74, 80, 84, and
the policy's existing tests under a policy from the root.
2. **Statements.** The users' registers (`create`, `key`, `node`, `vouch`, `accept`,
`release`), the user ID and the epoch in envelopes, nodes carrying for users and
the rule for committers by it, nothing plain, limits. Resolving, step 2 without
cuts, and `GET /registry/users`. The client signs statements. Tests: 1 to 7, 10,
11, 22.
3. **Namespaces.** The rule, with `userSigned` below a name. Tests: 21, 23. From here
the registry runs with users and hosts, names not yet unique.
4. **Claims in sight.** Kinds, names as they are written and rules by their
structure, kept names, limits, `claim` and `release`, judged as written;
resolving step 4 for claims in sight. Tests: 25, 28, 29, 30.
5. **Out of sight.** Late, contested and settled, for names and for users' names; a
claim's side by the lines its user stands on; the root's node writing when a
claim reaches it; `GET /registry/claims` with what makes a claim late; values
marked in `GET /store`. A node keeps its seeds and its user's other nodes as
neighbours, and shows a claim long unsettled. Tests: 8, 9, 12, 24, 26, 27, 31,
32, 69, 71, 72.
6. **Cuts.** `retire` and the keys above, a node that stops, what doesn't count left
out, the value before read again, the client listing what stopped counting. Tests:
33 to 37, 39, 40, 41.
7. **Unvouching.** `unvouch`, a user giving itself up, one that another vouched for
staying, those brought in going with it, the names of a user that is out waiting,
nothing taken from an unvouched user's nodes. Tests: 13 to 18.
8. **Coming back.** The drop an operator asks for, a forked actor proven and shown
with what the users on one's lines hold, the budget and the share per party.
Tests: 70, 76 to 79.
9. **Bans** for those who follow. Tests: 81, 82.
10. **The registry.** Import, export, and a federated swarm of the simulations with
the registry's kinds and a few hundred hosts. From here it can be used; it only
grows.
11. **Decisions.** The voters a root change names; lengths of time read from the
stamps, who is absent, a user gone; `propose` and `vote` taken from any node,
the nodes' records, taking up, the decision that holds;
`GET /registry/decisions`. What is decided: a number, a user's first keys, the
names of a user that is out, a name the rules leave to the voters. The
simulations start from a root change naming several voters, as a remake's does.
Tests: 19, 20, 38, 42 to 49, 51, 52, 53, 83.
12. **The root's seat.** Tests: 63.
13. **Remakes.** The coordinator and the base, taking a remake up, the snapshot
every node builds and what it leaves out, carrying over from the epoch before
and no further, what becomes final; peers answering for the epochs they keep,
and saying which; a node walking them, or rejoining by an ID its operator gave,
and by none that peers advertise. Who votes next, as the proposal says. The
digest with every stamp, a node checking itself before it writes, a remake
taken up by its result. Tests: 55 to 62, 79.
14. **The policy by a decision.** The policy as a matter, a node that doesn't know
it, what it no longer covers: shown aside, taken away by its user only, dropped
after a time, moved at a remake. Tests: 54, 80.
15. **Going apart, and refusing.** A fork followed by an operator's word, with who
votes on its side. Refusing a decision: a node's bounds, the epoch without the
decision. Tests: 65 to 68.
## What was checked, and what is assumed
Checked in the code:
* A change is judged at its dependencies, or at heads that hold the same for what
rules read, before it is applied; a refused change keeps out what builds on it; a
member whose own change broke a rule is refused as a whole (`access::rules`,
`access::screen`).
* What screening takes as read by rules is a list (`screen::relevant`).
* An envelope is checked for its swarm, path, `prev`, signature, and its user's key
in the registry as the writer saw it (`access::envelope::check`).
* The genesis node writes the users it starts with and their keys in its first change
(`access::registry`), so the policy can be written with them.
* Automerge refuses a second change at an actor's number (`DuplicateSeqNumber`), and
gives the change that made an operation (`hash_for_opid`), so the change carrying a
value is found from the document.
* A drop exists and does what coming back needs (`AppState::drop_taken`); what is new
is an operator asking for one its own rules wouldn't make.
* Neighbours are computed from the member list by key hashes (`swarm::topology`).
* A member's entry holds its alive stamp as Unix milliseconds (`swarm::members`).
From the docs: a node under an unknown policy receives without judging; every node
takes a token any member signed; alive stamps about daily; where a refused member
shows; the cost of checking a signature; a node keeps the documents of epochs it left
for a while (`archiveRetention`: 2 epochs, 30 days), and every node builds a remake's
snapshot itself today; a node that was away rejoins the epoch enough ready members
advertise, and has its own writes carried over within the replay window, the rest
quarantined; a member votes for one remake of its epoch at a time, and a
compaction's base is fixed by the coordinator when it commits.
Assumed, to be shown as it is built:
* Whether a change is in the history of a cut, or of a witness's change, can be
answered cheaply enough: per actor, the last number within that history, kept per
cut and per claim that has a rival. Statements and rivals are few.
* Resolving kept as the node goes costs little per change. Judging at old
dependencies reads the registers there (`document::at`), as an envelope's check
does today.
* Writing a quarantined statement again as it is works after a drop wherever what it
builds on still holds.
* A node can keep neighbours beside those the member list gives (its seeds, its
user's other nodes).
* A limit counted at a change's dependencies (a kind's claims by one user) is read
fast enough from the register; an index kept beside it otherwise.
* Who lost a user name as a change's history shows it (judging, 6 and 7) is worked out
only where a name has several creations there, which is rare.
* The snapshot of a federated remake, built from the resolved registry, comes out
byte for byte the same on every node, as today's does from the document.
* A node can answer sync for an epoch it left, from its archive.
* What was seen for long is kept as the node goes, per voter and per thing watched (a
voter silent, a user out, a thing uncovered), and costs little.
* The digest of the snapshot a node would build is cheap enough to make with every
stamp at a registry's size, and to make again at another node's stamp where both
hold the same changes.
* A version can speak the wire version before its own.
|