| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
| |
With the other options of the document file: what it does, what it saves (a writer per
node and per crash or drop, rather than per start), and what it must not meet: two
nodes started from copies of one document file, or a node from an older copy of its
own, which would write as the same actor.
|
| |
|
|
|
|
|
|
|
|
|
| |
How many writers (Automerge actors) the document's changes have matters: a writer is a
node's run (each start, rejoin or drop makes one, a start keeping the one of before with
`--keep-actor` after a clean stop), and every transfer of the whole history carries a
signature for each. `/admin/stats` now says it (`document.writers`, and
`document_writers` for Prometheus), counted from the changes' metadata alongside the
saved size's measurement in the background (at least a minute apart), as that takes
time proportional to the document too; an export, which measures the size alone, keeps
the count of before. The dashboard's figures show it with the rest.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
actor
A node writes as a fresh Automerge actor at each start, so no change number of an actor
comes twice: one sent before a crash but not yet appended to the document file would
otherwise be made again, differently. An epoch thus gathers a writer per start, and
every transfer of its history carries a signature per writer.
The document file now says when it holds everything the node wrote:
* at a clean stop, after the last append, an empty `Closed` record, only where the
file is up to the document's heads (ready, not rejoining);
* as a node starts on the file, before anything is written, an empty `Opened` record,
so a crash from then on leaves a file without the mark;
* the header names the actor the node writes with (`current`).
With `--keep-actor` (off by default; needs `--document-file`), a node starting on a
file that ends with `Closed` writes on as that actor. A crash, a drop, a switch or a
rejoin still starts a fresh one, so an epoch's writers come to one per node plus one
per such event. lifecycle.md says so.
|
| |
|
|
|
|
|
|
|
| |
`GET /auth/whoami` lists the user's capabilities (`caps`) and its token's `limit`, if
any, side by side; what the request may do is what both give, as endpoints decide it.
It says so now (`may`): the capabilities, or with a limit each capability met with each
of the limit's (`Capability::meet`): at the deeper of the two paths, a `*` of one
taking the other's key there, with the rights both name; nothing where the paths part,
no right is shared, or the path is protocol data one of them doesn't name.
|
| |
|
|
|
|
|
|
|
| |
Docs and comments outside future-work.md and review-rejections.md say what the code
does, and point nowhere else for what it doesn't: the architecture page's map, the
transports' forwarders, keys with `/`, a strict swarm's registry, trust.md's "Not yet"
(now saying what isn't protected, in a sentence), the topology bench, and the comments
of the change benchmarks, capabilities' grantor, tokens, dissemination, refusals and
topology.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Under a policy requiring signatures, a node takes a change only once a signature
vouches for it: one of the change itself, or of a later change of the same writer. A
writer is a node's run, each start, rejoin or drop making a fresh actor, so an epoch
gathers writers for as long as it lasts, and a transfer of its whole history (a joiner
pulling the document, a delta) must carry a signature for each of them, or the joiner
waits for the rest for good.
A message's signatures are now chosen by what it carries:
* changes, up to 256 signed among them: each one's signature;
* more (a whole document pulled): each writer's latest signed change among them, found
by the document's change metadata (`Signatures::for_changes`), vouching for the rest;
* a delta: each writer with changes in it (`get_changes_meta(from)`), its latest
verified signature (`for_delta`).
A message carries up to 65536 (6 MiB), and as many wait at the receiver for their
changes (a delta's come after it). So a transfer leaves writers unvouched only in an
epoch with more than 65536 runs writing since it began; a remake starts the count again.
Wire version 48.
|
| |
|
|
|
|
|
|
|
|
| |
Review 4, R3 partly: nodes refuse a strict remake of a strict swarm by its kind, a label
the proposal and the snapshot's metadata carry. A majority of members labelling one
`compact`, its snapshot holding the registry it wants, gets it followed: nodes whose own
build differs take the quorum's snapshot. access.md says outright that a strict swarm's
registry, policy and bans change by its users' signatures against a minority of members;
comparing a downloaded snapshot with the node's own build at the signed places, and
refusing the switch where they differ, goes to future-work.md.
|
| |
|
|
|
|
|
| |
Review 4, 1269's note: the node challenges for a password (`WWW-Authenticate`) only
what isn't a browser, by `Sec-Fetch-*` or a `Mozilla/…` user agent. A client calling
itself Mozilla that waits for a challenge before sending its password would get none:
api.md says to send the password with the request, as `curl -u` does, and why.
|
| |
|
|
|
|
|
| |
Review 4, 1258's note: the glossary says that a path whose way out broke while the
peer's PINGs still come is found by this node's own unanswered PINGs, which with the
turns come every one to two intervals, up to twice as long as before, and what still
finds it at once.
|
| |
|
|
|
|
|
| |
Review 4, 1257's note: measurements.md now says that no endpoint takes a secret in its
query, but every path read or written is in the request log, and paths are data here,
so the lines go wherever the node's log goes; and that a user who may set the node's
log filter may turn them on.
|
| |
|
|
|
|
|
| |
Review 4, 1250's note: a key's comment was kept as given, unbounded but by the request
limit (2 MiB), in protocol data every node holds and the dashboard shows. It says whose
and where the key is: at most 256 bytes now (`sshsig::MAX_COMMENT`), a longer one
refused with the key.
|
| |
|
|
|
|
|
|
|
|
| |
Review 4, appendix: api.md's Signed changes said a node passes on each node's latest
signature (true of deltas, not of messages carrying changes) and that every change
arrives vouched for. It now says what a message carries (its changes' signatures, each
run's latest past 4096 and for a delta), that a node signs each of its runs' latest
unvouched change at start and when its document is swapped (1277), and the two cases
that still arrive unvouched (a change vouched for only by a refused one, postponed;
more than 4096 runs writing in an epoch).
|
| |
|
|
|
|
|
|
|
|
|
| |
Review 4, F12: `Screening::of` started screening over when the document was swapped (a
drop, a switch, a rejoin), the root pin alone carried on, so whether the document had
held a policy (`policed`, 1211) was forgotten there too, not only across a restart as
documented: after a drop with the policy removed meanwhile, late changes written under
it went in unjudged again.
It is carried on now, as the root pin is; what belongs to the old document (changes
waiting, refusals, drops held) still starts over. access.md says so.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Review 4, F11: only streams answered to a token ended with it (1273); one opened by
password (basic authentication) or a proxy's header went on after its user was
disabled or removed.
`streams_end` now wraps those too, by the user `basic_auth` leaves on the answer: such
a stream ends once the document no longer has its user, enabled
(`token::user_taken`, the part of a token's check about its user, which
`still_taken` uses too), looked at as the stream begins and as protocol data changes.
No expiry: a password has none.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Review 4, R2 partly (F8): a joining node waited for "each of its seeds" to name the
swarm's root as a count, of every admitted peer that named one. Node identities are
self-made and admission is the swarm key: a member answering a joiner under as many
identities as it has seeds, naming its own root within the wait, made the joiner pin
it before the real seeds answered, and refuse them after as another history.
Now the seeds are the nodes proven at the seeds' addresses (`roots::seed_found`, as
`bootstrap::resolve_seeds` finds them), and only their roots count toward the wait
and start its clock. Another peer naming a root still disputes it (no root while they
differ), so such a member can keep a joiner from joining, as any member can, but not
hand it another root. access.md says so.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Review 4, F4: `?shallow=true` kept every entry the user may at least pass through, and
a shallow read inlines short scalars' values with their types and sizes. A scalar at a
level a `*` passes through (a capability on `data/*/public`, an entry `data/count:
42`) showed its value though no capability covers it; before `*`, only a scalar where
a capability expected a map did.
Now an entry the user may read only partly is shown only as the way through it is: a
map, with no more than its type (not its size, the number of its keys); anything else
there (a scalar, a list, which a deep read leaves out too) isn't shown. Entries it may
read whole are as they were.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A token was checked when a request came, and a stream answered to it (the dashboard's
events, a subscription's links and messages, the store's streams) then went on for as
long as the client kept it open: past the token's expiry, past its user's tokens being
revoked, its user disabled or removed, its session ended.
Now a middleware inside `login::bearer` (`login::streams_end`) ends a streaming answer
(`text/event-stream`, `application/x-ndjson`) to a request with a token at the token's
deadline, its expiry, or as soon as the document no longer takes it
(`token::still_taken`, the checks `verify` makes of the document, now its own
function): looked at when a change touches protocol data (the root, a switch, among
it), and when a session ends on the node itself in a swarm without a registry
(`AppState::session_ended`). No polling: a timer for the expiry, and the changes as
they come. The client reconnects with the token it has then; a refreshed token is a
new one, so a stream lasts a token's lifetime at most (12 hours by default).
`futures` is a dependency of every build now (for `take_until`), not only of libp2p's
and Tor's.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
After "log out" the page went to the login page, and the browser's back button brought
it back as it was (kept whole in its history): the map, the peers and the console went
on updating, by the stream it still had open and the subscriptions' token it still
held, though the session had ended (the document box, reading anew, got 401s).
Now leaving for the login page (`toLogin`: logging out, or the login ending) stops the
page's reading first: its stream, its subscriptions, its timers and tokens. And a page
the browser brings back whole (`pageshow`, `persisted`) loads again instead, so it
reads with the session it has now, and without one goes to the login page.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Going to another member's dashboard showed its login page for a moment: a browser
without a session there was sent from `/admin/dashboard` to `/login` (by
`auth::basic_auth`), which began a session with the token handed over and went on to
the dashboard.
Now the dashboard's page is served without authentication, as the login page is (it
holds nothing: what it shows it reads with the browser's session). Opened with a token
handed over, the page begins the session with it (`POST /auth/session`) before it reads
anything; without a session, `obtainTokens` goes to the login page, as the redirect
did. A request without credentials is refused at once rather than after the 1.5 s
that slow down guessing passwords: nothing is guessed there, and a page without a
session goes to the login page without waiting twice for it.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The page renewed its token half way through its life by a timer. A timer fires late
where the computer slept or the tab was in the background, by when the token (and the
session cookie, which lives as long) may have ended: the page then fell back to
`POST /auth/login`, which takes a password the browser keeps (basic authentication),
and without one retried every 30 s, logged out without saying so.
Now whether the token is half way through its life is looked at again when the page
shows and every 30 s (`refreshIfDue`, one renewal at a time), so it is renewed while it
still can be, until the login ends (`--session-lifetime`). Where the login ended, or
there is none (400, 401, 403), the page goes to the login page; a node away is tried
again in 30 s. And the page no longer gets in by a password the browser keeps: only
by its session or through a proxy naming the user (`throughProxy`, `login: "header"`).
The node never challenges a browser for a password either (`WWW-Authenticate`, the
browser's prompt): not only on the dashboard's paths and for a page's own requests, but
for any request with `Sec-Fetch-*` or a `Mozilla/…` user agent. curl sends the password
unasked with `-u`, as before.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A node in focus showed this node's `/peers` entry for it and the links it reports:
what this node knows of it, never what it says of itself, though the page subscribes to
it. Now a subscribed node in focus shows its own figures first (`Node:`), as this
node's are (its `/admin/stats`), then this node's entry for it (`as this node sees it`)
and its links, each a branch. A link in focus shows each end's paths to the other as
that end has them: this node's, and a subscribed node's from its `/peers`.
The page reads them from the node every 2 s while it is in focus (`readFigures`,
`figuresOf`), with its narrow token, which may look at a node as any user with a
capability may; pages of other origins may now call `/admin/stats` and `/peers`
(`cors::open`), as they do a node's links and messages. Where they can't be read, the
box says why.
|
| |
|
|
|
|
|
|
|
|
| |
A subscribed node's messages come without being written out (their stream leaves the
text out: up to 8 KiB each, 500 at a time), so the figures box said only when, both
ends, type and size of one. Now the node answers for one message by its number,
`GET /admin/dashboard/messages/own/{seq}` (`{"text": …}`, `404` once it isn't kept),
which pages of other origins may call as they do its streams (`cors::open`), and the
page reads it when the message is shown, with its narrow token, and keeps it on the
message. Where it can't be read, the box says why.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The console said with whom a message went, not how: the message log was noted above
the identity layer (`CountingTransport`), where a message is to or from a node, and the
address it went to (the paths layer picks one, `Paths::send_first`) or came from is
gone. Only the paths layer's own PINGs and PONGs, logged under their addresses, had one.
Now the identity layer notes the node's messages (`MessageLog::set_addressed`), with
the address each went to (`IdentityTransport::send_proven` returns it) or came from;
`CountingTransport` leaves them to it, and still notes them where there is no identity
layer (tests). A console line ends with the transport (the address's scheme, in its
colour on the map, the address in the tooltip), and the figures box shows the address.
A subscribed node's messages carry theirs too, and the map runs every message along
its transport's line, not only the PINGs and PONGs.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Setting a node's log filter took `write` on `_continuum`, all of the protocol data:
whoever could change one node's logging could change the swarm's members, remakes and
users too. Now what a node holds alone has paths of its own for capabilities, under
`_continuum/local/<node ID>/`: `log` (`/admin/log`), `config` (`/admin/config`) and
`debug` (`/debug`, always `write`), `read` to look and `write` to act
(`api::access::needed`, which takes the node's ID). So `_continuum/local/<node ID>` is
everything on one node, `_continuum/local/*/log` the log filter on every node, and
holders of `_continuum` keep it all.
Nothing is stored there: the store refuses writes under `_continuum/local`, and the
rules refuse a change touching it, as for epoch metadata. The quarantine stays with
`_continuum`: its entries are everyone's writes, which a node-local capability
mustn't show.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A capability's path covered by prefix only, so what is the same at every key of a map
(each node's log filter, say, at `_continuum/local/<id>/log` from the next patch on)
took a capability per key. Now a `*` segment stands for any one key, as it already does
in signed paths and sealed places (`envelope`, `sealed`, which now share one `agree`):
* covering (`Capability::covers`): the pattern no longer than the path, and agreeing
with it segment by segment;
* listings (`reaches`): pattern and path agreeing on the segments both have, so every
key on the way shows, nothing past where the pattern differs;
* narrowing (`Capability::holds`, for a token's limit narrowed again and a refresh's):
at or below, a `*` only under a `*`: a limit naming one key never widens to all.
A `*` first is outside protocol data, so it never covers `_continuum`, as `[]` doesn't.
A key that is `*` itself can't be named alone. Every node judges changes by
capabilities, so all must read them alike: they run the same version.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The node's keepalives take turns between the two sides (`PeerState::keepalive_due_at`),
but the paths layer pinged every path in use every `probe_every` (the keepalive interval,
30 s at least) whatever the peer did: a PING from the peer over the path didn't count.
So both sides pinged each path every interval, often within a second of each other
(seen on the dashboard: a PING answered, and our own PING right after it).
Now a PING from the peer over a path is noted (`PathState::peer_probe`), and a path is
pinged again (`rested`) an interval after the peer's PING, if that came last; after
ours, an interval and a half while the peer pings too (its PING within two intervals
before ours), so its turn comes first; else an interval. One exchange per interval
goes over a path, as the measurements' "two keepalives per neighbour per interval"
says, and each side measures every other interval; a shorter `--keepalive` measures
more often. A path whose way out broke while the peer's PINGs still come in is found by
our own PINGs half as often; a failed send or unanswered traffic (`suspect`) still
pings at once.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Requests weren't logged at all, short of what a handler logs itself. Now a middleware,
outermost on the API (and on a relay-only node's), logs a line per request as web
servers do: method and path (with the query), status, the milliseconds until the answer
began (a stream goes on after), where it came from (the address, or `unix`), and the
user (`-` where none was authenticated). The user comes back on the response:
`basic_auth` puts it there, for a token as for a password or a trusted proxy's header.
At debug, under the target `http` (`RUST_LOG=info,http=debug`, or `PUT /admin/log`):
an open dashboard alone would fill the log at info. With the target off, the request
passes untouched.
|
| |
|
|
|
|
|
|
|
| |
The remake dialog set the kind, its input, the settle time and the switch delay, but not
the replay window or the archive retention. Now each has two fields, epochs and an age,
their defaults as placeholders: both empty sends nothing (the node's defaults), one
given sends that limit alone, as the API reads it; in the body, or for an import in the
query (`replayWindow`, `archiveRetention`). Epochs that aren't a whole number are
refused in the page.
|
| |
|
|
|
|
|
| |
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).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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".
|
| |
|
|
|
|
|
|
| |
A key's line was stored as the key alone (`ssh-ed25519 <base64>`), whatever comment it
came with, both from `PUT /auth/keys/<name>` and from `--bootstrap-keys`. Now its
comment is kept after it (`PublicKey::kept_line`), the spaces between its words made
one. The comment checks nothing: every use of a key parses the line, and the parse
reads the key alone.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The credentials dialog listed a user's keys whole, to add one or
remove one but the last, and any failure replaced the dialog with the
error, the form lost.
* A key is shown short: its type, the first and last four characters
of what tells it apart (an Ed25519 key's base64 after the part all of
them share), its comment; the whole line in its tooltip.
* A key can be edited, its name and its line: saved under the name, a
new name written first and the old one removed.
* A user with a password on this node (`--htpasswd`) may remove its
last key: it logs in by the password. GET /auth/keys says so
(`password`), and DELETE /auth/keys/{id} takes it then; the rules no
longer keep the last key on every node (no other node can tell who
has a password where; under a strict policy the signed entry keeps
one still).
* What fails says why beside where it was asked (the add form, the
edited row, the removed one), which stays as it was, to be set right
and tried again.
|
| |
|
|
|
|
|
|
|
|
| |
A right press on a node started dragging it, as any press did: the map
took hold of the pointer, so the right-click went to the map and the
node's menu didn't open, and letting go put the node in focus. The
right button now opens the node's menu alone. Its entry for the node's
dashboard is `go to its dashboard`, greyed where the node publishes
none (and on this node's own), its tooltip saying where it goes or why
not.
|
| | |
|
| |
|
|
|
|
|
|
| |
With the entries' menus (1238), editing had three ways in: the
double-click, the `+` on a map's or list's row, and the menu. The
double-click and the `+` go; the menu does all of it. The title,
which stands for the root (its row isn't shown), now takes the
right-click too: its menu adds a key at the top level.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
The document box edited an entry by a double-click and added to a map
or list by the `+` on its row; a key couldn't be renamed, and a list's
`+` always asked for an index.
A right-click on an entry, or a finger held on it half a second, opens
its menu: `Edit value` (as the double-click), `Rename key` for a map's
key, and on a map `Add a key`, on a list `Append an item` or `Insert
an item at…`. Renaming reads the value, writes it under the new key
and deletes the old one (two writes, the value as JSON; a key that
exists already isn't overwritten). Inside a field being edited, and
where an entry offers nothing, the browser's own menu shows.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
A stream's lag in the tooltip of `behind: …` was one figure, from when
a node logged a message to when the page had it: a second and a half
for a node a LAN away said nothing of where it went.
The node now stamps each `messages` event with when it sent it
(`{"sent": …, "messages": […]}`), and the tooltip splits each stream's
lag: how long its batches waited in the node, from logging the first
message to sending the batch (the node's own clock: exact), and how
long they took on the way, from sending to arriving. This node's own
stream is always listed, beside the three slowest.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The console appended messages as their streams brought them, a
subscribed node's later than this node's by its path, each line with
its node's own clock's time: lines of one moment came apart, and their
times read out of order.
Each message is now listed by when its node logged it, on this node's
clock, as the map runs them (`aheadOf`): one a slower stream brought
late goes in among those of its moment. The time shown is on this
node's clock; a subscribed node's own clock's time is in its tooltip.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Each node's message stream comes its own way to the page, a subscribed
node's over the network to it: messages logged 16 ms apart on three
nodes ran over a second apart, as their streams arrived.
Messages now run when they went, on this node's clock: each at the
time its node logged it, taken back by how far that node's clock is
ahead of this node's (the map's clock chain, `aheadOf`), a while
behind real time (`replayDelay`): the most any stream lagged in the
last 30 s and a tenth of a second more, shown after the sliders
(`behind: …`), each stream's lag behind the fastest in its tooltip.
The browser's clock is against all streams alike and drops out. A
message known only from its receiver (its sender not subscribed) is
placed at its arrival, starting a crossing before. Messages lagging
more than 10 s (a gap) don't run, nor hold the map behind.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The node sent its messages to the page every 2 s (`messages` events,
this node's and a subscribed node's), and this node's ran on the map
from its counts, which come with the state: the map showed a message 2
to 4 s after it went, the messages of each 2 s spread evenly over the
next.
The message log now tells those waiting of each message logged
(MessageLog::logged_after), and both streams send what was logged a
tenth of a second after the first of it (MESSAGES_GATHERED): a busy
node about ten events a second, a quiet one each message soon after it
went. The page runs messages as their streams bring them, this node's
from its own log too (with the addresses its PINGs went to), as far
apart as they were sent; more than six on a link and way at once are
shared out by kind over the moment, and after a gap only the last 2 s
run. The counts still give the rates and what was delivered.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A message between this node and a subscribed one ran from this node's
counts both ways: one the subscribed node sent ran once this node had
counted it received, as it arrived. Each message now runs from its
sender where the page reads the sender (this node's counts, a
subscribed node's messages), so it runs the way it went, also where it
never arrived; only one from a node the page doesn't read runs as it
arrived. The console lists them as before.
Where the page reads both ends of a link, what one sent the other
should count received: a message sent and not received within 5 s
makes the link breathe red once (`breatheLink`), its tooltip saying
how many. Kinds the paths and identity layers count under addresses
are left out, as this node's counts leave them out; a subscribed
node's tick that came short (the latest 500 of more) starts the count
afresh, so what the page didn't see isn't taken as lost.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Dots and flashes ran straight from sender to receiver, whatever line
the message took: a PING over another transport, drawn as a curved
line beside the one in use, crossed the map off its line.
Each message now runs along the line it went over, as drawn (`route`:
the link's quadratic curve, its control point where renderGraph put
it): a message naming the address it went to (the paths layer's PINGs
and PONGs) over that address's transport's line, any other over the
line in use; a flash's streak bends with it.
|
| |
|
|
|
|
|
|
| |
On a dynamic map, links pull toward their lengths against the nodes
pushing each other apart, at one strength. A `pull` slider before the
layout's toggle, shown while dynamic, sets it from a quarter to four
times as hard (2 to its value, once at first), kept in the browser;
the map moves to the new balance smoothly.
|
| |
|
|
|
|
|
|
|
|
| |
A link was dotted where an end reported no links. Members used to
report their links to every dashboard (LINKS); the page now reads a
member's links only while subscribed to it, so a dotted link said no
more than that its other end isn't subscribed, which the node shows
already (hollow), and most links were dotted. The style and its
legend entry go; the link's tooltip says when one end alone reports
it.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A sync message carried the signatures its peer wasn't sent yet, by a
cursor per peer. Changes sent again, as after a message taken as
delivered and lost on the way, went without them: the peer was past
them. Under a policy requiring signatures they waited until the
writer's next signed change, or the next session.
A message now carries the signatures of the changes it carries
(Signatures::for_changes), whatever the peer was sent before: changes
sent again arrive vouched for as the first time. Past 256 (a message
carrying the batches of many writers), each writer's latest instead,
as a delta carries. The cursor goes, and with it resetting it after a
failed send or a new session.
|
| |
|
|
|
|
|
|
|
| |
api.md's Signed changes says what sent, all and none do, what each
costs, and which to choose: sent for nearly every swarm, all where each
change should carry its own proof, none to save signatures where only
protocol data and listed paths need them; and that signatures travel
with their changes, a message carrying none taking none. access.md no
longer says signatures come on their own within a second.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Signatures sent on their own (SIGNATURES, every second, to each peer
not sent them yet) flooded every link: a signature made after its
change left, or one that came with changes, went to every neighbour,
whether or not it got the change along with its signature elsewhere.
With every change of a node signed as it leaves (1217) and signatures
going where their changes go (1218), none is left for them to carry:
the message, its task and its traffic counter go (wire version 46).
Signatures waiting for their changes are offered as before when sync
messages bring them, and the changes waiting for signatures judged
again with the next batch.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A node signed its changes no rule signs a while after them (--sign-every
1s): the change left at once, unsigned, and its signature followed on
its own a second later, to every neighbour, over every link. A few
writes made a cascade of SIGNATURES. Under a policy requiring
signatures it signed every change as it was made instead.
A node now signs its latest change, if no signature vouches for it, as
a sync message takes it (--sign-every sent, the default): the
signature leaves in the same message, and goes where the change goes.
One signature per message, however many changes it carries, each
vouching for those before it: a slow writer's every change is signed,
a fast writer's batches are, at no threshold to pick. A policy
requiring signatures signs what is sent too, even with `none`. `all`
still signs every change as it is made; changes to protocol data,
listed paths and ?signed=true writes are signed as they are made, as
before. The interval, and the task signing on it, go.
|
| |
|
|
|
|
| |
Review 3: since 1184 a proposal is written as the user who asks, so
under a policy the node doesn't know it answers 409 like every user
write. remakes.md says so.
|