diff options
Diffstat (limited to 'docs/federation.md')
| -rw-r--r-- | docs/federation.md | 1117 |
1 files changed, 624 insertions, 493 deletions
diff --git a/docs/federation.md b/docs/federation.md index 6e1c87b..f53828d 100644 --- a/docs/federation.md +++ b/docs/federation.md @@ -68,13 +68,13 @@ but for what the voters can do to a user, said with each and below): 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 claimant's own line - (those who vouched it in) can get it **contested**, so that nobody holds the name; + 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 by the voters' clocks, unless a decision frees them - or gives them away before (Unvouching). + 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. @@ -83,15 +83,15 @@ And for what the swarm decides together, with the fractions the design recommend and fewer than a third of the voters present malicious, then or later (the voters are the users an epoch starts with, of those the root brought in): -7. **Decisions by two thirds.** A remake, a new root, first keys for a user that lost - its own, the names of a user that is out, a change of the numbers counts only once - more than two thirds of the voters present, and more than half of all the voters, - voted for it and their nodes took it up while the votes ran. No two decisions of - one matter both count, and none counts that no honest voter voted for. With no - voter malicious, no two do whatever the network does. A remake changes nothing in +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 to other rules: a change of policy is followed by choice, and a - decision that counts is refused by choice. + Nobody is moved against its will: a decision is refused by choice. What is not promised: @@ -99,8 +99,8 @@ What is not promised: 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 claimant's line can, by saying it saw another - first. + 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. @@ -111,8 +111,8 @@ What is not promised: * **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 a third of them, lying about the time, make a - user that is out gone at once. + 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. @@ -122,13 +122,13 @@ What is not promised: * **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, - together, can make two decisions of one matter count, and can push the swarm's - time. So can fewer, across a partition that outlasts the absence period and splits - the honest voters about evenly, by being present on both sides. Two that count - undo each other; two remakes are two epochs, and the parties choose (Decisions). - Nor against the past: the node 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. + 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. @@ -142,10 +142,10 @@ shape, and from Kleppmann's result that a hash-linked change graph whose validit 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 -classic two-thirds argument carried over to votes that take days, with a clock read -from the document: medium, less for the clock. A user gone after being out for long -reads the same clock. 5 and 7 want the tests named under -Attacks, and someone trying to break them. +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 @@ -161,8 +161,9 @@ its voters' votes. 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: +what becomes of what a later policy no longer covers (`uncovered`), 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`), @@ -171,6 +172,9 @@ root user, its keys and its nodes: * A node that doesn't know the policy's version 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: @@ -178,8 +182,8 @@ value: | Number | Recommended | What it says | |---|---|---| | `window` | 7 days | how long a vote runs | -| `absent` | 30 days | after how long without a stamp a voter is absent | -| `gone` | 30 days | after how long out a user is gone; none: its names wait until a vouch or a decision | +| `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 | | `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 | @@ -187,25 +191,24 @@ value: | `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 voter 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.time` | 1/3 | the share of the voters whose stamps make the swarm's time, and a user gone | +| `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 the time -and for a user gone, is the fewest voters that are that share at least, and one at -least. +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. -Anything else in the policy changes by a change of policy, which is a fork (below). - ### Users: statements, keys, nodes `_continuum/users/<name>/<digest>` holds the **statements** about and by a user, each @@ -216,13 +219,14 @@ grows. A statement is signed by one user and says one thing: |---|---|---| | `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 or a vote of its own given up; or itself | +| `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`, `decide` | a voter (a change of policy: a first user) | Decisions | +| `propose`, `vote` | a voter (a fork: a first 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 @@ -245,41 +249,65 @@ grows. A statement is signed by one user and says one thing: 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** are 1 to 64 of `A-Z a-z 0-9 . _ @ -`, as today, or fewer characters where - the policy says (the registry: lower-case letters, digits and `-`, so a name is one - label of a host name), less the top-level keys the swarm uses otherwise - (`_continuum`). +* **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, the line, the root +### 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. A user is **vouched -for** if its creator is, down from the root, and the creation counts; or if a user -vouched for it whose own line doesn't pass through it. So vouches form a graph, and -rings away from the root hold nobody. +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's **line** is the users from the root down to its creator, then itself: fixed -when it is created. (If its creation stops counting and a `vouch` keeps it, its line -runs through that voucher.) The line is whom a user answers to for what came first: +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: -* the line's nodes are the witnesses for and against its claims (below); -* a creator's limit bounds how many users come in through it, and each is on its line - for all to see. +* 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. -What a second vouch is for: a user stays vouched for when its creator takes the -creation back (Unvouching), cuts off the key that signed it (Cuts), or is banned by a -node (Bans). It changes nothing else. +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: the -swarm key, the root to start from, some seeds; and writing the `create`. - **What the root is.** A user like any other, but for three things. It is in the root +swarm key, the root to start from, some seeds; 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 three things. It is in the root change, so nobody vouched for it, and its name can't be created again. It is on every line, so its nodes' changes are what settles claims. And the users it creates are the **first users**, who come to vote for the swarm (Decisions). 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. + +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 @@ -292,24 +320,23 @@ exactly: 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 creation or vouch that counts, the - user stays as it is in everything; its line then runs through that voucher. +* **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 whose line ran - through it is out too, with the same consequences, unless another user that counts - vouches for it, before or after. +* **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**, at a set of changes, once a third of - the voters (`fractions.time`) each have a node with two alive stamps `gone` or more - 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 is read from the - changes alone and never goes away. Fewer than a third of the voters can't bring it - about early, and the time between two stamps is by one node's own clock. +* **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. @@ -325,6 +352,13 @@ So whoever relied on a user that is out has that long to find another who vouche 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) @@ -353,32 +387,42 @@ What must be unique across users (a host's name, an address, a top-level domain) **claimed**. The swarm declares the kinds, as data in the policy: ```json -{ "name": { "rules": [ { "match": ["*", "{user}", "**"], "by": "{user}" }, - { "match": ["*"], "by": ["tv"] } ] }, - "ip4": { "rules": [ { "match": ["10", "243", "*", "*"], "by": "*" } ], - "limit": 256 }, - "user": { "rules": [ { "match": ["*"], "by": "*" } ] } } +{ "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 a list of segments: a host name's labels from the top (`toaster.tv.r` is - `["r", "tv", "toaster"]`), an address's parts. In the document it is one key, the - segments joined by dots (`r.tv.toaster`, `10.243.0.7`). -* The first rule whose `match` fits decides who may hold the name (`*` fits any one - segment, `**` any number more): one user, a list, anyone vouched for (`"*"`), the - user whose name stands where `{user}` does, or whoever is the root (`"{root}"`). No - rule, no holder. +* **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. So above, `*.tv.<tld>` is tv's, for any top-level - domain, whatever tv claimed; a top-level domain is claimed by tv only; an address by - whoever comes first, 256 a user. + 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). + (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 decision counts. A claim is +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 @@ -393,8 +437,8 @@ 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 and of the -users on its line, the root's among them. +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 @@ -420,7 +464,7 @@ What this gives: * 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 on the claimant's line saying it saw the rival first, and nobody on the + 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 @@ -475,14 +519,15 @@ 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 nearer the root first; and -none by a user that a ban taken before leaves out. +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, a line 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 +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. @@ -491,9 +536,10 @@ 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, who the root is, a user's first -keys where it lost its own, when to compact, which change of policy is the swarm's. -They are decided by votes of users, signed by them, over days. +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's names the **first users** that count at its @@ -508,85 +554,87 @@ be a vote a puppet. 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.** The document gives a time. Each node's alive stamp says what its -clock read *(checked: `alive`, Unix ms)*. The **swarm's time**, at a set of changes, -is the latest time that a third of the voters (`fractions.time`) have a node stamped -at or after. A -voter whose nodes' last stamp is more than `absent` behind it, or that has no node, -is **absent**: it neither votes nor counts, until a node of it stamps again. The -voters **present** are those not absent. Fewer than a third of the voters can't move -the swarm's time forward by lying, so they can't make the others absent; and those -who left hold up nothing. +**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. A decision also takes more than half of all the epoch's voters (`fractions.all`), -absent or not (below). So with no voter malicious, no two decisions of one matter both -count, however long a partition lasts: each would be taken up by more than half, and -an honest voter takes up one. Where half or fewer are left for good, they go on by a -fork that names who votes (A change of policy is a fork). +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 saying what. -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. One vote on a - matter at a time; `release` takes one back. -3. Once more than two thirds of the voters present (`fractions.decide`) have votes for - it in the document, any of them **decides**: a statement naming the proposal, the - votes, and the decision of that matter before it, if there is one. Every node - judges it as written: the votes are there and not released, their users are more - than two thirds of the voters present there and more than half of all the voters, - and no other decision following the same one is there. -4. Every voter **takes it up**, by one node: the one it named first of those - that carry for it. When the decision reaches that node while the votes it names - still run, by the node's clock, the node records so in its member entry. Of the - decisions following one, it takes up the first that reaches it and no other. -5. The decision **counts** once more than two thirds of the voters present at - its dependencies, and more than half of all the voters, took it up. That is read - from the document, the same by every node, and never goes away. +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. +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. -Every node acts on a decision that counts, unless another following the same one -counts too, or the node refuses it (Refusing a decision). +**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. -**By any node.** A proposal, a vote, a vote's release and a decision 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. +**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. -**Two that count.** Two decisions following the same one both come to count only -by what isn't promised against: a third of the voters present malicious, or fewer -of them present on both sides of a partition that outlasts the absence period. -Where a set of changes has both counting, nodes act on neither there: the matter is -as the decision before them left it, every node shows both, and the next decision -of the matter names both. Two remakes are two epochs: a node is in the one it built -first, and moves to the other by its operator's word. +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). -**Decisions don't come from the past.** One that is crafted with old dependencies, or -kept back until its votes ran out, is taken up by no honest voter, so it 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 decision that -comes to count 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 the -decisions that count as it finds them; a remake puts everything before it out of -reach. +**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. +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.** -* **The numbers**: any number of the policy, the fractions among them, named by its - place there, to any value. It holds for what is made in sight of the decision. A - lowered limit takes nothing away: what a user has stays, and it gets no more until - it is below. +* **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 line - and those it brought in, and releases the other name. + 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 @@ -594,19 +642,18 @@ Taking up is their nodes', and takes as long as syncing does. 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 change of policy** (below): proposed and voted on the same way, made and - followed by choice. -A decision that counts 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. +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. From the decision on, in every change that has it in its -history: +gives the seat to any user. Where it holds: * the new root is on every line, so its nodes' changes settle claims, and the users it creates are first users. Like them, it votes from the next remake on if it @@ -617,7 +664,7 @@ history: 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 did that the decision's history doesn't hold is an ordinary +What the old root did that the proposal's history doesn't hold is an ordinary user's: users it created while away, or crafts later with old dependencies, are not first users. @@ -630,24 +677,41 @@ again. ### Remakes -A federated swarm compacts by a remake its voters decide. What the next epoch -holds isn't voted on: it is the registry as it resolves, which every node works out -itself. +A federated swarm compacts by a remake its voters decide. A remake throws history +away, 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** is the history of the decision. A voter takes a remake up only - if that holds all its node holds but the last `baseAge`, so a decision can't - leave out what the swarm has. -* **Every node builds the snapshot** itself once the decision counts, 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 and the numbers; who the - root is, and who votes (the first users that count and aren't absent); the users - that count, with their names (or that they are contested), IDs, keys, nodes, - creators and vouchers; the names held or contested, each marked settled or not; - the values in the namespaces that count, as the envelopes they are; the members' - entries, as today. A user that is out and not gone is in it with all it has, and - with the time since when a third of the voters saw it out, which stands for the - first of the two stamps after. It leaves out what doesn't count otherwise, and all +* **The base.** A remake is proposed and voted on like anything else. 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. 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 (the first users that count and aren't absent); 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** goes aside or is dropped, as the policy says + (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 @@ -672,7 +736,7 @@ year. A remake a year is plenty. 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 nodes of its line are in. A +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: @@ -680,11 +744,11 @@ gets there in one of two ways: 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's decision count; builds - the snapshot itself; and is in the next epoch, whose ID is the snapshot's hash. + 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 that count, or the walk ends in another epoch + 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 @@ -715,58 +779,70 @@ Either way, what follows: A node moves to an older epoch, or to another of the same age, only by its operator's word. -### A change of policy is a fork +### 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`, `uncovered`. 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 version a node doesn't know.** Such a node 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**: 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. Where the decision + holds, it doesn't count, and nothing more is written there. It stays in the + document, as everything does within an epoch, and counts again under a later + policy that covers it. +* **At the next remake**, `uncovered` says what becomes of it. `"aside"`, as + recommended: the snapshot holds it under `_continuum/uncovered/<user>/`, at the + path it had and as it was: read by all, written by none, counting for nothing. Its + user takes from there what it wants to write again under the rules that hold, and + the remake after that leaves it out. `"drop"`: left out at once. +* **Whole, not merged.** Two proposals of a policy are two policies, and the later + holds: what one changed isn't worked into the other. -A change of policy (a new kind of name, another rule, a version of continuum that -judges differently) can't be made to bind anyone: a node judges by the rules it knows -and its operator chose. So it is a fork, by design: +### Going apart: a fork by choice -* A first user proposes the new policy, and voters vote. Any first user decides, - with whatever votes there are: the decision names the proposal and, by its history, - the base. It takes no two thirds to be made, only to be the swarm's, and it stands - in no other decision's way: there can be several. -* The proposal may name who votes on its side. Where it doesn't, those vote who - would after a remake at the base. Whoever follows it chose them with it. -* A node follows it if it knows the policy's version and its operator said so - (`POST /admin/policy`, naming the decision): it builds the snapshot at the base - with the new policy in it, and goes on in that epoch; a voter's node records - taking it up. A node that doesn't stays in the epoch it is in, which goes on as it - was. +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: + +* A first 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, those vote who + would after a remake at the base. 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. -* Once as many voters took it up as make a decision count, the change is the - swarm's, and nodes show it so. It still moves no node whose operator didn't say so. * 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: those who stay propose the - policy they have, naming who votes. - -So a fork is also the way out for those the swarm's decisions leave behind: with a -root that went before the first remake, with half the voters or fewer left, or with -two thirds of them turned against the rest. 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. + 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. -This is how Bitcoin changes its rules. Nobody decides for anyone: nodes run the rules -their operators chose. A change that isn't compatible takes effect at a block height -named beforehand, and nodes with the old rules go on with the old chain, the same up -to there. Miners signalling for a change says how many will go, not who must. And a -transaction that would count on both chains is kept to one by naming its chain. Here -the height is the decision's base, the signalling is the vote, and the epoch in every -envelope keeps a statement on its side. And nodes that don't accept a change the -others made keep their rules, and with them their chain: here, refusing a decision. +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 that counts binds the nodes that follow it, and no node has to. By default -a node follows every decision that counts. Its operator has it refuse: +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 counts. Bounds that the policy + 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 decision): any decision, +* **By hand** (`POST /admin/decisions/refuse`, naming the proposal): any decision, whenever. What refusing does: @@ -774,22 +850,30 @@ 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 decision's history, where it doesn't count yet: the registry as it resolves - there, the policy and the voters as after a remake at that base, and the digest of - the decision 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 a change of policy: 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 or a change of policy** is refused by staying: the epoch a node is in - goes on as it was. + at the history of the proposal: the registry as it resolves there, the policy and + the voters as after a remake at that base, 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: @@ -798,29 +882,30 @@ A change is valid if, in the document at its dependencies: 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, 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 what a decision is made of). +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. 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. `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. + 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. -8. `propose`, `vote`: by a voter present there (a change of policy: proposed by a - first user), signed in this epoch; a vote where the user has none on that matter - that isn't released. `decide`: as said under Decisions, and for a change of - policy by a first user, naming the proposal. +8. `propose`, `vote`: by a voter present there (a fork: proposed by a first user), + signed in this epoch; a proposal within its voter'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 @@ -830,10 +915,10 @@ of a name is known can't scribble over the namespace, and one whose name a liar 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 numbers, the root, the -first keys and the names freed or given are as the decisions that count there leave -them. A statement in a -register was valid when it was taken, so judging the next needs no look further back. +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 @@ -841,32 +926,32 @@ The **resolved registry** is a function of a set of changes that holds each chan history (and, for reading, of the users followed). In five steps, each reading only the steps before: -1. **Decisions.** Which count (Decisions), one after the other as each names the one - of its matter before it, two following the same one undoing each other: the - 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 (nearer the root, 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, 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. +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 lets its user: anyone vouched - for where the rule says `"*"`, 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. +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, @@ -877,15 +962,16 @@ A node keeps the resolved registry as it goes (a statement changes it around its name), and answers from it: * `GET /registry/users/<name>`: the user that has the name, its keys and nodes, its - line; or that it is contested, between whom; or out, since where, and until when - its names wait; or gone. + 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 numbers, the swarm's time, the - voters present and absent; what is proposed, who voted, until when; what was - decided, who took it up, whether it counts, and what this node holds back. +* `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 /store`, as today, with a value that doesn't count marked, and the one that counts in its place. @@ -896,10 +982,10 @@ 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 change of policy, a decision some refuse, or two remakes -that both count, are a fork: 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). +**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 @@ -928,25 +1014,32 @@ 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 its line holds (its -creator's nodes, up to the root's), and the operator follows its line or doesn't. A +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 one's line +### 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. -So in a federated swarm a node also keeps as neighbours the other nodes of its user, -and the nodes of its user's creator, and they it. A claim then reaches the root along -the line whatever the graph gives, and cutting a party off takes its creator. The -root's nodes link to the users the root created, not to everyone. +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 @@ -977,9 +1070,9 @@ 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 (taking a decision up), never into what it -reads from the document. What must hold for this, and doesn't everywhere today: +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 @@ -1009,21 +1102,22 @@ from changes it judged, so the snapshot holds only what counted. **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 is on every line, 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 (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. +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 line that lies, or that truly received -c' first. If that node also committed changes with c and not c' (it was up), both +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 @@ -1037,48 +1131,47 @@ 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 decision's dependencies, n of -them, fewer than a third malicious. +**Promise 7.** Let E be the voters present at a proposal, n of them, fewer than a +third malicious. -* *None that honest users didn't want.* A decision that counts was taken up by more - than 2n/3 of E, so by the nodes of more than n/3 honest ones. An honest node takes - up only a decision that is valid as written, so one with the votes of more than - 2n/3 of E, more than n/3 of them honest, and only while those votes ran. -* *One per matter.* Take two decisions following the same one, each made out of the - other's sight (in sight, the second is refused). An honest voter takes up, by its one - node, the first that reaches it and no other. Each was taken up by more than half - of all the voters, so some voter took up both: with no voter malicious that can't - be, whatever the network does. With malicious ones: where both have the same - voters present, each needs more than 2n/3 of them, so more than n/3 took up both, - one of them honest, which can't be. The voters present are the same, but for those - who went absent in between, as long as voters that are up reach each other within - the absence period. Across a partition longer than that they differ, and the - voters that took up both may all be malicious: that is not promised against. - Decisions of an earlier epoch are out of reach: a node acts on those of its own. -* *Not from the past.* An honest voter takes a decision up only while its votes - run, so one crafted later with old dependencies, or kept back, has only its - makers' records: it counts only 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 clock.* The swarm's time is a time that a third of the voters stamped at - or after. Fewer than a third, stamping ahead, leave it at an honest stamp or - before: nobody honest and up is made absent. Stamping behind, or not at all, makes - only themselves absent. -* *A remake changes nothing.* The snapshot is a function of the base, the decision's - history, which every node holds and judged itself. A decision chooses the base, and - an honest voter's node takes up none that leaves out what it holds. Who votes - next is in the snapshot: the first users present at the base, for all to see - before they vote for it. -* *Nobody is moved.* A node follows a change of policy only by its operator's word, - and with it whoever the proposal names to vote; and it refuses any decision by its - operator's word or bounds. -* *With other fractions.* Write d, a and t for `fractions.decide`, `fractions.all` - and `fractions.time`. Two decisions of one matter are each taken up by more than d - of the voters present, so they share more than 2d − 1 of them: one per matter holds - while no more than that share is malicious, which at a half or less is nobody. - Honest voters alone never make two while a is a half or more. A decision no honest - voter voted for takes more than d of those present malicious. The swarm's time, - and a user gone, hold against fewer than t of the voters. +* *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 snapshot: the first users present at the base, + 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 @@ -1106,15 +1199,35 @@ back, so the point only moves forward as more of them show up. * **Votes by nodes, or by every user**: a party runs as many nodes, and brings in as many users, as it likes. Only whom the root brought in itself can't be multiplied by anyone but the root. -* **Recommending two thirds of all the voters**, absent or not: no two decisions - whatever the network does, malicious voters or not, but a third that left for good - ends all decisions. 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**: with votes that take days and nodes - that take a decision up one by one, a voter that takes up two decisions of one - matter must not be able to tip both. Two thirds stand that against fewer than a - third; a majority stands it against nobody. +* **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 @@ -1157,12 +1270,12 @@ back, so the point only moves forward as more of them show up. | 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 decision it sees count, or an epoch its operator gave it | +| 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, which the links along the line bound | -| Say when it was alive | Believed for itself only; a third of the voters together move the swarm's time, and say how long a user was out | +| 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. @@ -1170,13 +1283,12 @@ 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, keys, cuts, nodes that carry), the -namespace rule, claims by kind with their rules and limits, late and contested, bans, -decisions by the voters with the swarm's time, the numbers, the root's seat, a user's -first keys, remakes that every node builds, a change of policy as a fork, refusing a -decision, resolving -and its API, the drop an operator asks for, the links along the line. All of it is -general: a swarm with other kinds, or none, uses the same. +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 @@ -1186,19 +1298,19 @@ against the resolved registry, when it writes the files out. So are a node kept 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 version, where it hasn't; either way a change of policy. +continuum, with a policy version, 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 creator's 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. No message -changes. -[`trust.md`](trust.md), [`access.md`](access.md), [`remakes.md`](remakes.md) and the -glossary get the federated case: party, statement, line, first user, voter, claim, -cut, late, contested, settled, out, gone, the swarm's time, bounds, and a ban of a -user beside a ban of a node. +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. No message changes. [`trust.md`](trust.md), [`access.md`](access.md), +[`remakes.md`](remakes.md) and the glossary get the federated case: party, statement, +line, first user, voter, claim, cut, late, contested, settled, out, gone, seen for, +taking a proposal up, a decision that holds, bounds, and a ban of a user beside a ban +of a node. ## Attacks @@ -1220,133 +1332,143 @@ Statements and users: | 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 is on its creator's line, 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` | +| 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 | |---|---|---|---|---| -| 12 | Unvouches a user it didn't vouch for | Refuse | 3 | unit: `only_the_voucher_unvouches` | -| 13 | 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` | -| 14 | 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` | -| 15 | A user is unvouched: what of those it brought in, and its names? | They are out from the same point unless another vouches for them; a vouch after brings them back whole. Their names wait | 4 | unit: `those_it_brought_in`, `a_vouch_brings_back` | -| 16 | An unvouched user's nodes keep sending | Refused as peers; nothing taken from them | 6 | sim: `nothing_from_an_unvouched_node` | -| 17 | 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` | -| 18 | 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` | -| 19 | 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` | +| 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 | |---|---|---|---|---| -| 20 | 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 | -| 21 | Writes plain values, or protocol data but its own member entry | Refuse | 3 | unit: `nothing_plain` | -| 22 | Puts an old envelope back | Refuse: it doesn't replace what is there | 3 | unit, exists: `what_an_envelope_must_be` | -| 23 | 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` | +| 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 | |---|---|---|---|---| -| 24 | Claims a held name in sight of the claim | Refuse | 4 | unit, claims: `first_come_first_served` | -| 25 | 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` | -| 26 | 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` | -| 27 | Claims `x.tv.r` as another; claims a top-level domain, as another or as a late creation of the name `tv` | 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 | 4 | unit: `a_kinds_rules`, `a_kept_name`, `a_rule_goes_by_who_has_the_name` | -| 28 | 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` | -| 29 | A user on the claimant's line 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` | -| 30 | A whole line kept silent, then claims with old dependencies | Before the claim is settled: contested. After: late | 5 | unit: `silence_before_settling` | -| 31 | 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` | +| 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 | |---|---|---|---|---| -| 32 | 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` | -| 33 | Signs with a retired key and old dependencies | Hold it (valid as written), leave it out | 3 | unit: `backdated_past_a_cut` | -| 34 | With a stolen device key, retires the keys above or beside it | Refuse | 3 | unit: `no_key_retires_one_above` | -| 35 | 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` | -| 36 | 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` | -| 37 | 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` | -| 38 | 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` | -| 39 | 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` | +| 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 | |---|---|---|---|---| -| 40 | Forges a vote; votes as a user that isn't a voter, or one absent | Refuse | 7 | unit, decisions: `a_vote_is_a_voters` | -| 41 | Brings in many users to vote | They don't: only those a root created come to vote | 7 | unit: `who_votes` | -| 42 | The root creates many first users to carry a vote | They vote from the next remake on, which takes two thirds of the voters there are; these see who would vote, and can replace the root first | 7 | unit: `voters_are_the_epochs` | -| 43 | Decides without the votes, or with released ones | Refuse | 7 | unit: `a_decision_as_written` | -| 44 | Crafts or keeps back a decision until its votes ran out, then shows it | No honest voter takes it up: it doesn't count. One that counts by its makers' own records, long after (two thirds of their node 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` | -| 45 | Two decisions of one matter, out of each other's sight | Each node takes up the first it receives; at most one counts | 7 | sim: `one_decision_a_matter` | -| 46 | Stamps its nodes far ahead, to make the others absent | The swarm's time stays at a stamp a third reached | 7 | unit: `the_swarms_time_by_a_third` | -| 47 | 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` | -| 48 | 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) | -| 49 | 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` | -| 50 | 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` | -| 51 | Decides a remake at a base that leaves out what the swarm holds | No honest voter's node takes it up | 7 | sim: `a_stale_base_is_not_taken_up` | -| 52 | 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` | -| 53 | 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` | -| 54 | 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` | -| 55 | 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` | -| 56 | 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` | -| 57 | The old root comes back after its seat went; crafts users "from before" | It is a first user; what the decision's history doesn't hold of it is an ordinary user's | 7 | unit: `the_old_root_comes_back` | -| 58 | 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), 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 (A change of policy is a fork, Refusing a decision) | -| 59 | 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` | -| 60 | A change of policy with two thirds; a fork naming its proposer the only voter | A node follows only by its operator's word, which names the decision; what that says shows | 7 | sim: `a_fork_by_choice` | -| 61 | 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` | -| 62 | 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` | +| 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 | They don't: only those a root created come to vote | 7 | unit: `who_votes` | +| 44 | The root creates many first users to carry a vote | They vote from the next remake on, which takes two thirds of the voters there are; these see who would vote, and can replace the root first | 7 | unit: `voters_are_the_epochs` | +| 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 | They don't count where it holds; the next remake keeps them aside for an epoch, or drops them, as the policy says | not promised | unit: `what_a_policy_no_longer_covers` | +| 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 | The old root comes back after its seat went; crafts users "from before" | It is a first user; what the decision's history doesn't hold of it is an ordinary user's | 7 | unit: `the_old_root_comes_back` | +| 63 | 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) | +| 64 | 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` | +| 65 | 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` | +| 66 | 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` | +| 67 | 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 | |---|---|---|---|---| -| 63 | Sends different changes to different peers, each under its own number | Take all: changes made at once | 2 | sim: `two_histories_merge` | -| 64 | 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` | -| 65 | 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` | -| 66 | Places its nodes around a victim by choosing their keys | The victim's node also syncs with its creator's | 5, 6 | sim: `around_a_victim` | -| 67 | 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` | -| 68 | 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` | -| 69 | Lies in its member entry (ready, reached, stable) | Sync and trees bend; nothing gets in that way | 6 | argued (Reaching one's line) | -| 70 | 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` | -| 71 | 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` | -| 72 | 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` | +| 68 | Sends different changes to different peers, each under its own number | Take all: changes made at once | 2 | sim: `two_histories_merge` | +| 69 | 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` | +| 70 | 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` | +| 71 | 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` | +| 72 | 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` | +| 73 | 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` | +| 74 | Lies in its member entry (ready, reached, stable) | Sync and trees bend; nothing gets in that way | 6 | argued (Reaching the others) | +| 75 | 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` | +| 76 | 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` | +| 77 | 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 | |---|---|---|---|---| -| 73 | A rule differs between versions (a bug) | Each side refuses, logs that the policies disagree, shows the peer refused; one side upgrades and drops | not promised | sim: `a_side_drops_and_comes_back` | -| 74 | A policy version a node doesn't know | It doesn't sync | 1 | unit: `no_sync_under_an_unknown_policy` | -| 75 | 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` | -| 76 | 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` | -| 77 | A partition that outlasts the absence period | One side at most decides: a decision takes more than half of all the voters. With a malicious voter on both sides of an even split, both may: the two undo each other, or are two epochs the parties choose between | 7, not promised | sim: `one_side_decides`; unit: `two_that_count` | -| 78 | None: a swarm that isn't federated | Everything as today | Optional | the existing tests, untouched; `bench_screening` | +| 78 | A rule differs between versions (a bug) | Each side refuses, logs that the policies disagree, shows the peer refused; one side upgrades and drops | not promised | sim: `a_side_drops_and_comes_back` | +| 79 | 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` | +| 80 | 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` | +| 81 | 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` | +| 82 | 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` | +| 83 | 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/<d> tv's claim of 10.243.0.7 -_continuum/claims/name/r/<d> tv'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": "…" }}} +_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": "…" }}} ``` -`toaster.tv.r` is kept for tv by the kind's rule and needs no claim. An IPv6 address -is generated from the net, the user's name and the host's today; a kind `ip6`, first -come, first served like `ip4`, holds whether or not anyone generates honestly. +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`), so nobody else creates a user over a -namespace that is there. The root is trusted as it is (every party is given its +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 @@ -1366,53 +1488,60 @@ 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. At every stage -the existing tests pass untouched (78). +the existing tests pass untouched (83). 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: 67, 68, 74, 78, and + from its party only, keeps ended sessions to itself. Tests: 72, 73, 79, 83, and the policy's existing tests under a policy from the root. -2. **Statements.** The users' registers (`create`, `key`, `node`, `vouch`, +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, 21. -3. **Namespaces.** The rule, with `userSigned` below a name. Tests: 20, 22. From here + 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, rules, kept names, limits, `claim` and `release`, - judged as written; resolving step 4 for claims in sight. Tests: 24, 27, 28. -5. **Out of sight.** Late, contested and settled, for names and for users' names; - the root's node writing when a claim reaches it; `GET /registry/claims` with what - makes a claim late; values marked in `GET /store`. The links along the line. - Tests: 8, 9, 23, 25, 26, 29, 30, 63, 65, 66. +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, 68, 70, 71. 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: - 31 to 35, 37, 38, 39. -7. **Unvouching.** `unvouch`, a user giving itself up, those brought in going with - it, the names of a user that is out waiting, nothing taken from an unvouched - user's nodes. Tests: 12 to 17. + 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 line holds, the budget and the share per party. Tests: 64, 70, 71, - 72, 73. -9. **Bans** for those who follow. Tests: 75, 76. + with what the users on one's lines hold, the budget and the share per party. + Tests: 69, 75 to 78. +9. **Bans** for those who follow. Tests: 80, 81. 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, the swarm's time and who is - absent, `propose`, `vote` and `decide` taken from any node, taking up, what - counts, `GET /registry/decisions`. What is decided: the numbers, a user's first - keys, the names of a user that is out. A user gone. The simulations start from a - root change naming several voters, as a remake's does. Tests: 18, 19, 36, 40 to - 47, 49, 50, 77. -12. **The root's seat.** Tests: 57. -13. **Remakes.** The base, the snapshot every node builds, 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. Tests: 51 to 56. -14. **Forks by choice.** A change of policy, 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: 59 to 62. +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, 82. +12. **The root's seat.** Tests: 62. +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. Tests: 55 to 61. +14. **The policy by a decision.** The policy as a matter, a node that doesn't know + it, what it no longer covers, kept aside or dropped at a remake. Tests: 54, 79. +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: 64 to 67. ## What was checked, and what is assumed @@ -1441,7 +1570,8 @@ shows; the cost of checking a signature; a node keeps the documents of epochs it 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. +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: @@ -1453,7 +1583,8 @@ Assumed, to be shown as it is built: 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 graph gives (its creator's nodes). +* 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 |
