aboutsummaryrefslogtreecommitdiffstats
path: root/src/state
Commit message (Collapse)AuthorAgeFilesLines
* refusals: a big drop waits for a second member only where there is onetv3 days1-4/+11
| | | | | | | | | | | | Review 2, N8: a node with one neighbour (a swarm of two, a leaf) never hears a second member name the change: its big drop was held for good, an error logged at every REFUSALS. It now waits only where it has another neighbour than the one telling it; with one, one word is all there is. access.md also says what the wait is: it guards against one peer, not two members saying the same (any node with the swarm key registers), and never makes a node drop what its own rules take.
* access: a change judged by the policy it was written under, locally tootv3 days1-1/+5
| | | | | | | | | Review 2, N10: the judgement before a change leaves (1140) and a replay's (1141) skipped changes when the document *with* the change had no policy it enforces. A write removing the policy, or setting one the node doesn't know, then left unjudged, while receivers judge it at its dependencies, where the policy was enforced. Both now read the policy at the heads before the change, as receivers do.
* remake: no --follow-strict-remakes; a strict remake failing says whotv3 days2-7/+1
| | | | | | | | | | | | | | | | | A strict remake takes a majority of members whose --strict-keys take its manifest's signer: no node votes for one without, so --follow-strict-remakes (1173) goes. A node that doesn't vote still switches once the others commit, as with any remake. So that an operator can tell why one fails: a strict remake aborted for want of a majority (the voting period over, or a majority out of reach) says in its reason (GET /admin/remakes/{id}, and the coordinator's log) which members didn't vote, and that a member votes for one only with its manifest signed by a key of its --strict-keys. Each of those logs why itself (`Not voting for a strict remake`). The sim a_strict_remake_too_few_take: only a takes the manifest; the remake aborts, naming b and c.
* remake: turning a running swarm strict, keeping what an operator signedtv3 days2-0/+12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Until a swarm is strict, the user a change names is its node's word, so any member could have planted registry entries, policy, bans or user-signed values as any user, plain or signed by a key it put there itself; writing the strict policy trusted all of it as it was. A remake of a new kind, `strict`, turns a swarm strict instead: * Its content is a manifest (access::strict): the swarm, the strict policy, and each value kept at the places values are signed, by the SHA-256 of its JSON, signed by an operator (ssh-keygen -Y sign -n continuum-strict). * Every node builds the next epoch as compaction does, keeping at the signed places only the values the manifest names, as they were at the base, and writing its policy (RemakeKind::protocol, the one kind that changes protocol data). Writes after the base reach the new epoch only by replays, judged by the strict policy. * A node votes for one only with its manifest signed by one of --strict-keys, keys given out of the document; without any, for none (logged once, `Not voting for a strict remake`), unless --follow-strict-remakes. A coordinator proposes only what it would vote for (403). Once a majority commits one, every node switches, as with any remake: refusing to switch would only cut a node off. * GET /admin/strict-manifest gives the manifest keeping everything held now, the values for review and the heads they are at; continuum-signed strict shows them, signs once confirmed and proposes at those heads. The sim a_swarm_turning_strict_through_a_remake: a member plants a registry entry as a manager; the operator's manifest leaves it out; a and b, given the key, commit; c, without, follows; the entry is gone and the policy strict everywhere.
* refusals: a drop taking much waits for a second member, and showstv3 days1-4/+58
| | | | | | | | | | | | | | | | | | A node told (REFUSALS) that it holds a change the swarm's rules refuse, and whose own rules refuse it too, dropped it and everything built on it on one peer's word, however much that was; its own writes among them went to the quarantine, the others' nowhere. * A drop taking more than 100 changes, or more than ten and a tenth of the document, now waits until a second member names the same change: the node's rules and one peer's word could both be off. Logged as an error, counted in /admin/stats (sync.dropsHeld). * The others' changes dropped are listed with the refused (GET /admin/refused, "taken unjudged, dropped"), for operators to see. The cheap half of asking the others (future-work.md, Telling a malicious change from a wrong policy).
* login: logins by key capped per usertv3 days1-0/+4
| | | | | | | | | | | | Review 6: a node remembers the challenges used until they expire, 10 000 at most, and refused every login by key once full. One user holding a key could make 10 000 logins within five minutes and keep everyone else out. A user may now log in by key at most --key-logins-per-user times (100) on a node within a challenge's lifetime, past which that user waits and others don't; the 10 000 stay as the cap for all users together. The lifetime, five minutes until now, is --challenge-lifetime.
* state: a document swap keeps the signatures of the changes it keepstv3 days1-3/+4
| | | | | | | | | | | | | Review 2.2: swap_document forgot every signature. After dropping changes taken unjudged (or a rejoin into the same epoch), the node could vouch for none of the changes it kept: under signatures required, those building on an actor waited for that actor's next signature, and for an actor that stopped writing, forever. Signatures::kept_for keeps the signatures of the changes the new document holds, checks each actor's latest again, and keeps those waiting for their changes. This node's replayed changes are signed at once, as before.
* sync: a member refused as a whole, synced with again once it changed documenttv3 days1-0/+5
| | | | | | | | | | | | A node refusing a member as a whole sent it nothing, and lifted that only when a change of the member's was taken again. But a member that dropped what was refused (REFUSALS) starts its sync sessions over, and a fresh session sends no changes until the other side answers: the two waited for each other, and the member stayed refused. The refusal now notes the member's run and epoch_seq when it began; a member with another document since (it dropped what was refused, or rejoined) is synced with again.
* access: a node judges its own changes before they leavetv3 days1-3/+51
| | | | | | | | | | | | | | | | | | | | The API checks the path a request asks for; the rules judge everything the change does. Where they disagreed, the honest node's change was refused by every other node, and with signatures required the node was refused as a whole. The plan had both gates run the same judgement. Under a policy, each change a node makes (its own writes and those through the API) is now judged by rules::judge as receivers will judge it, before it is signed or sent; one every node would refuse is rolled back (the document forked at the heads before it, same actor: the change never left) and the request refused with 403 (StoreError:: Refused). rules::judge_held, judging a change the document holds, moves from swarm::refusals to access::rules for both. The simulations that play a member past its own checks write with a helper that bypasses them (forge). Review finding 3 (the same judgement at both gates).
* access: an epoch's root is never droppedtv3 days1-2/+9
| | | | | | | | | | | | | | | A REFUSALS naming the swarm's first change made the node receiving it judge that change again in a document that isn't empty, refuse it as "a change without dependencies that isn't the epoch's root", and drop it with everything built on it: its whole document. The first epoch's root is now the first change a document holds, as it comes to an empty one or as it is in one; a change judged again on a peer's word is never refused for having no dependencies; and drop_taken refuses to drop an epoch's root, or anything that would leave nothing kept, logging an error instead. Review finding 1 (critical).
* swarm: changes taken unjudged, dropped (REFUSALS)tv4 days1-1/+85
| | | | | | | | | A node refusing changes a peer brought tells it which broke a rule; the peer judges them itself, and if its rules refuse them too, drops them with all built on them (its document forked without them, a fresh actor), quarantining its own writes among them. Wire version 42. Auth plan M6 (rejoin on taint).
* auth: logging in by keytv4 days1-0/+2
| | | | | | | | POST /auth/challenge and /auth/login/key: a node-signed challenge, valid five minutes and taken once, signed with one of the user's SSH keys (ssh-keygen -Y sign -n continuum-login), for a token. Auth plan M7 (key login, M2b).
* access: a node registers before it writestv4 days1-19/+83
| | | | | | | | | Under a policy, a change by a node that isn't a member is refused with all built on it: a node's registration is written before any write of its own or through the API, not after (the registration itself exempt). Auth plan M6 (fix).
* access: signatures requiredtv4 days2-3/+19
| | | | | | | | "signatures": "required": a change counts only once a signature by its committer vouches for it, and waits until then; every node then signs every change it makes. Auth plan M6.
* access: the policy enforcedtv4 days2-3/+29
| | | | | | | | _continuum/access/policy ({"version": 1}) has every node screen every change it receives, on every path changes come in. A policy a node doesn't know stops its writes through the API. GET /admin/refused. Auth plan M6.
* remake: forgetting and releasing name their usertv4 days1-0/+19
| | | | | | | Forget records and released quarantines name the user who made them, so they are judged by its capabilities. Auth plan M6.
* signatures: with sync messagestv4 days2-3/+12
| | | | | | | Sync messages carry, for each actor's writes, the latest signature the sender checked; the receiver checks it once it holds the change. Auth plan M5.
* signatures: --sign-everytv4 days1-3/+30
| | | | | | | | Changes left unsigned are vouched for by the node's next signature: --sign-every 1s (default) signs the latest within a second, all signs every change, none only those that must be. Auth plan M5.
* signatures: changes signed by their nodetv4 days2-5/+27
| | | | | | | | | | Every node signs the changes it commits with its node key, an Ed25519 signature over the change's hash, kept beside the document. One signature on an actor's latest change vouches for its earlier ones. Protocol data and paths listed in _continuum/access/signed are signed, and writes with ?signed=true. Auth plan M5.
* dashboard: without LINKS; LINKS removedtv4 days1-2/+0
| | | | | | | | | The dashboard reads every member's links from the members themselves; LINKS and LINKS_REPLY, swarm::links and their wire tags go (wire version bumped). Nodes the browser can't reach are drawn from what the reachable ones measure. Auth plan M3.
* dashboard: own links over the API, and CORStv4 days1-0/+2
| | | | | | | | | GET /admin/dashboard/links/own and its event stream: a node's own links, as LINKS_REPLY carried them, for other members' dashboards with a token. CORS for the members' dashboard origins and --cors-origins, without credentials. Auth plan M3.
* auth: logging in, and tokens as bearerstv4 days1-0/+4
| | | | | | | | | POST /auth/login (by htpasswd or the proxy), /auth/refresh, /auth/logout, GET /auth/whoami; every request may bring Authorization: Bearer instead of a password. --token-lifetime and --session-lifetime. Auth plan M2.
* access: first users at genesistv4 days1-0/+2
| | | | | | | | --bootstrap-users gives a new swarm a registry from its first change, each first user with everything; other users may do nothing until added. Without it the swarm has no registry and works as before. Auth plan M1.
* refuse misplaced authorstv5 days1-0/+5
|
* HyParView (slim)tv5 days1-4/+21
|
* dissemination traittv5 days1-0/+2
|
* topology traittv5 days1-0/+2
|
* handshakes countedtv5 days1-0/+56
|
* working links recordedtv5 days1-0/+2
|
* worker sends off the passtv5 days1-1/+6
|
* suspend detectedtv5 days1-0/+12
|
* one grace wait per peertv5 days1-0/+4
|
* asks by askertv6 days1-0/+5
|
* round trips in treestv6 days1-0/+5
|
* push by defaulttv6 days1-4/+0
|
* push changes experimenttv6 days1-0/+4
|
* announce only to those behindtv6 days1-2/+2
|
* asked again countertv7 days1-0/+5
|
* hop delaystv7 days1-0/+3
|
* slow round back offtv7 days2-0/+5
|
* hello again when heard fromtv7 days1-0/+52
| | | | | | While we wait for a peer's HELLO_REPLY, anything else that comes from it makes our HELLO go again at once, at most once a second, instead of when the backoff says.
* no heal while both move ontv7 days1-3/+49
| | | | | | | The heal clock now also restarts when our heads and the peer's have both changed since the last STATUS that showed them differing. While writes go on, two neighbours's heads differ because of changes still on their way, and that alone no longer leads to a heal.
* overdue sync asked after from thentv7 days1-45/+88
|
* unanswered sync asked aftertv7 days1-6/+28
| | | | | An overdue sync answer is now followed by a PING too, and the peer stays reached once it answers the PING.
* unanswered members not reachedtv8 days2-3/+88
|
* announce what trees misstv8 days1-0/+3
|
* replace eager links by treestv8 days1-10/+0
|
* add writer treestv8 days2-3/+8
|
* measure 2-hop neighbourhoodtv8 days2-0/+7
|
* introduce eager linkstv8 days1-0/+10
|