| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
A page opened from another node's page takes that page's subscriptions
along; one of them can be this node, another there. The page then
followed this node as a subscribed one too, and showed its messages
twice, from its own log and from the subscription (`→ LLL PING` and
`LLL ← RRR PING`, the same message), on the map as in the console.
The page follows no subscription of its own node: not when it knows
which node it is, and one begun before that stops once it does; its
messages, if any came, are left out.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| | |
|
| |
|
|
|
|
|
| |
Opened by a finger held on an entry, its menu had its top left corner
at the finger, under the hand holding it, a right hand mostly. By
touch it now has its top right corner there; opened by a pointer, its
top left, as before.
|
| |
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
| |
The map's dots and the console coloured six kinds of message; PROBE,
PRUNE/GRAFT, NEIGHBOUR and REFUSALS were drawn in the text colour and
missing from the legend. Each kind (`traffic`'s counters) now has a
colour, and the legend names them all, saying which messages a kind
stands for where its name doesn't.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The user's name, top right, opens a menu:
* "credentials…": the user's keys (GET /auth/keys), one added with its
.pub line or removed but the last (/auth/keys/{id}); under a strict
policy, how to sign them anew (continuum-signed key-add, key-remove);
and where its password is: each node's --htpasswd, its operator's.
* "log out": this browser's session ends. Where the page got in by
what it can't end (the browser's password, the proxy's), it is greyed
(aria-disabled, not disabled, so a click still lands), its tooltip
saying how to log out, and a click says so in a dialog.
The log out button and the text beside the name from 1165 go into it.
Checked in Chromium against stubbed endpoints: the menu, both log out
cases, the credentials dialog, no errors, nothing the content policy
refuses.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
The dashboard had no way to log out; only the login page did.
It now shows the user beside the connection, and "log out" where the
page got in by its session: that ends this browser's session (DELETE
/auth/session), other sessions of the user going on, and opens the
login page. Where it got in by what it can't end, the browser's
password or the proxy's, it says so instead (the token answer now
names `login`, how the user proved it). After logging out, a page
still let in by the browser's password says so too.
A list of a user's sessions, to end one by one, is in future-work.md.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Opening a dashboard through its login page showed a 401 on /auth/login
and the browser's password prompt. The page asked for its token by the
browser's password first (POST /auth/login), with none to send, and the
401 carried a Basic challenge, which a browser answers with its prompt.
The page now asks by its session first (POST /auth/session), then by
the browser's password or the proxy's. And a 401 to a page's own
requests (Sec-Fetch-Dest: empty: fetch, EventSource) carries no
challenge: a page has its login of its own. Navigations and clients
without the header (curl) are still challenged.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Review finding 8, second part: opening another member's dashboard
handed it a token reading everything (READ_ALL). A member writes its
own dashboard URL, so a malicious one received a read-everything token
under the operator's identity: /admin/config, /admin/changes,
/admin/log and the rest, on every node the user reaches.
The token handed over now reads the member list only (READ_MEMBERS, as
sent to subscribed nodes), which is enough to draw the map; the user
logs in at the other dashboard to see more. With 1151 it lasts five
minutes and isn't refreshed past them.
|
| |
|
|
| |
Flashes are drawn beneath the nodes' labels.
|
| |
|
|
|
| |
messages: off / dots / flash: a message expands at its own speed and
leaves an afterglow fading out in the direction it went.
|
| |
|
|
|
|
|
|
| |
Opening another member's dashboard passes a token reading only, for
five minutes, in the URL's fragment; its login page begins a session
with it. Tokens may be asked for with a shorter lifetime.
Auth plan M3 (hand-off).
|
| |
|
|
|
|
|
|
|
| |
With signatures required, a member whose own change broke a rule is
refused as a whole: sent nothing, shown in /peers (refused), in stats
(sync.refusedPeers) and on the dashboard, until a change of its is
taken again or the epoch changes.
Auth plan M6 (diverged standing).
|
| |
|
|
|
|
|
|
| |
The cookie lets in GET, HEAD and the session's own requests; anything
changing something brings the token itself, which no page of another
site or port can make a browser send. The dashboard's writes send it.
Auth plan M3 (browsers).
|
| |
|
|
|
|
|
|
|
| |
GET /login without authentication: log in by password or with a token
(from a login by key). POST/DELETE /auth/session keep the token as an
HttpOnly, SameSite=Strict cookie; the dashboard sends a browser
without a user there and renews the session while open.
Auth plan M2/M3 (browsers).
|
| |
|
|
|
|
|
| |
Subscribed nodes' data merged into the state as the page patches it,
not overwritten by the next patch.
Auth plan M3 (fix).
|
| |
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
| |
The page compares the clocks the reports on its map carry: this
node's peers', and those of the nodes it subscribes to; the node no
longer gathers them from LINKS (api/dashboard/clocks.rs goes).
Auth plan M3 (preparing LINKS' removal).
|
| |
|
|
|
|
|
| |
Each tick's messages of a subscribed node, listed in the console with
this node's own, from its own stream.
Auth plan M3 (live data from every reachable node).
|
| |
|
|
|
|
|
| |
The page reads a subscribed node's message counts from it too, and
draws them as dots on the map, as this node's own.
Auth plan M3 (live data from every reachable node).
|
| |
|
|
|
|
|
|
| |
Double-click a node to subscribe: the page reads its own links from it
directly with a narrowed token (reading the member list only), drawn
over what this node knows; kept per swarm in the browser.
Auth plan M3.
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|