aboutsummaryrefslogtreecommitdiffstats
path: root/src/remake
Commit message (Collapse)AuthorAgeFilesLines
* access: ending a single sessiontv48 hours1-0/+72
| | | | | | | | | | | | | | | | | | | | Logging out of the dashboard (`DELETE /auth/session`) only cleared the cookie: the session's tokens stayed taken until they expired, by every node. Ending them took `POST /auth/logout`, which ends all of a user's sessions. Now `DELETE /auth/session` ends the session of the token it comes with (the cookie, or a bearer token): where the swarm has a registry, it is recorded as `_continuum/sessions/<user>/<session>` (`registry::session_path`), its value until when the session would have lasted, and every node refuses the session's tokens once the change reaches it (`token::Refused::Ended`): the page's own, its narrow tokens, and one handed to another member's dashboard, all refreshed from it and keeping its ID. The rules take the entry from the user itself or one who manages users, as a logout. A remake's snapshot leaves out entries past that time, a user's map where none is left: their tokens are expired anyway. Without a registry the document can't record it: the node ending it refuses the session's tokens itself (`AppState::ended_sessions`, until it restarts), others take them until they expire, as a logout there is refused.
* remake: earlier epochs' metadata read as the snapshot built ittv2 days2-9/+55
| | | | | | | | | | | | | | A snapshot leaving out earlier epochs' metadata (`snapshot::left_out`) and a rejoining node (`catchup::prepare`) read it from the document as it stands. It is never writable through the store, and under a policy the rules take a change to it only from an epoch's first; but with no policy, screening judges only an epoch's root, so a member's own change could rewrite or remove an entry: a replay window narrowed or widened, a translation or a clock changed, a strict remake's entry gone. Now both read it at the epoch's snapshot change (`snapshot::as_built`), as `verify_document` reads the epoch's own: a snapshot from its base's epoch's, a rejoining node from the target's. Every earlier entry is a copy in that change, so what was written there since doesn't count. The first epoch follows none and has none.
* remake: replayWindow and archiveRetention in an octet-stream import's querytv2 days1-2/+50
| | | | | | | An import uploaded as `application/octet-stream` takes `rules`, `baseHeads`, `settle` and `switchDelay` in the query, but not the retentions: for those the document had to go base64-encoded in the JSON form. Now `replayWindow` and `archiveRetention` go in the query too, as JSON like the rules (`{"epochs":1,"age":"7d"}`, as in the body).
* remake: leave out earlier epochs' metadata past the replay windowtv2 days2-5/+123
| | | | | | | | | | | | | | | | | | | | | Every snapshot carried the metadata of all earlier epochs (`_continuum/epochs`) forward, so it grew by an entry, a clock per actor among it, with every remake. Only a rejoining node reads an earlier entry: a node on epoch P reads P+1's (its clock, when it was proposed, its replay window) and every one up to the swarm's epoch (kind, translation, whether it was strict). It replays only while P+1's replay window covers it; beyond, its writes are quarantined whatever the entries say. So a snapshot now leaves out the generations older than the oldest whose replay window still covers a node on the epoch before it (`snapshot::left_out`), judged by the remake's `proposedAt`: the same on every node, and earlier than any rejoining node's clock, so nothing a node can still replay through goes. Metadata that doesn't read stays, with all after it. A strict remake's metadata stays for good: by it a rejoining node, however far behind, tells a strict remake of a swarm strict already (`catchup::prepare`, which reads the generations it finds). There is at most one, short of such a remake. No new key and no change to the rejoin: a node beyond the window that finds some entries and not others quarantines with "cannot translate" rather than "outside the replay window".
* remake: a strict remake of a strict swarm isn't followedtv3 days2-0/+28
| | | | | | | | | | | | | | | Review 3, R3: 1186 kept honest nodes from proposing or voting for a strict remake of a swarm strict already, but a majority of members, each with keys of its own, could still commit one, and switch and catchup followed it without asking what kind of remake made the epoch: the registry, policy and bans of a strict swarm changing by a member majority, the honest minority following. A node now refuses to switch to such an epoch (its document's policy strict, the remake's kind strict), and catchup refuses to rejoin one whose chain of epochs holds a strict remake after the swarm was strict (as the old document is, or once a remake since made it so). access.md says so.
* document: tell whether a document holds a change without building ittv3 days1-1/+1
| | | | | | | | | | | | | | | With the policy's reads kept, judging a change still costs about one read (some 3.5 µs), and so does judging without a policy: what is left is At::new, which asks get_change_by_hash for each head. Automerge builds the change again from the document's operations to answer, at a cost growing with its history. Screening asks it again for every candidate and every dependency, and sync for heads. Ask by the missing dependencies instead (changes::holds), which look the hash up: one the document holds is not among them. A change queued in the document, waiting for its dependencies, isn't either; but then some are missing, and only then is it asked the slow way. Every check of whether a document holds a change goes through it.
* roots: an epoch's root pinned, and named in every handshaketv3 days1-1/+6
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Review 2, N7, the care it raised: a joining node's empty document took as the first epoch's root whatever dependency-less change came first, from whichever peer it joined through; the first epoch's ID is all zeros, so nothing pinned it. A remake's epoch has its ID, the snapshot change's hash, which the rules hold its root to. * Every node names its epoch's root in HELLO and HELLO_REPLY (the epoch's ID for a remake's, its genesis change for the first; wire version 45). A node whose document starts from another root of the same epoch refuses the handshake, logging it: another history under the swarm's ID, a forged root or a genesis started again. * A joining node with an empty document takes as its root only the one it was given (--genesis-root, new), else the one its peers name, and none while they name different ones (access::screen::RootPin, kept in Screening across documents): one peer hands it another root only if it is all the joiner hears, or never, with --genesis-root. A root refused is listed with the refused, what builds on it with it. * The genesis node logs the root (`The swarm's root`); GET /admin/epochs shows it (`root`). A genesis started again under a remembered swarm ID used to merge with the nodes of it still running; they now refuse it (another root), and the docs say to join them with seeds instead. The epoch's identity stays as it is (generation 0 all zeros): records and epoch metadata name it so, and every comparison of epochs with them.
* strict: no strict remake of a swarm strict alreadytv3 days2-7/+18
| | | | | | | | | | | | | Review 2, N2: nothing checked that a strict remake's base wasn't strict already. In a strict swarm the registry, the policy and the bans change only as a user who may change them signs; a strict remake committed by a majority of members, each with keys of its own, could drop any of them (unban, remove managers, empty userSigned and the sealed declarations), and the honest minority would follow. A node now takes a strict remake (proposing or voting) only in a swarm not strict yet: turning strict is done once, by a remake; after it, by signatures.
* remake: aborts written as the user who asks; no abort by timetv3 days2-10/+46
| | | | | | | | | | | | | | | | | Review 2, N3: 1150 let any member write an abort decision once a remake had expired a grace period ago, judged by the change's own time, the writer's word. A member could race the coordinator's legitimate commit with an abort dated as it liked; both taken, Automerge shows one, and nodes that acted on the commit split from those that didn't. The two writers of such aborts are operator requests whose endpoints already need write on _continuum/remakes: aborting an expired remake (DELETE /admin/remakes/{id}) and superseding one (POST /admin/remakes?force=supersede). Both now commit as the user who asks (the proposal and its vote with them, for a supersede), which the rules take as one who may write the decision. The clause judging an abort by its time goes, and with it the time the rules carried. An expired remake is still aborted through any member, by an operator.
* strict: a manifest serves one remake, at its basetv3 days2-9/+27
| | | | | | | | | | | | | | Review 2, N1: a strict manifest wasn't bound to its base. Any member could propose an old, legitimately signed one again at the current heads; nodes would verify it and vote, and every value at a signed place changed or added since (a ban, a new user, a rotated key, a changed userSigned or sealed declaration) would be deleted, nothing after the base being left to replay. The manifest now names the epoch and the heads it was made at, and a node takes it (proposing or voting) only for a remake of that epoch with exactly that base. GET /admin/strict-manifest makes it so; continuum-signed strict proposes at those heads already.
* remake: a strict remake makes the registry, policy and bans anewtv3 days1-0/+16
| | | | | | | | | | | | Review 2, N12: a strict remake kept only the values at the signed places its manifest names, and copied what holds them as it was. A member that replaced _continuum/users by a scalar before the turn left the strict epoch without a registry, where any member may do anything. The holders of the registry, `_continuum/access` and the bans are now made anew as maps, with only what the manifest keeps copied into them; and a manifest keeping no user is refused (at proposal, vote and build alike), as a strict swarm without one would trust every member.
* access: a change judged by the policy it was written under, locally tootv3 days1-1/+4
| | | | | | | | | 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: a replay taken back keeps the node its authortv3 days1-1/+72
| | | | | | | | | | | | | Review 2, N4: replay_newest names the node the document's author once, before its first group. Taking a refused group back forks the document at the heads before it, and Automerge's fork has no author; when the refused group was the actor's first change, the next one was committed as seq 1 without the footer naming the node, refused as "it names no node", taken back too, and so on: the whole replay in the quarantine with a misleading reason. The fork now names the node its author again. Test: the first group refused (a capability revoked in the new epoch), the next replayed.
* remake: no --follow-strict-remakes; a strict remake failing says whotv3 days3-12/+44
| | | | | | | | | | | | | | | | | 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 days6-12/+241
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* rules: a carried remake record taken from the coordinator its snapshot namestv3 days4-1/+10
| | | | | | | | | | | | | | | | | Review finding 7, second part: a record carried into a new epoch whole (proposal and decision in one change) was taken from any member, for any remake ID. A member could write a forged record, decision included, for a fresh ID in a single change. The snapshot's epoch metadata now names the remake's coordinator, as the quorum built it. The carried-record exception holds only in a later epoch, only for the remake that made it, and only from the coordinator its snapshot names, where there is no record yet. Wire version 43: the snapshot, and so the epoch ID, changes. The review suggested carrying the record inside the snapshot instead. The snapshot can't hold the decision, which names the snapshot's own hash. So the snapshot vouches for who may carry the record instead.
* remake: replays judged before they go intv3 days4-2/+118
| | | | | | | | | | | | | | | A switching or rejoining node replays late writes as fresh changes naming each write's user, and nothing judged them before they left: a replay every node would refuse (a revoked capability, a second envelope at a signed path, readers changed) was refused everywhere, and with it everything the node wrote after it. Under a policy, replay_newest now judges each group it commits as every node will (rules::judge_held, in the new epoch); one they would refuse is taken back (forked at the heads before it, same actor) and its paths quarantined with the reason. replay_newest takes the epoch. Review finding 3 (remake replays).
* remake: replay from the quarantine where the user may writetv3 days1-12/+35
| | | | | | | | | | POST /admin/quarantine/{id}/replay needs write on _continuum, which never covers user data, and replayed every path of the entry as the user who asked: every node refused the user data in it. It now replays only the paths the user may write and keeps the rest in the entry (left), for one who may; 403 if the user may write none. Review finding 3 (quarantine replays).
* swarm: changes taken unjudged, dropped (REFUSALS)tv4 days1-0/+17
| | | | | | | | | 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).
* remake: replays keep their writerstv4 days4-25/+198
| | | | | | | | A remake's replay writes each value as the user who wrote it, splitting where several users wrote under a path, so replays are judged as the original writes. Auth plan M6 (remakes, 2.4).
* remake: forgetting and releasing name their usertv4 days2-2/+22
| | | | | | | Forget records and released quarantines name the user who made them, so they are judged by its capabilities. Auth plan M6.
* store: keys_at tells no map from an empty onetv4 days3-3/+8
| | | | | | | | registry::exists counted any value at _continuum/users as a registry; keys_at answers None where there is no map, and its users (records, members, forget records, remakes) say what they make of it. Auth plan M1 (fix).
* refuse misplaced authorstv4 days1-0/+9
|
* dashboard: polish remake ringstv5 days1-1/+17
|
* dashboard: remake switch delaytv5 days1-1/+1
|
* dashboard: add epoch switch indicatortv6 days2-7/+33
|
* dashboard: map remake votestv6 days1-0/+37
|
* fixup 700, 701, 702tv8 days1-38/+2
|
* remake: read ops, not diffstv9 days2-6/+6
| | | | This gives bench_replay a speedup of ~8x/64x/280x.
* api: add type caststv9 days1-0/+1
|
* add remake benchmarkstv9 days2-0/+91
|
* remake: download wakes on end instead of tickertv10 days2-6/+15
|
* remake: decouple grace time from node's keepalivetv10 days4-4/+25
|
* document: allow writes during rejointv10 days4-214/+271
|
* LWW everywhere, mostlytv10 days4-23/+340
|
* remake: copy stale view when writes stoptv10 days2-5/+11
| | | | This ensures that stale reads show every write that was answered.
* remake: log expired and missed votestv10 days2-9/+61
|
* api: off clock refuses aborting expired remaketv10 days1-14/+29
|
* remake: introduce follow-the-majority ruletv10 days1-27/+139
|
* document: reject writes during rejointv10 days4-31/+72
|
* use Automerge as Document instead of AutoCommittv11 days9-144/+174
| | | | This allows a lot of operations to drop mut.
* store function accept any documenttv11 days1-6/+10
|
* tests write through document::testing helperstv11 days5-210/+210
|
* remake coordination use document, not automergetv11 days6-46/+41
|
* one function for changed pathstv11 days1-27/+6
|
* remake needs sane clocktv11 days2-1/+22
|
* remake: drop is_reserved; use is_protocoltv11 days1-10/+5
|
* remake: drop parse_metadatatv11 days1-12/+8
|
* replace EpochInfo by EpochRecordtv11 days1-16/+1
|
* remake: move proposal to its own moduletv11 days3-252/+271
|