diff options
Diffstat (limited to 'docs/dashboard.md')
| -rw-r--r-- | docs/dashboard.md | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/docs/dashboard.md b/docs/dashboard.md new file mode 100644 index 0000000..b7e3069 --- /dev/null +++ b/docs/dashboard.md @@ -0,0 +1,324 @@ +# The dashboard + +A page showing one node's view of the swarm, live: a map of the members and the links +between them, the node's peers, its figures, and the document, which it can also edit. +This page is about using it; how it works underneath (events, LINKS, what is sent +when) is at the end, [Underneath](#underneath). + +## Opening it + +The dashboard is in builds with the `dashboard` cargo feature: + +```sh +cargo build --release --features mycelium,dashboard +``` + +It is at `/admin/dashboard` on the node's HTTP API, by default +`http://127.0.0.1:3000/admin/dashboard`, and asks for a user from `--htpasswd`, as the +rest of the API does. The API listens on localhost unless `--bind-addr` says otherwise; +to look at a node on another machine, forward the port (`ssh -L 3000:localhost:3000 +<machine>`) or put a TLS proxy in front ([`trust.md`](trust.md)). + +Every node has its own dashboard, and shows the swarm as that node sees it: what it +knows of the others, what they report to it, and its own figures. + +## The page + +At the top, the node's alias and node ID; in orange, when this node's clock is off +from its peers' ([`measurements.md`](measurements.md), Clocks), by how much; and the connection's status: + +| Status | Meaning | +|---|---| +| `connecting…` | Opening the page's event stream. | +| `live` | Up to date. The dot beside it breathes with every update from the node. | +| `reconnecting…` | The stream broke; the browser reconnects by itself. | +| `disconnected; retrying…` | The node answered with an error (it may be up but not serving the page yet); again in 5 s. | +| `node stopped; reconnecting…` | The node said it is stopping; again in 3 s, until it is back. | +| `paused while hidden` | The page was hidden (another tab, a minimised window) for 30 s: it closed its stream, so the node does no work for it. Shown again, it reconnects. | +| `new version; reloading…` | The node now serves another version of the page; it is loaded. | + +A click on the status refreshes everything at once while live, and reconnects at once +otherwise. While not live, the page is dimmed and shows what it had last: ages stop +counting. + +Below, five boxes: the swarm's map on the left; the peers, the figures, the document +and the console on the right. In a narrow window they are stacked. + +Dragging the space between two boxes resizes them: between the map and the right +column, below the peers, between the figures and the document, and above the +console. Dragged all the way to the right, the right column goes, and the map takes +the whole width; drag the space right of the map back to bring it again. A +double-click there sets that space back to its default. The sizes are kept in the +browser, for the next time the page is opened. + +The `▾` before a box's title collapses it to its title (`▸` opens it again). A +collapsed box isn't drawn at all, so it costs the browser nothing while the node +updates; opened, it shows what's current. The open boxes take the room the collapsed +ones leave: in the right column they share it as before (the peers take all of it +when they're the only one open); the map collapsed, the right column takes the width. +The right column all collapsed keeps its width: its titles stay at the top, with a +hint that dragging the space beside them to the right hides the column. Which boxes +are collapsed is kept in the browser too. + +## The map + +### Nodes + +Every member of the swarm, by alias (or a shortened node ID): + +* **This node**: filled blue, slightly larger. +* **Filled**: the member reports its links to this node. +* **Hollow**: it reports none: it doesn't answer this node's LINKS, or its last answer + is more than 90 s old (it is unreachable from here, stopped, or doesn't yet take this + node for a member). +* **Red outline**: this node holds it dormant (it has been silent, or said farewell). +* **Dashed orange ring**: its clock is off the swarm's: it differs from the median of + the members' clocks by more than `--clock-skew-warn`, beyond what the measurements + allow. The members report their peers' clocks with their links, so this works for + members this node doesn't reach itself; it needs three members compared. + +### Links + +Two kinds, drawn over each other: + +* **Intended** (dashed grey): the neighbours the member list gives each member, as this + node computes them. Shown only where no measured link backs it. +* **Measured** (coloured): the links members report. A link is drawn if either end + reports it. + +A measured link is coloured by transport (blue Mycelium, purple libp2p, teal Tor). The +transport in use is the thick line in the middle, labelled with its round trip in +milliseconds; the other transports the peers can reach each other on are thin lines +beside it, from node to node, bowing out a little (furthest at the middle), with their +round trips smaller. The round trips are drawn near the end that +measured them: of the two ends' reports, the one drawn is the one with a round trip, +else the fresher. Then: + +* **Solid**: direct. +* **Dashed**: through a relay. +* **Dotted**: one end reports no links: what is drawn may be out of date. +* **Narrow, over a red band**: either end holds the other dormant, or the path in use + is down (no path to the peer answers; `/peers` shows its connection as `null`). + +Messages this node exchanges run along its links as dots, from sender to receiver. +They come from the per-peer message counts, which arrive every 2 s: what was sent and +received since is spread over the next 2 s, at most 6 dots per link and direction, +shared out by message kind, each kind in its colour (named in the legend; the console +colours them alike). A dot is as large as its kind's messages were on average +(by the size's logarithm: a ping small, a large sync message large); the slider right +after their legend makes all dots larger or smaller, kept in the browser. A picture +of the traffic, not a record of it; links between other members show none. +`messages: on` after the slider hides them (`messages: off` shows them again; the +legend and the slider greyed meanwhile); the choice is kept in the browser. + +A label on the map that changes, a round trip or a node's name, breathes once: it +glows around its new text and fades (not where the system asks for reduced motion). + +`links: live` beside the box's title says the members tell this node of their links' +changes as they happen. Clicked, it's `links: every 30 s`: the members are only asked +every 30 s, which costs the swarm fewer messages and leaves the map up to 30 s behind +(this node's own links stay current). It's the node's choice, for every page open on +it, until clicked again or the node restarts. + +The legend above the map names the transports that appear; `hide legend` beside the +box's title gives its room to the map (`show legend` brings it back). + +`layout: static`, beside it, is the default: nodes move only when the map is laid out +(below). A click switches to `layout: dynamic`: each measured link is as +long as its round trip is slow, against the median link's, by the square root, from +0.4 to 2.5 times the usual length; links without a round trip keep the usual length. +In either layout, a link only intended (dashed grey) gives way to the measured ones: it +pulls its ends together only a little when stretched, and doesn't push them apart. As +round trips change, the map moves to the new lengths smoothly. Clicked again, it's +static, the nodes staying where they are. The choice is kept in the browser. + +Hovering over a node or a link highlights it and shows a tooltip: for a link, each of +its lines with transport, direct or relayed, round trip, and how long ago it was +measured. + +### Moving around + +* **Wheel**, or pinching on a touch screen: zoom, around the pointer (or between the + fingers, the map moving with them). +* **Drag beside the nodes, or along a link**: pan. +* **Two fingers turning**, on a touch screen: the map turns with them; labels stay + upright. +* **Drag a node**: move it. On a dynamic map, the others follow at once, and once + let go of, it moves back among them; on a static map it stays where it is put until + the map is laid out again. +* **Double-click a node**: pin it where it is (a dot in its middle): the layout leaves + it there. A double-click whose second press drags the node pins it where it's + dropped. Double-clicked again, it's let go, and moves to its place among the others + (on a static map, only it moves). + +The map is laid out when the page opens, and again when the window or the box is +resized. A member that comes later is placed among the others, which stay where they +are, as they do when links appear or disappear; pinned nodes always stay put. The view and pins last as long as +the page is open. + +## Focus + +What the Figures box shows is what is in focus: + +* **Nothing** (the default): this node's figures. +* **A node** (click it on the map, or its row in Peers): this node's entry for that + peer (as `/peers` has it), and the links that member reports. Ringed on the map, + highlighted in Peers. A second click on its row, a click beside the nodes or a click + on this node goes back. +* **A link** (click it): whether the member list intends it, each end's report of it + (transport, relayed, round trip, dormant, its other transports, and how old the + report is), and, where this node is an end, the link as this node has it in `/peers`: + where messages go, its paths (each with its connection and round trip), and the + addresses known of the other end. Both ends are ringed. + +The focus is in the page's address (`#<node ID>`, or the two IDs of a link), so a +reload keeps it and a link to the page can name it. + +## Peers + +Every peer this node knows, with: + +| Column | Meaning | +|---|---| +| peer | Alias, or a shortened node ID (the full one in the tooltip). | +| status | `active`, `dormant` (for how long: in the tooltip) or `unknown` (not heard from since this node started). | +| seen | How long ago this node last heard from it. | +| via | The address this node sends to it at, and whether `direct` or `relayed`. | +| rtt | The round trip on that path. | +| clock | How far its clock is ahead of this node's (behind: `−`), give or take (in the tooltip); in orange beyond `--clock-skew-warn`, for sure ([`measurements.md`](measurements.md), Clocks). | +| neighbour | Whether this node keeps contact with it (HELLOs, keepalives, sync); others it only answers. | +| same epoch | Whether it is on this node's epoch ([`remakes.md`](remakes.md)); blank while unknown. | + +The box is as tall as its rows, up to eight; then it scrolls. + +## Figures + +The figures of what is in focus, as a tree: this node's `/admin/stats`, or a peer's +`/peers` entry and links. Branches stay as opened across updates, and ages count on +between them. What each figure means: [`measurements.md`](measurements.md). + +## Document + +The swarm's document, as this node holds it, its first level open. A branch is read +when opened, and read again when the document changes there; what changed or came +breathes once, as the map's labels do: its row glows and fades. + +Editing writes to the document, as the user the page was opened as, and the write goes +to the whole swarm like any other: + +* **Double-click an entry** to edit it: a string as text, anything else as JSON, a map + or list whole. Saved, it is shown open. The red `Delete` beside `Save` deletes the + entry instead, on a second click (`Delete: sure?`) from half a second to 3 s later: a + double-click doesn't delete. +* **`+` on a map's row** adds a key and its value. +* **`+` on a list's row** inserts an item at an index (`-1`: at the end); the items + from there on move up. + +Protocol data (`_continuum`) is written only by users in `--protocol-writers`, and epoch +metadata and the swarm ID by nobody ([`trust.md`](trust.md)). A key containing `/`, or an empty +key, is listed but can't be opened. + +## Console + +The messages this node exchanged with its peers, one by one: when (on the 24-hour +clock, to the millisecond), which way (`→` sent, `←` received, `↛` refused: the +transport wouldn't send it), with whom, the type (`HELLO`, `SYNC`, `STATUS_REPLY`, +`LINKS`, …, in its kind's colour, as on the map) and the size. The latest 500, the +newest at the bottom; scrolled along while at the bottom. They arrive with every update +(every 2 s). The PINGs the paths layer sends on its own, and the PONGs it answers +PINGs with, are listed too: under the node at the address they went to, where it is +known, else under the address. Not listed: WHO and ME, which stay in the layers +below the node (they are counted, [`measurements.md`](measurements.md), Traffic). + +A click on a message shows it in the figures box: when, with whom, its type and size, +and the message written out (its bytes as sizes). A click on it again, or on anything on +the map or in the peers, goes back. + +Pointing at a message marks the others of its exchange (a lighter grey, and a bar on +their left); while one is shown, its exchange stays marked. An exchange is a PING and +its PONG (by their nonce, wherever they were listed), a HELLO and its replies (by the +run of the node that sent the HELLO), a LINKS and its replies (by their nonce), a blob's +requests and answers, from whichever peers (by the blob), and a delta offered in a SYNC +with the blob it is fetched as. A STATUS and its reply carry nothing that ties them, nor +does an ordinary SYNC. + +## While you read + +Updates don't pull things from under you: while text is selected in a box, that box +waits, and while an entry is edited, the branch it is in waits; both catch up once you +are done. The map isn't redrawn under the pointer: what you point at stays put. + +## When something looks odd + +* **The status stays at `connecting…`, or the page doesn't load**: the node isn't + built with `dashboard` (404), or the user isn't in `--htpasswd` (401). +* **A hollow node**: no links report from it. If it is also dormant here, it is + probably down or unreachable; if not, it may not take this node for a member yet + (it hasn't registered it), or its answers don't arrive. +* **Only dashed grey between two nodes**: the member list wants them as neighbours, but + neither reports a link to the other: they haven't reached each other (yet). +* **A dotted link**: one end has stopped reporting; the link is as it was last + reported. +* **A red band**: one end holds the other dormant. If the other end still reports the + link, the trouble is likely in one direction only. + +## Underneath + +The page is `GET /admin/dashboard`, authenticated as the rest of `/admin`. It is kept up +to date through Server-Sent Events (`/admin/dashboard/events`); +`/admin/dashboard/state` has the latest state at once. + +**Events.** One producer per node serves every client, and runs only while one is +connected (or for a minute after a `/state` request); a page hidden for 30 s +disconnects until it is shown again. Every 2 s it builds the state; when anything but +ages changed, it sends every client a JSON merge patch onto the state before. Nothing +changed, nothing is sent. Ages are as of the event, and the page counts them on. A click +on the status refreshes at once (`POST /admin/dashboard/refresh`). When the node stops, +every client gets an `offline` event and its stream ends, so stopping doesn't wait for +them; the page reconnects once the node is back, and reloads if the node now serves +another version of it (the state's `page`). + +**The map's graphs.** The intended one is the neighbours the member list gives every +member (`swarm::topology`), as this node computes them. The measured one is every +member's links as it reports them in reply to a LINKS (sent to every member, at most +512, while the producer runs). The LINKS subscribes: the member replies again whenever +its links change (a peer, a transport, relayed, dormant, or a round trip moved by a +fifth; it looks every 2 s), for 90 s, renewed every 30 s (each renewal answered), and +ended when the producer stops; a node that is gone stops it by not renewing. A reply +older than 90 s is left out. A member keeps 64 subscribers at most. With `links: every +30 s` (`POST /admin/dashboard/links?live=false`; `true` back), the subscriptions end +and the members are only asked every 30 s, for every page open on this node, until +asked otherwise or the node restarts. + +The links a member reports: its neighbours and the peers it holds dormant (a neighbour +that goes dormant is one no more, but stays listed until it is heard from again), by +which transport, whether only through a relay, the round trip (as the paths layer +measures it), whether it holds the peer dormant or the path is down, the peer's other +transports with a path up (each with its fastest path's round trip, and whether it goes +only through a relay), and the peer's clock against the member's ([`measurements.md`](measurements.md), +Per peer, `clock`). A member answers a LINKS from members only, at most every 5 s, and +every build answers, with or without the feature (`swarm::links`). + +**Clocks.** The clocks in the links chain every member's clock to this node's, along +the narrowest bounds, and the map takes each against their median: a member more than +`--clock-skew-warn` off it, beyond the chained bound, is ringed. With fewer than three +members compared, none is. + +**Messages on the map** come from the per-peer message counts in the state (`/peers`, +`traffic`), not from messages of their own. **The console's** come from a log the node +keeps only while the producer runs (`traffic::MessageLog`, the latest 1000), sent as a +`messages` event every 2 s with those since the last. + +**The document** is read through the store API, a level at a time as it is opened +(`GET /store?shallow=true`, then `/store/<path>?shallow=true` for each map opened, and +`/store/<path>` for a long value). Where it changes, the node says so (a `document` +event with the paths changed, at most every 2 s), and the page reads again what is open +there. A key with `/` in it (or an empty key) can't be opened: store paths are split at +`/`. + +Edits go through the store API too, as the user the page was opened as: `PUT +/store/<path>` (a text saved becomes a string; the root isn't written whole), a list's +items each on its own (`PUT /store/<path>/<index>`), a new key with `PUT +/store/<path>/<key>`, an inserted item with `POST /store/<path>/<index>` (`-1` the end; +an index past the end is refused), and `DELETE /store/<path>`. The dashboard itself +writes nothing to the document: only edits do. |
