aboutsummaryrefslogtreecommitdiffstats
path: root/docs/federation.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/federation.md')
-rw-r--r--docs/federation.md1253
1 files changed, 1253 insertions, 0 deletions
diff --git a/docs/federation.md b/docs/federation.md
new file mode 100644
index 0000000..947da71
--- /dev/null
+++ b/docs/federation.md
@@ -0,0 +1,1253 @@
+# Federation: a design
+
+A design, not what the code does: none of this is built. It says how a swarm can be
+kept by parties that don't trust each other, for a first use: the krebs registry of
+users, their hosts, and the hosts' names, addresses and keys
+([krebs/kartei](https://github.com/krebs/kartei)), today plain files in a git
+repository that one maintainer merges into.
+
+Claims about the code as it is are marked *(checked)* where they were read in the
+code, *(docs)* where they are taken from the docs, and *(assumed)* otherwise; the last
+section lists them. How sure each part of the design is, is said with the promises.
+
+## Optional
+
+A swarm is federated if its root carries the federated policy (below). Everything here
+holds under that policy, and nothing else changes:
+
+* A swarm started as today has no such policy and stays as it is: its rules, its
+ users with their capabilities, its remakes by the members' votes, its tokens, its
+ messages.
+* The code is apart where it would cost. Judging reads the policy as it does today and
+ takes the federated rules under the federated one only; the resolved registry is
+ kept there only; envelopes carry their new fields there only; no message changes.
+* Shown at every stage by the existing tests passing untouched, and by the cost of
+ judging a change (`bench_screening`) staying where it is for a swarm without a
+ policy and with today's.
+
+## The parties and the adversary
+
+A **party** is a user and the nodes that carry for it. The user holds SSH keys and
+signs; its nodes commit what it signed and sync with the others' nodes. One user
+starts the swarm: the **root**. Nobody owns the swarm, and any party may be malicious,
+alone or together with others, the root too.
+
+What an adversary can do:
+
+* run nodes, and once it is in, bring in users of its own;
+* craft any change: under any actor, with any dependencies (so "made long ago", as
+ the document sees it), any operations;
+* send different changes to different peers, send none, or pass on only some;
+* delay or drop messages between any nodes for as long as it likes, short of forever
+ between honest nodes;
+* choose its node keys, and so where its nodes fall among the members;
+* steal a key of another party (a user's key, a node's key), as a case of its own.
+
+What it can't: forge an Ed25519 signature, or find two changes with one hash.
+
+Out of scope: keeping the registry from being read (every party reads all of it),
+hiding who talks to whom, and a party's own machine taken over whole (then the
+adversary is that party).
+
+## What is promised
+
+To an honest node, whatever the others do, with any number of them malicious:
+
+1. **Only valid changes.** Every change it holds passed the rules, judged as its
+ writer saw the document (at its dependencies).
+2. **The same document.** Two honest nodes holding the same changes hold the same
+ document, and resolve it (Resolving, below) to the same registry.
+3. **Signed by its user.** Whatever the resolved registry shows in a user's namespace,
+ or as held by a user, was signed with a key that was that user's when it signed, or
+ is as the root change has it.
+4. **One holder.** The resolved registry gives each name of each kind to one user at
+ most, and each user name to one user.
+5. **First come, first served, once the root saw it.** A claim is **settled** once a
+ node of the root committed a change after receiving it, with no other claim of
+ that name around. From then on nobody else ever holds that name while the claim
+ stands: every other claim of it, whenever and with whatever dependencies it shows
+ up, is late. Two things can still happen to it. Users on the claimant's own line
+ (those who vouched it in) 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).
+6. **Nobody waits for anybody.** An honest user's valid write needs no vote and no
+ other party's consent; it counts at once, and reaches every honest node its nodes
+ reach.
+
+And for what the swarm decides together, with 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, a change of the voting 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 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.
+
+What is not promised:
+
+* **One history at all times.** Both sides of a partition go on. A name claimed on
+ both sides goes to the side the root was on, or is contested until one side
+ releases it.
+* **That a name is never contested.** Before a claim is settled, a claim made out of
+ its sight contests it. After, the claimant's line can, by saying it saw another
+ first.
+* **Protection from a stolen key** before its user notices: what was signed with it
+ counts until a key above it cuts it off; then all of it stops counting (Cuts). A
+ remake in between makes it final.
+* **Agreement in the classic sense, for what users write.** There is no quorum there,
+ so no n ≥ 3f+1: promises 1 to 4 and 6 hold against any number of malicious parties,
+ and 5 from when the root saw the claim. Only decisions take two thirds, and only
+ the voters make them.
+* **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.
+* **Decisions without the voters.** With half an epoch's voters or fewer around,
+ nothing more is decided there; what users write goes on. Until the first remake
+ the root is the only voter. Those left go on by a fork.
+* **A swarm that never splits.** A node that forks its own actor (two changes with one
+ number) splits the honest nodes that took one from those that took the other, until
+ one side drops its own (Partitions). It is found, proven and named; it isn't
+ prevented.
+
+How sure: 1 to 4 follow from how changes are judged today plus rules of the same
+shape, and from Kleppmann's result that a hash-linked change graph whose validity
+depends on a change and its history alone converges whatever the faulty nodes do
+(*Making CRDTs Byzantine Fault Tolerant*, 2022): high, given the conditions under Why
+it holds. 6: high. 5 is this design's own construction, argued below: medium. 7 is the
+classic two-thirds argument carried over to votes that take days, with a clock read
+from the document: medium, less for the clock. 5 and 7 want the tests named under
+Attacks, and someone trying to break them.
+
+## The design
+
+It builds on the strict policy ([`access.md`](access.md), Values signed by their
+users): values as envelopes their users sign, which every node checks against the
+registry as the writer saw it. What it adds: users come in by being vouched for, and
+go by being unvouched; each has its namespace; names of declared kinds are held by one
+user each; what a key signed can be cut off after the fact; and the swarm decides by
+its voters' votes.
+
+### The policy, in the root
+
+A federated swarm's policy is `{"version": 2, "values": "signed", "users": "vouched"}`,
+with what the rules read beside it in `_continuum/access`: the kinds of names
+(`claims`), the paths in namespaces whose values are signed (`userSigned`, as today),
+the limits, and the voting numbers (`window`, 7 days; `absent`, 30 days). All of it is
+in the swarm's root, its genesis change, with the root user, its keys and its nodes:
+
+* Nothing is ever written before the policy, so nothing is judged by none.
+* Every node is given a root and takes no other: the genesis root (`--genesis-root`),
+ or, in a swarm that was remade, the ID of a later epoch. The root is what a party
+ trusts, not what its seeds say.
+* A node that doesn't know the policy's version doesn't sync at all. (Today it
+ receives without judging *(docs)*, which here would let anything in.)
+
+The voting numbers change by a decision. 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
+an envelope kept under its SHA-256, never replaced or deleted: the register only
+grows. A statement is signed by one user and says one thing:
+
+| Statement | Signed by | Says |
+|---|---|---|
+| `create` | another user (the creator) | the name, its first keys, the nodes that carry for it |
+| `vouch` | another user | "I vouch for this user too" (no keys) |
+| `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 |
+| `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 |
+
+* **A user is its creation.** The `create` statement's digest is the **user ID**, and
+ every envelope the user signs names it, with the name, the swarm and the epoch it
+ was signed in. Two creations of one name are two users with two sets of keys; which
+ has the name is decided below, and a key listed in both signs for the one its
+ envelope names.
+* **Keys form a tree.** A user's first keys are those its creation names. A `key`
+ statement adds one, signed with a key the user has, which is **above** the new one,
+ as are the keys above itself. Any number over time; 64 at once at most (a limit in
+ the policy). Use: the first keys stay offline, a key added below them is on each
+ device.
+* **A node carries for a user** that named it. A node commits changes only while it
+ carries for a user, and an envelope is taken only in a change committed by a node
+ that carries for its signer (a `node` statement also in one by the node it names).
+ So every change is some party's: its node's signature proves the node
+ ([`access.md`](access.md), signatures required), and the user's statement binds the
+ node to the user. 8 nodes a user at once (a limit).
+* **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`).
+
+### Vouching, the line, the root
+
+A user creates whom it likes, within a limit (16, in the policy), and vouches for whom
+it likes. 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.
+
+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:
+
+* 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.
+
+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.
+
+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
+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).
+
+### Unvouching
+
+A user takes back a `create` or a `vouch` it signed: `unvouch`, naming it. Nobody else
+can, but the root for what a root before it signed (The root's seat). What it does,
+exactly:
+
+* **From where.** From the unvouch on, as far as the user had been seen. What the
+ unvouched user signed stays if it is in the history of the unvouch, or of a change
+ that a node of the unvoucher or of the root committed without the unvouch in its
+ history. Everything else it signed doesn't count through this vouch, whenever it
+ shows up. So an unvouch crafted with old dependencies undoes nothing that its
+ signer's own nodes, or the root's, had seen: no more than what reached neither.
+* **Another vouch keeps the user.** With another creation or vouch that counts, the
+ user stays as it is in everything; its line then runs through that voucher.
+* **Else the user is out, from there.** It holds nothing: its names are free for a
+ claim made in sight of the unvouch, and so is its user name. Its namespace doesn't
+ count. Its nodes witness nothing, and what they commit in sight of the unvouch is
+ refused.
+* **Those it brought in go with it,** from the same point: a user whose line ran
+ through it stops counting there too, with the same consequences, unless another
+ user that counts vouches for it, before or after. A vouch given after brings a user
+ back whole, with what it signed in between.
+* **What it did before stays done.** A name it released and another claimed stays the
+ other's; a user it created and another vouched for stays.
+* **Out of sight.** What the user signs with dependencies from before the unvouch (on
+ the other side of a partition, or crafted) is valid as written, so nodes hold it;
+ it is past the point, and doesn't count. A node takes changes of an unvouched
+ user's nodes only as part of another party's history, not from those nodes, so they
+ can't fill the document.
+* **Undoing it**: a new `vouch`.
+
+A user gives itself up the same way: a `release` of its own creation, from that
+release on.
+
+### Namespaces
+
+A user's namespace is the top-level key with its name. One rule: below `<name>`, only
+envelopes signed by a user of that name, at the depths `userSigned` lists (for the
+registry, `["*", "hosts", "*"]`: a host is one signed value). A value there is
+replaced as today, by an envelope naming the one it replaces (`prev`). Nothing plain
+is written anywhere but a node's own member entry.
+
+Delegating (another user writing below `<name>/shared`, say) would be a statement in
+the user's register granting a capability within its namespace: for later; the rule
+has room for it.
+
+### Claims: names held by one user
+
+What must be unique across users (a host's name, an address, a top-level domain) is
+**claimed**. The swarm declares the kinds, as data in the policy:
+
+```json
+{ "name": { "rules": [ { "match": ["*", "{user}", "**"], "by": "{user}" },
+ { "match": ["*"], "by": ["tv"] } ] },
+ "ip4": { "rules": [ { "match": ["10", "243", "*", "*"], "by": "*" } ],
+ "limit": 64 },
+ "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 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, 64 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).
+
+`_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 is taken, as its writer saw the document, if a
+rule lets the user, the user is within the kind's limit, and every claim of that name
+there is released. That is first come, first served wherever one claim was made in
+sight of the other: the later one is refused by every node.
+
+A claim says nothing else. What the name is for is in the namespace: a host's entry
+names its addresses and names, and they count while its user holds them.
+
+### Two claims out of each other's sight
+
+Two users claim one name at once, or on two sides of a partition, or one of them
+pretends to: it crafts a claim whose dependencies are old, from before the other's. No
+node can tell these apart, and both claims are valid as their writers saw the
+document, so every node holds both. What decides is who can be shown to have seen
+what. The witnesses for a claim are its **side**: the nodes of its user and of the
+users on its line, the root's among them.
+
+* A claim is **late** against another if a change committed by a node of its side
+ (one carrying for such a user when it committed it) has the other in its history
+ and not the claim itself: the claimant's side had seen the other while the claim
+ wasn't there yet.
+* Of the claims of a name that count and aren't released, those not late against any
+ other remain. If one remains, its user holds the name. If none or several do, the
+ name is **contested**: nobody holds it, until releases leave one.
+
+Being late never goes away (changes aren't taken back), and it is read from the
+changes alone, so every node finds the same.
+
+What this gives:
+
+* Every node writes its member entry about daily (its alive stamp, *docs*), and such
+ a change has in its history everything the node had received. The root is on every
+ line. So once the root's node wrote after receiving a claim, while it had received
+ no other of that name that isn't released, every claim of the name surfacing later
+ is late against it, however old its dependencies look: the claim is settled. A node
+ tells (`settled`, below). The root's node writes when a claim reaches it that it
+ hadn't seen, a minute apart at most, so a claim is settled within a minute of
+ reaching it.
+* A user that is up is bound by its own node: after its node wrote once with a claim
+ in its history, it never holds that name against it.
+* To take a name from a claim, a rival needs the claim to be late and its own not to
+ be: a node on the claimant's line saying it saw the rival first, and nobody on the
+ rival's side having written after seeing the claim. After the root saw the claim,
+ the second can't be.
+* Before a claim is settled, a claim out of its sight contests it. Honest users
+ claiming at once see "contested", and one releases.
+
+A user's name is claimed the same way: the `create` statement is the claim, its side
+the creator's side and the new user's own nodes. Two creations of one name out of
+each other's sight are decided as above; while it is contested, no user has the
+name, and neither namespace counts.
+
+A user that doesn't have its name still holds what it claimed where anyone may claim:
+those claims stand, so contesting a user's name frees none of them. What a rule gives
+to a name (a kept name, a name only `tv` claims) is nobody's while the name is.
+
+### Cuts: what stops counting
+
+A **cut** is a set of changes, named by their hashes: "as far as I saw". A statement
+is within the cut if a change that carries it is in the history of the cut. Whether
+it is doesn't depend on who asks or when: a change's history is fixed by its hash.
+
+* **Retiring a key** names the key and a cut. What the key signed within the cut
+ counts as before; what it signed outside doesn't count, for anyone, whenever it
+ shows up. A statement signed with a retired key and offered later with old
+ dependencies is outside the cut. That is how retiring reaches a node that was away:
+ it needs no order of arrival.
+* **Who retires what.** A key above another retires it with any cut: a user whose
+ device key was stolen cuts it off from before the theft. A key retires itself only
+ from where it stands (its cut must hold everything it signed that the retiring
+ change's history holds): rotating, not rewriting. No key retires one beside or above
+ it, so a stolen device key can't cut off the keys above it, nor undo what it signed
+ before.
+* **A node that stops carrying** is named with a cut the same way, by the key that
+ named it or one above: what it committed outside the cut is nobody's, as a witness
+ or as a carrier.
+
+What stops counting stays in the document, and every node holds the same. It is left
+out when resolving. In a namespace, where a value that doesn't count replaced one
+that does, the one that does is read again (Resolving).
+
+The price: a key above retiring one below with an early cut takes back what that key
+signed since, a vouch or a release among them, further back than an unvouch reaches.
+Whoever relies on another's statement relies on that; a second vouch is what makes a
+user independent of its creator's keys. And a user's first keys have none above them:
+one of those stolen is that user lost, to be unvouched, and created again under
+another name.
+
+A cut reaches no further back than the epoch (Remakes).
+
+### Bans: for those who follow
+
+A user may state that another doesn't count for it, from a cut on: `ban`. A node
+resolves with the bans of the users it **follows** (`--follow`; its own user by
+default): what the banned user signed and what its nodes committed outside the cut is
+left out, as a holder and as a witness. Bans are taken in the order they were made;
+of two made out of each other's sight, the one by the user nearer the root first; 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
+user's write that only its bans make valid (a claim of a name the banned user holds):
+the others would refuse it, and everything the node writes after.
+
+This is the trust root a user has beyond the swarm's: the root every node is given,
+and whom it follows. In a dispute, the nodes following a user read its side.
+
+### Decisions
+
+Some things are the swarm's to decide: its voting numbers, who the root is, when to
+compact, which change of policy is the swarm's. 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
+base and aren't absent there: the root, those who were the root, and the users each
+created as the root. So a first user votes from the next remake on, one that left
+is no voter after it, and until the first remake the root decides alone. A root
+can't fill the votes with users of its own: those who vote see who would, before
+they vote for the remake, and can seat another root first. Users further down the
+lines don't vote: whoever brought them in speaks for them, and a vote a user would
+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 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.
+
+A decision also takes more than half of all the epoch's voters, 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).
+
+**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 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.
+
+Every node acts on a decision that counts, unless another following the same one
+counts too.
+
+**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.
+
+**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. A node that joins later takes the decisions
+that count 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.
+
+**What is decided.**
+
+* **The voting numbers**, `window` and `absent`: they hold for what is proposed in
+ sight of the decision.
+* **The root's seat** (below).
+* **A remake** (below).
+* **A change of policy** (below): proposed and voted on the same way, made and
+ followed by choice.
+
+### 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:
+
+* 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
+ didn't before;
+* the old root is a first user like the others, and votes on. It keeps its name, its
+ namespace and what it holds, and stays on the lines of those it brought in;
+* what a root signed as the root is the seat's: the new root may unvouch the users
+ the old one created, and the old root itself, which goes alone (those it brought
+ in stay, their lines running through the seat). The old root no longer may.
+
+What the old root did that the decision'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.
+
+So a root that disappears is replaced by two thirds of the voters present, and so is
+one that turns on them. (Before the first remake there is nobody to do it: a swarm
+remakes once its root brought the first of them in.) A root that comes back finds
+itself a first user; it may be voted in again. While the seat's holder is away
+nothing new is settled: claims count, and can be contested, until there is a root
+again.
+
+### Remakes
+
+A federated swarm compacts by a remake its voters decide. 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 ten minutes, 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 voting 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. It leaves out what doesn't count, and all history.
+* **Carried over.** A node writes again, as they are, the statements of its user
+ that the base lacks. An envelope names the epoch it was signed in. A claim signed
+ in an earlier epoch is in sight of the claims the snapshot holds settled, and is
+ refused where the name is held; it is out of the sight of those it holds unsettled,
+ and contests them, until what nodes commit from then on shows one late.
+* **What a remake makes final.** A cut reaches no further back than the snapshot.
+ What a key signed that counted at the base counts for good, though the key be cut
+ off later from before; so for an unvouch. The days a vote takes are the time to cut
+ off a stolen key and to sort out what is contested. After, what a thief did stands
+ until it is undone by hand.
+* **A node that was away** gets the rest of the old epoch from its peers' archives
+ (they answer for an epoch they left as long as they keep it, `archiveRetention`),
+ sees the decision count, builds the snapshot itself and carries its own over. Where
+ no peer keeps the old epoch any more, its operator gives it the new epoch's ID, and
+ it takes the snapshot by that.
+* **A node that joins** is given a root to start from, by whoever brings its user in:
+ the genesis root, or the ID of a later epoch.
+* **No other kinds.** `replace` and `import` put content nobody signed: they start a
+ new swarm.
+
+For the registry the document is small: a statement is under a KiB, a node's alive
+stamp with its signature about 300 bytes, so a hundred nodes write some 10 MiB a
+year. A remake a year is plenty.
+
+### A change of policy is a fork
+
+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:
+
+* 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.
+* 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.
+
+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.
+
+### What every node judges
+
+A change is valid if, in the document at its dependencies:
+
+1. It has no dependencies and is the root the node was given (the genesis change, or
+ the epoch's snapshot). Else:
+2. Its committer is a node that carries for a user there, one that isn't out and
+ hasn't stopped there, proven by the node's signature.
+3. Apart from the node's own member entry, 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 (but for a `node` statement) the committer carries for that user.
+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 isn't released or out, a rule of
+ the kind `user` lets the signer, and the signer is within its limit. `key`, `node`,
+ `vouch`, `ban`: within the limits. `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 every claim of that name there is released or its user out.
+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.
+9. `_continuum/access`, the swarm ID, the epochs: never.
+
+Where a rule goes by a user's name (6, 7) and the name has several creations there, a
+user that lost it, as far as the change's history shows (another remains, Two claims),
+is refused; while none remains, each of them may write. So a user whose late creation
+of a name is known can't scribble over the namespace, and one whose name a liar got
+contested goes on writing, for those who ban the liar to read.
+
+Judging reads the registers at the change's dependencies, and nothing else: no cut is
+applied beyond a retiring or an unvouch that is there. A statement in a register was
+valid when it was taken, so judging the next needs no look further back.
+
+### Resolving
+
+The **resolved registry** is a function of a set of changes that holds each change's
+history (and, for reading, of the users followed). In five steps, each reading only
+the steps before:
+
+1. **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), it is within every cut a counting
+ `retire` sets for that key, 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. Past a
+ limit, a user's creations and vouches don't count: those made in sight of the
+ others first, then by digest.
+2. **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
+ voting numbers, and who the root is.
+3. **Users' names.** Of the creations of a name that count: the one that remains (Two
+ claims) has the name.
+4. **Names.** A claim counts as a claim if its statement does and a rule lets its
+ user: anyone vouched for where the rule says `"*"`, else the user that has the name
+ the rule gives. Past the kind's limit, a user's claims don't count, as in 1. Of
+ the claims of a name: one that has another unreleased one in its history loses to
+ it; of the rest, the one that remains holds the name. A kept name is held by the
+ user that has the user name.
+5. **Values.** A value in a namespace counts if its envelope does and its user has the
+ name. Where the document holds several at one place (written at once), those that
+ count, in Automerge's order. Where the one there doesn't count, the last that does,
+ back along `prev` (read in the document at the dependencies of the change that
+ replaced it).
+
+A node keeps the resolved registry as it goes (a statement changes it around its own
+name), and answers from it:
+
+* `GET /registry/users/<name>`: the user that has the name, its keys and nodes, its
+ line; or that it is contested, between whom; or out, since where.
+* `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 voting 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 /store`, as today, with a value that doesn't count marked, and the one that
+ counts in its place.
+
+### Partitions, and coming back
+
+Three things are "conflicting data", and are dealt with apart.
+
+**Names claimed twice** are no partition: every node holds both claims and finds the
+same holder, or contested. The users see it and one releases; nobody drops anything.
+
+**Two epochs**, after a change of policy or after two remakes that both count, are
+a fork: each node is where its operator put it, or where it got to first, and moves
+to the other side by being told to (it builds that side's snapshot from the base it
+holds, and carries its own over).
+
+**Changes one side refuses** are a partition nobody chose. A node is cut off from the
+others when it holds a change they refuse, as everything it writes after builds on
+it. That happens by three things, and each shows:
+
+* **Its own node broke a rule**, by a bug or on purpose. The others refuse the change
+ and, its signature proving it its own, the node as a whole: `GET /peers` (`refused`,
+ with the change and why), the dashboard, `sync.refusedPeers` *(docs)*.
+* **The two sides judge differently**: versions that differ, a bug in a rule. Each
+ refuses what the other takes; a node told so judges what it holds again and logs
+ that the policies disagree *(docs)*.
+* **An actor forked.** A node committed two changes under one actor and number and
+ gave each to some. Automerge holds one change per actor and number, so a node
+ holding one can't take the other, nor anything built on it *(checked)*. Both carry
+ the same node's signature: a node offered the second holds proof, shows it, and
+ refuses the forking node from then on.
+
+Coming back is the side's own choice, never forced: an operator has the node **drop**
+what the others refuse (`POST /admin/refused/drop`, naming the peer whose word it
+takes). The node's document becomes what it would be without those changes and what
+was built on them, as a drop does today *(checked: `AppState::drop_taken`)*. Its own
+user's statements among them go to the quarantine and are written again as they are:
+an envelope doesn't name the change that carried it, so it is as valid in a new one.
+Those that no longer fit stay there for the user to sign again: one replacing a value
+that is gone, or a cut naming a change that was dropped.
+
+Which side drops, for a fork of an actor? No rule can say: the forking node may have
+ground the hash of the change it kept back, and a rule by hash would let it make
+everyone drop a year's work. A node shows which of the two its line holds (its
+creator's nodes, up to the root's), and the operator follows its line 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
+
+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.
+
+### The API, tokens, reading
+
+* **Writing** through a node is handing it envelopes: it carries those of the users
+ it carries for, judged before they leave, as today. `scripts/continuum-signed`
+ signs them, votes among them.
+* **A node's API users** (`--htpasswd`, a proxy's header) are its party's: they read,
+ and use the node's admin endpoints. The registry gives nobody anything on another
+ party's node.
+* **Tokens** are taken only from the node itself and from nodes that carry for the
+ same user. (Today every node takes a token any member signed *(docs)*: a party's
+ node could let itself in anywhere.) Ended sessions are each node's, as in a swarm
+ without a registry.
+* **Reading**: every party reads everything. The registry holds public keys, names
+ and addresses, nothing secret; a value only some should read is sealed for them, as
+ today.
+* **The swarm key** stays a secret all nodes share: it keeps those who aren't parties
+ from connecting and reading, as far as every party keeps it. It is no boundary
+ between the parties.
+
+## Why it holds
+
+**Promise 1**, by how a change is judged: every node judges a change before applying
+it, at its dependencies, and refuses it with everything built on it *(checked:
+`access::rules::judge_reading`, `access::screen`)*. The rules above read the document
+at the change's dependencies alone.
+
+**Promise 2.** Whether a change is valid depends on the change and its history; its
+hash covers both. So honest nodes agree on every change, hold the same valid changes
+once each has what the other has, and Automerge gives the same document for the same
+changes. Resolving is a function of the changes and the follows; a node's clock
+enters only into what its own node writes (taking a decision up), never into what it
+reads from the document. What must hold for this, and doesn't everywhere today:
+
+* the root given, never learned;
+* no window without a policy (it is in the root), and no syncing under a policy the
+ node doesn't know;
+* a batch judged at heads that differ from a change's dependencies only where nothing
+ the rules read changed in between: the registers must be among what screening takes
+ as read by rules *(checked: `screen::relevant` lists members, users, remakes, votes,
+ access and bans; claims must join)*;
+* nothing a node keeps a bounded number of decides validity: a change pushed out of
+ the waiting ones comes again *(checked: `MAX_WAITING`)*;
+* one change per actor and number. A forked actor breaks "hold the same changes"
+ until one side drops. Automerge names a change by actor and number where
+ Kleppmann's construction needs the hash.
+
+**Promise 3.** A value counts only with an envelope whose signature checks against a
+key that step 1 gives its user for the change that carries it. A key is a user's only
+by statements signed with keys above it, up to its creation, signed by its creator,
+and so on up to the root, in the root every node was given. A node that carries
+doesn't sign in the user's place: the envelope names its swarm, epoch, path, user and
+user ID *(checked for swarm, path and user: `access::envelope::check`)*, so it counts
+nowhere else and for nobody else. Across a remake: every node builds the snapshot
+from changes it judged, so the snapshot holds only what counted.
+
+**Promise 4.** Steps 3 and 4 name a holder only where exactly one claim remains.
+
+**Promise 5.** Let c be a claim that counts and isn't released, and s a change the
+root's node committed with c in its history and no other claim of the name that isn't
+released (c is settled). Let c' be any other claim of that name. It isn't in the
+history of s. The root 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 that counts stands. 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
+orders are in the document under its user's name, and the registry says so.
+
+Before the root saw c: a claim c' out of its sight isn't late unless its own side
+shows otherwise, so the two contest each other, or, where c's line saw c' first and
+says so, c' holds. That is first come, first served as far as anyone can witness it.
+
+What the assumptions are: signatures and hashes hold; an honest node's changes build
+on all it has received (Automerge does that); the changes the root's nodes commit
+count (their node keys aren't stolen, or are cut off once they are).
+
+**Promise 6.** No rule for what users write reads a vote or waits for another party;
+a write is refused only by what its own history holds, and counts from the start.
+
+**Promise 7.** Let E be the voters present at a decision's dependencies, 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.
+
+**Unvouching** takes away no more than its signer could have kept from seeing: what
+the unvouched user signed stays if a node of the unvoucher or the root committed a
+change that has it in its history and not the unvouch. Those changes can't be taken
+back, so the point only moves forward as more of them show up.
+
+## Roads not taken
+
+* **Agreement among the parties for every write** (PBFT and the like) would give one
+ order of claims, final at once. It needs a fixed set of voters of which fewer than
+ a third fail, halts the smaller side of a partition, and makes every claim wait for
+ a round. It works against what continuum is for: no node special, each working on
+ its own, both sides of a partition going on. Here only decisions take it, a few a
+ year.
+* **A registrar**: every claim signed by the root before it counts. That is the
+ root's witnessing made a condition: one order, but nothing counts while the root is
+ away, and the root decides alone. Here the same witness only makes final what
+ already counts.
+* **Deciding by a hash or a timestamp** of the claims: whoever crafts later chooses
+ the lower hash and the earlier time.
+* **Deciding by seniority** (the user nearer the root holds the name): always a
+ holder, but a senior user whose line kept silent takes a junior's name by
+ backdating, and a root that lies takes any. Contested, with `release` to give a
+ name up, fails safer: a name is blocked in the open, not taken.
+* **Votes by nodes, 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.
+* **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 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.
+* **The root's clock** for who is absent and when votes run out: the root is the one
+ being replaced.
+* **A document per user**, each written by one party, a claim counted across them (as
+ in Secure Scuttlebutt's feeds). A fork of an actor then spoils only the forker's
+ own document, not the swarm, which is the one thing this design can't give. It
+ needs what continuum doesn't have: many documents, and what one had seen of the
+ others recorded by hand. One Automerge document records that by itself.
+
+## What members are trusted with, then
+
+[`trust.md`](trust.md) lists what a member can do today. In a federated swarm:
+
+| Today | Then |
+|---|---|
+| Write or delete any user data | Only envelopes, of users it carries for; nothing plain, nothing deleted from a register |
+| Write as any user | An envelope needs the user's key |
+| Edit the member and user registries | Its own member entry; users come and go by statements, which only add |
+| Forge votes, commit a remake alone | Votes are users', signed; a decision takes two thirds of the voters; every node builds the snapshot itself |
+| Make the swarm forget peers | A forget record of another's node is refused; whom a node talks to is its own |
+| Advertise an epoch a rejoining node follows | A node follows a decision it sees count, 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 |
+
+"Member" stays the word for a node in the swarm; the unit that is trusted or not is
+the party.
+
+## Continuum, or the application
+
+In continuum, as rules every node judges by and data that parametrizes them: users by
+statements (creating, vouching and unvouching, 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 root's seat, remakes that
+every node builds, a change of policy as a fork, 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.
+
+In the application, for the registry: which kinds there are and their rules (data in
+the root), what a host's entry holds, the client that signs (an extension of
+`scripts/continuum-signed`), moving the repository in, and writing out the files the
+consumers read. Whether a host's entry may use a name is the application's check
+against the resolved registry, when it writes the files out.
+
+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.
+
+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, and takes no remake but one its first
+users decided. 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, the swarm's time, and a ban
+of a user beside a ban of a node.
+
+## Attacks
+
+Each with what honest nodes do, the promise it touches, and what shows it: `unit` in
+the module named (`access::statements`, `access::claims`, `access::resolve`,
+`access::decisions` are new), `sim` a simulation in `src/simtest/federation.rs`,
+`argued` where no test can.
+
+Statements and users:
+
+| # | The adversary | Honest nodes | Promise | Shown by |
+|---|---|---|---|---|
+| 1 | Signs a statement with a key that isn't the user's | Refuse | 3 | unit, statements: `a_statement_needs_its_users_key` |
+| 2 | Puts another user's statement into a change of its own node | Refuse: its node doesn't carry for the signer | 3 | unit: `only_a_users_node_carries_it` |
+| 3 | Replaces or deletes a statement, or what holds the register | Refuse | 2, 3 | unit: `a_register_only_grows` |
+| 4 | Offers a statement again; or an envelope signed for another swarm, epoch, path or user ID | Nothing where it is there under its digest; refuse elsewhere | 3 | unit, exists for swarm and path: `what_an_envelope_must_be`; `signed_for_its_user_id_and_epoch` |
+| 5 | Creates a user listing a victim's public key beside its own | The victim's envelopes name the victim's ID: they count for no other | 3 | unit: `a_key_in_two_creations` |
+| 6 | Users vouch for each other in a ring away from the root; a user vouches for its creator | None is vouched for by it | 3 | unit, resolve: `no_line_no_user` |
+| 7 | Creates a name in sight of its creation | Refuse | 4 | unit: `a_name_created_once` |
+| 8 | Creates a name another has, with old dependencies | Hold both; late once the root saw the first; contested at worst before | 4, 5 | unit: `a_backdated_creation_is_late` |
+| 9 | Gets a user's name contested to free its names | Its claims stand without the name | 5 | unit: `claims_stand_without_the_name` |
+| 10 | Creates many users, each claiming many names (sybils); creates them out of each other's sight to pass the limit | Refuse past the limits in sight; out of sight, those past them don't count. The limits bound how fast users come, not how many: each 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` |
+| 11 | Adds keys, nodes or statements without end | Refuse past the limits | 6 | unit: `limits_on_a_register` |
+
+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 stop counting from the same point unless another vouches for them; a vouch after brings them back whole. Its names are free in sight of the unvouch | 4 | unit: `those_it_brought_in`, `a_vouch_brings_back`, `free_in_sight_of_the_unvouch` |
+| 16 | An unvouched user's nodes keep sending | Refused as peers; nothing taken from them | 6 | sim: `nothing_from_an_unvouched_node` |
+
+Namespaces:
+
+| # | The adversary | Honest nodes | Promise | Shown by |
+|---|---|---|---|---|
+| 17 | 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 |
+| 18 | Writes plain values, or protocol data but its own member entry | Refuse | 3 | unit: `nothing_plain` |
+| 19 | Puts an old envelope back | Refuse: it doesn't replace what is there | 3 | unit, exists: `what_an_envelope_must_be` |
+| 20 | 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 |
+|---|---|---|---|---|
+| 21 | Claims a held name in sight of the claim | Refuse | 4 | unit, claims: `first_come_first_served` |
+| 22 | 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` |
+| 23 | 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` |
+| 24 | 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` |
+| 25 | 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` |
+| 26 | 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` |
+| 27 | A whole line kept silent, then claims with old dependencies | Before the claim is settled: contested. After: late | 5 | unit: `silence_before_settling` |
+| 28 | 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 |
+|---|---|---|---|---|
+| 29 | 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` |
+| 30 | Signs with a retired key and old dependencies | Hold it (valid as written), leave it out | 3 | unit: `backdated_past_a_cut` |
+| 31 | With a stolen device key, retires the keys above or beside it | Refuse | 3 | unit: `no_key_retires_one_above` |
+| 32 | 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` |
+| 33 | Steals a user's first key | That user is lost: it is unvouched, and created again under another name | not promised | argued (Cuts) |
+| 34 | 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` |
+| 35 | 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` |
+| 36 | 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 |
+|---|---|---|---|---|
+| 37 | Forges a vote; votes as a user that isn't a voter, or one absent | Refuse | 7 | unit, decisions: `a_vote_is_a_voters` |
+| 38 | Brings in many users to vote | They don't: only those a root created come to vote | 7 | unit: `who_votes` |
+| 39 | 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` |
+| 40 | Decides without the votes, or with released ones | Refuse | 7 | unit: `a_decision_as_written` |
+| 41 | 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` |
+| 42 | 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` |
+| 43 | 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` |
+| 44 | Stays away to hold decisions up | Absent after 30 days: 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` |
+| 45 | 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` |
+| 46 | 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` |
+| 47 | 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` |
+| 48 | 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` |
+| 49 | Tells a node that was away of an epoch that isn't the swarm's | The node finishes the old epoch from archives and builds the next itself; or takes the ID its operator gave | 1 | sim: `away_over_a_remake` |
+| 50 | 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` |
+| 51 | Two thirds of the voters turn on the rest | They remake, seat a root, set the numbers. They forge nothing, take no settled name, move nobody to other rules; the rest ban, stay, or fork | not promised | argued (A change of policy is a fork) |
+| 52 | 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` |
+| 53 | 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` |
+
+Nodes and syncing:
+
+| # | The adversary | Honest nodes | Promise | Shown by |
+|---|---|---|---|---|
+| 54 | Sends different changes to different peers, each under its own number | Take all: changes made at once | 2 | sim: `two_histories_merge` |
+| 55 | 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` |
+| 56 | 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` |
+| 57 | 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` |
+| 58 | 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` |
+| 59 | 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` |
+| 60 | Lies in its member entry (ready, reached, stable) | Sync and trees bend; nothing gets in that way | 6 | argued (Reaching one's line) |
+| 61 | 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` |
+| 62 | 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` |
+| 63 | 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 |
+|---|---|---|---|---|
+| 64 | 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` |
+| 65 | A policy version a node doesn't know | It doesn't sync | 1 | unit: `no_sync_under_an_unknown_policy` |
+| 66 | 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` |
+| 67 | 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` |
+| 68 | 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` |
+| 69 | 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": "…" }}}
+```
+
+`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.
+
+**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
+hash), so what it holds counts without signatures: a host from the root counts until
+its user signs over it, and a claim from the root is held by whoever has its name.
+The maintainer then creates each user as it comes with a key; from then on the user
+signs its own, and is a first user: it votes from the next remake on. A namespace
+whose user never comes stays as the root has it.
+
+**Writing out.** `scripts/kartei-export` reads the resolved registry from a node its
+operator trusts (its own) and writes the tree the consumers read
+(`<user>/hosts/<host>/retiolum/{aliases,ip4,ip6,ed25519.key}`), leaving out what
+doesn't count and any address or name its host's user doesn't hold. Run on each
+change of the document (the store's stream), it replaces the merge button.
+
+## Stages
+
+Each leaves the system working, and is a few patches with its tests. At every stage
+the existing tests pass untouched (69).
+
+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: 58, 59, 65, 69, and
+ the policy's existing tests under a policy from the root.
+2. **Statements.** The users' registers (`create`, `key`, `node`, `vouch`,
+ `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 1 without
+ cuts, and `GET /registry/users`. The client signs statements. Tests: 1 to 7, 10,
+ 11, 18.
+3. **Namespaces.** The rule, with `userSigned` below a name. Tests: 17, 19. 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: 21, 24, 25.
+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, 20, 22, 23, 26, 27, 54, 56, 57.
+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:
+ 28 to 32, 34, 35, 36.
+7. **Unvouching.** `unvouch`, a user giving itself up, those brought in going with
+ it, nothing taken from an unvouched user's nodes. Tests: 12 to 16.
+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: 55, 61, 62,
+ 63, 64.
+9. **Bans** for those who follow. Tests: 66, 67.
+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`, taking up, what counts,
+ `GET /registry/decisions`; the voting numbers. The simulations start from a root
+ change naming several voters, as a remake's does. Tests: 37 to 44, 68.
+12. **The root's seat.** Tests: 50.
+13. **Remakes.** The base, the snapshot every node builds, carrying over, what
+ becomes final; peers answering for an epoch they left; a node given an epoch's
+ ID. Tests: 45 to 49.
+14. **A change of policy.** The fork, followed by an operator's word, with who votes
+ on its side. Tests: 52, 53.
+
+## Open questions
+
+Each with what I'd choose.
+
+1. **Who votes.** The first users an epoch starts with (as designed): nobody but the
+ root brings one in, and none votes before two thirds of the voters remade, so a
+ root can't fill the votes. The price: the root decides alone until the first
+ remake. Or first users from when they come: then a root that turns bad stays. Or
+ after a wait without a remake, which needs a user's age read from what others
+ saw of it. Or users further down, to a depth the policy names: then every first
+ user brings voters in. *As designed.*
+2. **Two thirds, or a majority?** Two thirds of those present (as designed) stand
+ against fewer than a third malicious; a majority is easier to gather and stands
+ against nobody who takes up two decisions. And of whom: with the absent left out
+ down to half of all (as designed), those who stay decide, and a malicious voter
+ can make two decisions across a long partition; with two thirds of all, none
+ can, and a third leaving ends all decisions. *As designed.*
+3. **Throwing a user out by decision**, for everyone, where its voucher won't unvouch
+ it. Not designed: bans are each party's. It would make two thirds of the voters
+ the judges of who is in. *Later, if bans prove too weak.*
+4. **Does cutting a key off take back what others rely on**, a vouch, a release? Yes
+ (as designed): what a thief signed must be undone. Or not for what another user
+ built on: then a thief's puppets stay for good. *Yes*, with the second vouch as
+ the remedy.
+5. **How far an unvouch reaches back**: to what its signer's nodes and the root's had
+ seen (as designed). Or only forward from where each node receives it, which no
+ node can read from the document. *As designed.*
+6. **A user's first keys** have none above them: stolen, the user is lost. Several
+ first keys of which a number must sign to cut one off would mend that; continuum
+ has no rule for several signatures yet. *Later.*
+7. **The numbers**: `window` 7 days, `absent` 30 days; 64 keys and 8 nodes a user
+ at once, 16 users a creator (the root: no limit), 64 addresses a user; ten
+ minutes a remake's base may lack. Guesses; the policy carries them, and the
+ first two change by decision.
+8. **How long peers answer for an epoch they left**: `archiveRetention`, 30 days
+ today. A node away for longer needs the new epoch's ID from its operator. *90 days
+ in a federated swarm.*
+9. **A user without a node** names another party's to carry for it. Allowed as
+ designed: that node's changes then witness for both. Fine?
+10. **What the root holds unsigned** (the repository as it is) counts until signed
+ over. Or the import waits until every user has a key. *Count unsigned*: the
+ repository is what is trusted today.
+
+Settled: a name is contested, not the senior's, with `release` to give it up; one
+root, whose seat is decided anew; votes by users, over days, by numbers a decision
+changes, the absent left out; unvouching as above; a change of policy as a fork; the
+mode optional.
+
+## What was checked, and what is assumed
+
+Checked in the code:
+
+* A change is judged at its dependencies, or at heads that hold the same for what
+ rules read, before it is applied; a refused change keeps out what builds on it; a
+ member whose own change broke a rule is refused as a whole (`access::rules`,
+ `access::screen`).
+* What screening takes as read by rules is a list (`screen::relevant`).
+* An envelope is checked for its swarm, path, `prev`, signature, and its user's key
+ in the registry as the writer saw it (`access::envelope::check`).
+* The genesis node writes the users it starts with and their keys in its first change
+ (`access::registry`), so the policy can be written with them.
+* Automerge refuses a second change at an actor's number (`DuplicateSeqNumber`), and
+ gives the change that made an operation (`hash_for_opid`), so the change carrying a
+ value is found from the document.
+* A drop exists and does what coming back needs (`AppState::drop_taken`); what is new
+ is an operator asking for one its own rules wouldn't make.
+* Neighbours are computed from the member list by key hashes (`swarm::topology`).
+* A member's entry holds its alive stamp as Unix milliseconds (`swarm::members`).
+
+From the docs: a node under an unknown policy receives without judging; every node
+takes a token any member signed; alive stamps about daily; where a refused member
+shows; the cost of checking a signature; a node keeps the documents of epochs it left
+for a while, and every node builds a remake's snapshot itself today.
+
+Assumed, to be shown as it is built:
+
+* Whether a change is in the history of a cut, or of a witness's change, can be
+ answered cheaply enough: per actor, the last number within that history, kept per
+ cut and per claim that has a rival. Statements and rivals are few.
+* Resolving kept as the node goes costs little per change. Judging at old
+ dependencies reads the registers there (`document::at`), as an envelope's check
+ does today.
+* Writing a quarantined statement again as it is works after a drop wherever what it
+ builds on still holds.
+* A node can keep neighbours beside those the graph gives (its creator's nodes).
+* A limit counted at a change's dependencies (a kind's claims by one user) is read
+ fast enough from the register; an index kept beside it otherwise.
+* Who lost a user name as a change's history shows it (judging, 6 and 7) is worked out
+ only where a name has several creations there, which is rare.
+* The snapshot of a federated remake, built from the resolved registry, comes out
+ byte for byte the same on every node, as today's does from the document.
+* A node can answer sync for an epoch it left, from its archive.