aboutsummaryrefslogtreecommitdiffstats
path: root/src/state/host.rs
Commit message (Collapse)AuthorAgeFilesLines
* screen: signatures required as the policy a change came under saystv3 days1-2/+1
| | | | | | | | | | | | | | | | | | | | | Review 3, R1: what a node received was vouched for only while its own document's policy required signatures. A joiner's first sync, and every node's message bringing `required`, went in by the rules alone: a malicious first peer could plant changes under other members' actors at their next seqs, their real ones then failing as duplicates for good. The signatures a message carries are always offered now, and the screen always has them: a change waits for one where the policy at the heads it is judged at requires it (rules::Reads::required, read once per heads), whatever the receiving document holds. Changes written under a policy not requiring signatures wait for none, as on every other node; the check for forged changes runs always. Changes waiting for a signature are judged again whenever signatures come. The test of signatures required now takes the history into an empty document; a new one takes changes written under a policy not requiring them without any.
* screen: what a node receives judged, policy or nottv3 days1-6/+3
| | | | | | | | | | | | | | | | | | | | | Review 2, N7: a node screened received changes only while its own document had a policy it enforces. A joiner's empty document has none, so its first sync, as much history as the node it joins through chose, was applied unjudged, and under signatures required unvouched; the same for any node while the policy's own change arrived with later ones. Sync messages, deltas and downloaded changes are now always screened, each change judged by the policy at its dependencies (1135), so the verdicts are the swarm's. While neither the document nor the changes hold a policy (none touches a "policy" key: inspect::touch_key), only an epoch's root is judged and nothing is placed: bench_screening, after 1188, measured placing everything at about as much again as applying (564 ms + 543 ms for 100 000 changes), judging by the rules at about five times (+2.5 s), which a document with a policy pays anyway. access.md no longer lists the joiner's first sync, nor nodes agreeing only as the policy reaches them (judged at the dependencies, they agree), as left to do; changes::load_delta is for tests now.
* 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.
* remake: no --follow-strict-remakes; a strict remake failing says whotv3 days1-4/+0
| | | | | | | | | | | | | | | | | 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 days1-0/+8
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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).
* 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.
* 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).
* access: signatures requiredtv4 days1-2/+15
| | | | | | | | "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 days1-3/+18
| | | | | | | | _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: changes signed by their nodetv4 days1-0/+2
| | | | | | | | | | 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.
* refuse misplaced authorstv5 days1-0/+5
|
* bootstrap: replace pull by progress notifytv10 days1-0/+2
|
* document: allow writes during rejointv10 days1-100/+169
|
* LWW everywhere, mostlytv10 days1-0/+29
|
* remake: copy stale view when writes stoptv10 days1-0/+13
| | | | This ensures that stale reads show every write that was answered.
* state: drop epoch_txtv10 days1-3/+3
| | | | The stamp is the only lock-free epoch label.
* document: reject writes during rejointv10 days1-2/+14
|
* use Automerge as Document instead of AutoCommittv11 days1-5/+31
| | | | This allows a lot of operations to drop mut.
* rename AutoMerge to Documenttv11 days1-9/+9
|
* remake needs sane clocktv11 days1-0/+4
|
* unify request_sync logictv12 days1-2/+1
|
* move ChangeSet from swarm to documenttv12 days1-1/+2
|
* unify sync_on predicatetv12 days1-4/+2
|
* unify source resettv12 days1-3/+2
|
* peers from member list start as unknowntv2026-10-281-3/+3
|
* move keepalive and firewall to pathstv2026-10-281-8/+18
|
* rename PING/PONG to STATUS/STATUS_REPLYtv2026-10-281-2/+2
|
* introduce bounded blob downloadstv2026-10-261-2/+6
|
* wrap unix timestamps in newtypetv2026-10-261-2/+2
|
* unify peers snapshot logictv2026-10-261-16/+4
|
* unify lock logictv2026-10-261-8/+3
|
* split state moduletv2026-10-251-0/+276