netviz GitHub

netviz web

netviz web opens an inventory in the browser and draws it as you edit it, with an info box on every node and link. It has two faces, and which one you get is decided by what you point it at:

Synopsis

netviz [GLOBAL OPTIONS] web [OPTIONS] [SOURCE]

The netviz web interface: the YAML document stream on the left, the rendered layer-2 diagram on the right, and the info box open on a switch showing its interfaces, addresses, VLANs and links

Hovering sw-home in examples/home-lab: every port, its addresses and VLAN mode, and what each one is cabled to.

The editing session

netviz web ./inventory                # read-only: browse the tree and its diagram
netviz web ./inventory --write        # read-write: save, undo and redo from the page

The window is three panes. On the left is the inventory: every file the loader would read, grouped by namespace, with the documents each one declares listed under it by kind and name. In the middle is the file being edited and the problems found in the tree. On the right is the diagram.

The panes are wired to each other, which is the point of the command:

Every file's state is shown as it is, not as it would be convenient. A file you have typed into is unsaved. A file that changed on disk while you had unsaved edits in it is conflict, and netviz will not resolve that for you: it says so and leaves both versions alone until you decide. A file that was deleted underneath you says deleted on disk.

Three consequences of that, because they are the ones people meet:

A conflict is resolved by saving again, which is what the message offers: the second save is a write over the version you were told about, not over whatever happens to be there when it lands. If the file moves a third time in between, it is refused again — the precondition is never dropped, only re-aimed.

The guided tour

The first time you open a session, the page offers a sixty-second tour. Take it with Enter, decline it with Esc — declining is remembered, and Ctrl-KTake the guided tour runs it again whenever you want it.

It creates a device, cables it to one of yours, moves its document to another file, opens the changes drawer on the YAML all three wrote, and undoes the lot. The point is the mapping: every shape on the canvas is a document, so a shape cannot appear without a file appearing, and undoing the gesture brings the bytes back.

It never touches your inventory. Starting the tour copies the tree — only the documents the loader reads, plus netviz.toml — into a temporary directory and points the page at a second session over the copy. That session is always writable, so a read-only netviz web DIR can take the tour too; the panel names both directories so there is no doubt which one is being written. Finishing, skipping or closing the tab deletes the copy, and so does stopping the server.

Any step may be refused — it is the real write path, and it is allowed to say no. The tour shows the refusal and carries on to the next card.

Writing

--write is off by default, and it is refused unless the server is bound to loopback — publishing an endpoint that changes your inventory is not something a flag should let you do by accident. Without it every read works and every write answers 403.

With it:

Every write goes through netviz.edit — the same typed, reversible, comment-preserving operations netviz edit applies from the command line. The server constructs no YAML of its own, so untouched lines are not rewritten and a diff of an edit shows the edit.

Reviewing what you changed

The Changes button opens a drawer listing every gesture the session made, newest first. One entry per gesture rather than per operation: deleting a switch is one line, not the five operations it became. Each carries

A revert is a new change, not a rewind. It applies the gesture's own inverse as a fresh edit, which is itself logged and itself undoable, so reverting the third of ten gestures leaves the other nine alone. When one of those nine depended on what the third one did, the revert is refused with the reason and nothing is written — which is the honest outcome, and the one an undo stack cannot give you.

Opening the drawer also repaints the canvas as a diff: added elements green, removed ones red and dashed but still in place, changed ones amber with a badge naming the fields that moved, everything untouched faded. The since menu chooses what it is drawn against —

this session started the tree as this page first saw it. The default: "what have I done this afternoon" is the question, and neither git nor the undo stack answers it.
git HEAD HEAD as the inventory root looks in it. Offered only when the root is in a repository, because an option that always fails is not an option.

It is the same overlay netviz diff draws, from the same changeset — the drawer and the diagram are two views of one answer.

Copy commands hands the whole session over as a list of netviz edit invocations, in the order they happened, for a pull-request description or somebody else's terminal. The rendering is never lossy; see docs/editing.md.

The history timeline

The inventory is a folder of YAML in a repository, so its whole history is renderable. History, above the canvas, opens a scrubber along the bottom of it, over the commits netviz log lists — and the diagram becomes the diff each one carries against its parent as you step:

 ▶  ◀◀  ────────────●───────  ▶▶   Now   ×
 e58b3c3a0  Add a lab switch   Scrubber · 2026-08-14   1 device added

Oldest on the left. Beside the control is the commit itself — the abbreviated hash, its subject, its author and date, and the one-line summary of what it did to the network — because a picture with a hash under it places nothing in time.

Control
the slider Any commit in the range. It repaints as it is dragged; ground already covered comes back out of the cache.
◀◀ / ▶▶ One commit older or newer. Alt-Left and Alt-Right.
Play: step forward by itself, about a frame a second, until the newest revision or until one that will not load. Alt-P.
Now Leave the history and draw the working tree again. Escape does the same.

Each frame is arranged the way that revision arranged it. Both sides of the diff are read out of the commit, layout document included, so a diagram that was hand-placed in March is still hand-placed when you scrub back to March — and one placed since is not retro-fitted onto a picture that never had it.

The history and the changes drawer are two overlays on one canvas, so opening either puts the other away. The legend means the same four things in both.

What it will not pretend

What it costs

A frame is one inventory read, one changeset and one Graphviz layout — the same work drawing the tree at all costs, plus the read and the diff. Two things keep that interactive:

tools/bench_history.py measures it. On the 1056-device benchmark tree, where one plain render of the working tree is about 1.5 s, a step is about 1.5× that and a frame already drawn comes back in about 13 ms.

Reconciliation

The session does not own the files. watchfiles watches the folder exactly as netviz watch does, so an edit made in $EDITOR, a git checkout, or a second netviz process bumps the tree revision — and the page is told, over a server-sent-events stream, the moment it happens. If the watch cannot start, the command says so rather than leaving a page that is quietly stale.

The event says what moved, which is what makes the page's response proportionate to the change:

The stream is an optimisation, and the page works without it. A proxy that buffers responses, a browser without EventSource, a stream that will not open: any of them drops the page back to polling /api/state once a second, which replays the very same events out of the server's ring buffer. The indicator above the file list says which of the two you are on. Nothing is writable through the stream and no write depends on having read one.

More than one client

A session is shared — two tabs, or two people on the same machine — and it says so rather than leaving them to collide:

All of that is advisory. It blocks nothing: the row still opens, the file still saves, and presence expires by itself if a tab goes away without saying so. The only things that can refuse a write are the ones that are checks on the tree itself — the content hash of a whole-file save and the tree revision of an operation batch. A soft lock built on a heartbeat would be a way to lock an inventory by closing a laptop lid.

So the conflict story is unchanged by having company: two clients saving the same file gives the second a 409 carrying what is really on disk; a save racing an $EDITOR write gives the same; and an undo issued in one tab rewrites the files and moves the buttons in the other, which is what a server-side history means.

The API

The page is a client of a small JSON API, and so can anything else be. All of it is on loopback and none of the write routes exist unless --write was given.

Route What it answers
GET /api/bindings Every command the page has, its section, its keys, what it needs and what it does — plus the element kinds this build knows. Answered in both faces; it is netviz.web.bindings, which is also what the table below is generated from.
GET /api/state The tree revision, whether this session writes, the undo/redo depth, and who else is connected. ?since=<id> adds the events published after that id — the polling client's half of the push channel. ?client=<id> keeps that client's presence alive.
GET /api/events A text/event-stream of tree-changed, file-changed, history-changed, disk-changed, presence, opening with hello and beating every 15 s. Resumes from Last-Event-ID; a resume point older than the ring buffer opens with resync, meaning refetch.
POST /api/presence {"client": …, "selection": [ … ], "editing": [ … ]} — what this client is looking at and has unsaved edits in; answers with everybody. {"leaving": true} drops it at once. Advisory, and the one route here that a read-only session still accepts, because it writes nothing.
GET /api/tree Every file, its content hash, its documents, and each document's kind, name, address and line. ?path=a.yaml&path=b.yaml answers for those files only, with partial: true and a missing list; ?diagnostics=0 leaves out the findings, which cost a validation of the whole tree either way.
GET /api/graph?view=l2 The resolved graph as an embeddable SVG, its info-box records, its problems, its stored geometry and its annotations — the same payload netviz render -f json publishes, which is how the canvas knows where a note or a zone is, an arranged drawing having painted the zone into the background with no id on it. &annotations=0 leaves both the drawing and the payload without them. graphHash fingerprints the drawing; passing it back as ?known= answers unchanged: true with no SVG when this revision would draw the same picture, having skipped the layout.
GET /api/file/<path> One file's text and the hash a write of it must quote.
PUT /api/file/<path> That file back: {"text": …, "hash": …}. A stale hash is 409; a new error is 422, listing them; "force": true overrides the second, never the first.
POST /api/ops {"revision": …, "ops": [ … ]} — a batch of edit operations, applied atomically. Answers with the applied operations, their inverses, the files changed and the tree's diagnostics.
POST /api/undo, POST /api/redo Move the server-side history one step.
GET /api/changes The session's log — one entry per gesture, with its hunk, the files and addresses it touched, and the netviz edit lines that replay it — plus the whole session as one command list and the baselines this tree can be diffed against.
GET /api/diff?against=session The same payload /api/graph answers, drawn as a diff, with diff holding the marks per node and edge and diff.changeset the whole plan. against=git compares with HEAD.
GET /api/history The commits that changed this inventory, newest first, each with its hash, parents, author, date, subject and the hash of the inventory tree at it. bound is the ceiling, total how many there are and truncated whether the list is the newest of more. ?limit= asks for fewer.
GET /api/frame?rev=<commit> One of them, drawn as the diff against its parent: the /api/diff payload plus the commit's own facts and the one-line summary of what it did. ?known= works as it does on /api/graph. A revision that will not load answers 200 with status: failed and the reason — it is a fact about the history, not a bad request.
POST /api/revert {"id": 3, "revision": …} — put one logged gesture back.
POST /api/fix {"rule": "W138", "message": …, "fix": "prune", "revision": …} — apply the mechanical repair for one diagnostic, as one gesture. The finding is named by rule and message, not by its place in the list, so a stale list is refused rather than misapplied. fix picks between the repairs a rule offers more than one of.

<path> is relative to the inventory root and is checked, not normalised: an absolute path, a .., a component the loader skips and a suffix that is not YAML are each refused by name. No other request ever becomes a file name.

Right-clicking

Right-click the diagram and you get the handful of commands that make sense where you clicked. On an element, on a link, and on the paper between them — three menus, listed below.

Two things it is not. It is not a second set of gestures: every row runs a command from the table under that command's own id, so the menu, the palette and the key are three ways to the same one implementation. And it is not a full list: the palette is one keystroke away with all fifty in it, and a menu long enough to need reading has stopped being quicker than typing. All commands… is the last row of the canvas menu for exactly that.

What it does add is a target. Right-clicking a shape focuses it first, so Delete it deletes the one you pointed at rather than the one the keyboard was left on, and the menu's heading is the element's address so there is no doubt which that is. Every row also prints its own shortcut, the same way a palette row does: use the menu for a week and you will not need it.

A row that cannot run now is greyed, with the reason on itthis session is read-only; restart it with --write — rather than missing. Escape closes it, the arrow keys walk it, and Shift-F10 or the menu key opens it on whatever the diagram has focused, because a menu only a mouse can open is a set of commands a screen-reader user does not have.

Right-clicking a bend on a link still removes that bend, and shows no menu: the handle is a control of its own, and burying its one gesture two rows deep would be a loss. Right-clicking anywhere off the canvas is the browser's own menu, untouched.

Right-clicking a multi-selection

Offers Same as Needs
Align left Align left — palette only --write
Align centres Align centres — palette only --write
Align right Align right — palette only --write
Align top Align top — palette only --write
Align middles Align middles — palette only --write
Align bottom Align bottom — palette only --write
Distribute horizontally Distribute horizontally — palette only --write
Distribute vertically Distribute vertically — palette only --write
Snap to the grid Snap to the grid — palette only --write
Copy Copy the selection — Ctrl-C a folder
Cut Cut the selection — Ctrl-X --write
Duplicate Duplicate the selection — Ctrl-D --write
Set a field on all of them… Set a field… — e --write
Remove a field from all of them… Remove a field… — palette only --write
Move their documents… Move to another file… — palette only --write
Move into a namespace… Move into a namespace… — palette only --write
Clear the selection Clear the selection — Ctrl-Shift-A
Delete all of them Delete the selection — Delete --write

Right-clicking an element

Offers Same as Needs
Inspect it Open the inspector — Enter a focused element
Pin the inspector Pin the inspector — Space a focused element
Cable it to… Connect this element… — c --write
Add an interface… Add an interface… — i --write
Note about it… Add a note to the diagram… — Shift-N --write
Copy Copy the selection — Ctrl-C a folder
Cut Cut the selection — Ctrl-X --write
Duplicate Duplicate the selection — Ctrl-D --write
Rename it… Rename the focused element… — F2 --write
Change how it looks… Restyle the selection — palette only --write
Set a field… Set a field… — e --write
Remove a field… Remove a field… — palette only --write
Move its document… Move to another file… — palette only --write
Delete it Delete the selection — Delete --write

Right-clicking a link

Offers Same as Needs
Inspect it Open the inspector — Enter a focused element
Add a bend Add a bend to the focused link — b --write
Straighten it Straighten the focused link — Shift-B --write
Route it… Change how the link is routed… — r --write
Pin the computed route Pin the route the renderer worked out — Shift-R --write
Put the label back on the line Put the link's label back on the line — palette only --write
Note about it… Add a note to the diagram… — Shift-N --write
Change how it looks… Restyle the selection — palette only --write
Set a field… Set a field… — e --write
Disconnect it Delete the selection — Delete --write

Right-clicking a note, an area or a legend

Offers Same as Needs
Edit the text… Edit the note's text… — Shift-E --write
Delete it Delete the selection — Delete --write

Right-clicking a namespace box

Offers Same as Needs
Fold or unfold it Fold or unfold this namespace — f
New in it ▸ (one row per element kind) Create an element… — n --write
Paste into it Paste — Ctrl-V --write
Move the selection into it… Move into a namespace… — palette only --write
New namespace inside it… New namespace… — palette only --write

Right-clicking the canvas

Offers Same as Needs
New ▸ (one row per element kind) Create an element… — n --write
New namespace… New namespace… — palette only --write
New note Add a note to the diagram… — Shift-N --write
Paste here Paste — Ctrl-V --write
Selection tool Selection tool — v
Pan tool Pan tool — h
Show another layer… Switch layer… — palette only
Toggle icons Toggle icons — Alt-K
Fit the diagram Fit the diagram — 0
Undo Undo — Ctrl-Z --write
Redo Redo — Ctrl-Shift-Z --write
Show what changed Changes drawer — Ctrl-B a folder
All commands… Command palette — Ctrl-K

New ▸ opens one row per element kind, and picking one opens the create form with that answer already filled in — the same form n opens, writing the same document through the same netviz edit create.

The keyboard

Everything this page does is reachable without a pointer, and the page says so out loud rather than making you find out. There are three ways in, and they are three views of one table:

A fourth, for the pointer: right-clicking the diagram offers the few of these that fit what is under the cursor, each row printing the chord that also runs it.

A chord written Ctrl-… means the platform's command modifier: ⌘ on a Mac. A single letter is a canvas gesture and fires only while the diagram has focus — n creates a device there and types an n in the YAML pane, which is the distinction the Where column makes. v and h are the two exceptions: which pointer tool is up is a question about the mouse, not about what the keyboard last landed on, so they answer from anywhere but a text field.

The pointer tool

A drag across the paper means two things and both are right: to one person it is a region being selected, to the next it is the paper being moved. Nothing in the gesture tells them apart, so — as in every drawing tool — it is a mode, and the toolbar above the canvas says which one:

Tool Key A left-drag
Select (default) v Draws a rubber band on the paper; moves a shape, a note, a namespace frame or a bend that can be moved; pans when it started on something none of those claim.
Pan h Moves the diagram, wherever it started. Nothing in the drawing can be nudged out of place by a press that was meant to scroll.

The cursor is the other half of the answer: an arrow under the selection tool, an open hand under the pan tool, closed while the pan is happening. It is the hand everywhere inside the canvas — over a switch and over a bend handle as much as over the paper — because that is exactly where the two tools differ.

Neither is ever more than a modifier away, which is what keeps the mode from being a trap:

Right-clicking the canvas offers both tools, because the press that wanted the other one has already happened over the diagram and the toolbar is at the far corner of it.

Driving the diagram

Tab reaches the canvas like any other control; the diagram is one stop on the page's tab order and never a trap. From there the arrow keys walk it, preferring the elements the focused one is linked to, so a path is followed rather than a grid swept. Enter opens the inspector — and, in a session, the document that declares the element, at its line. n, c and Delete are the create, connect and delete gestures; each opens a small prompt whose element field is already filled in with whatever is focused, so the same command works from the palette with nothing focused at all.

Two rings, deliberately different: focus is a solid violet halo — where the keyboard is — and selection is a long dash, with somebody else's selection a short one. Three patterns, not three shades, so they are told apart without colour.

Delete takes what cannot survive

On the command line, deleting a cabled switch is refused and you are told to pass --cascade. On a canvas that would be theatre: somebody who dragged a box to the bin has already said what they want, and a gesture that stops to ask for a flag it will then be given is a gesture with one extra step in it. The editor always cascades. What it owes you instead is the truth about what that costs, before the fact and once:

Delete 1 element?

switches/sw-home

These cannot survive it and go too: cables/cbl-rtr-sw — one end of it is switches/sw-home:port1 …

8 layout entries that placed them are dropped.

This is one change: Ctrl-Z puts all of it back.

That list is not read off the picture. GET /api/cascade asks netviz.edit for the set it will actually remove, so it includes the things a diagram cannot show you: the tunnel three levels up that runs over a cable that runs to the switch, the note anchored to it in a view you are not looking at, the group that lists it as a member, and the coordinates in every kind: layout document that placed any of it. A client-side guess would get the cables right and everything else wrong.

A delete that takes nothing but what you named does not ask at all — a confirmation that always appears is one that stops being read. Deleting an uncabled spare switch, a note, an area or a legend is immediate.

And the whole of it is one entry in the undo stack: the elements, the annotations and the geometry go together, and one Ctrl-Z brings them back byte for byte, comments included.

Selecting several things

Most of the editor acts on a selection, and a selection is a set:

Gesture What it does
drag on the paper A rubber band. Everything it encloses is selected; hold Shift to add to what was already there rather than replace it. A drag that starts on a shape moves it, or pans when it cannot be moved. Needs the selection tool, which is the default — hold Alt to band under the pan tool.
Shift- or Ctrl-click Adds one element, or takes it back out. Works on the outline entries too.
Ctrl-A Everything the current view draws — including whatever is culled off screen. Only on the canvas: Ctrl-A in the YAML pane is still the text.
Shift-arrow Extends along the same neighbour search the arrow keys use, so a trunk and everything hanging off it is collected without a pointer.
Escape Clears it, before it closes anything else.

The selection is held as element addresses, not as shapes, which is what lets it survive a redraw — a save, an undo, somebody else's edit — and lets a culled element half a screen away stay in it. The ring is drawn for the ones on screen; the count is on the diagram outline, where a screen reader hears "8 elements, 3 links, 2 selected" and each selected entry as pressed.

With more than one thing selected, the gestures that can mean a set act on all of it, as one change:

Right-clicking inside a multi-selection opens the selection's own menu rather than the element's: the subject is the set, and "Rename it…" on eleven shapes would have to mean whichever one the pointer happened to be over.

A thousand devices

Above four hundred elements the canvas stops drawing all of them at once. Two things happen, and both are visible:

Only what is on screen is drawn. Everything more than half a screen outside the viewport keeps its place in the document and loses its contents until you pan back to it. The status line says so — drawing 140 of 2106 in view (pan, or Ctrl-K to find) — because a canvas that is quietly missing things is worse than a slow one. Nothing about reaching an element changes: the arrow keys, the outline, the command palette and find-in-diagram all work from the records rather than from the drawing, so selecting something on the far side of the diagram brings it back and pans to it.

Zoomed out, the labels come off. Below the scale at which a device name is a smudge, the names and the icons are dropped and each namespace grows a dashed frame with its name and how many elements are in it. Zoom back in and they return. The zoom range is the drawing's own, not a fixed multiple, so a label on a thousand-device diagram can always be reached.

The first layout of an inventory that size is a real Graphviz run and takes a second or two; the status line counts while it happens rather than sitting still. If a redraw after dragging a node feels slow, it is: a diagram with some positions stored has to be laid out twice, and netviz layout --write places the rest and takes the redraw to a fraction of it. The measured ceilings are in docs/follow-ups.md entry 20.

Routing a cable

A link is geometry as much as a node is (docs/schema.md §18), and once the view is arranged it can be routed here. Click a link to select it; double-click the line to drop a bend where you clicked; drag a bend to move it; drag the hollow midpoint handle to insert a bend and place it in one motion; right-click a bend to remove it. A label the inventory has already pinned gets a handle of its own, which slides it along the route and lifts it off.

Each of those is also a command, because a bend that can only be placed with a mouse is a bend somebody working from the keyboard cannot place at all: b adds one half way along the selected link, Shift-B straightens it, r sets its routing style — spline, orthogonal or straight, on this link alone — and the palette puts a moved label back on the line. On a view that is not arranged they refuse with the fix, netviz layout --write, rather than doing nothing: a diagram Graphviz is still routing has nowhere to keep a bend.

Nothing here is a browser-side model of the arrangement. Letting go of a handle posts one set-link-geometry operation (docs/editing.md), the server rewrites the kind: layout document through the same comment-preserving path netviz layout uses, and the canvas repaints from the render that follows — so what you see here is what netviz render draws, and the gesture is one entry in the changes drawer with a YAML hunk under it. The line under the cursor is drawn by a port of netviz's own router, which tests/test_browser.py runs against the Python it mirrors on every CI run, so a drag cannot land somewhere the render would not.

Writing on the diagram

The commentary of docs/schema.md §21 — a callout, a zone, a key — is edited here the way a cable's route is. Shift-N drops a note at the pointer and opens it for typing; right-clicking an element or a link and choosing Note about it… anchors the note to that instead, so it follows the device when the diagram is laid out again. A note is retyped by double-clicking it, or with Shift-E on a selected one: Ctrl-Enter or clicking away writes it, Escape leaves it alone. Dragging a note moves it, its corner resizes it, and a zone pinned to a rectangle is dragged by its outline and resized by its corners. Delete removes whichever is selected. Alt-N hides the lot, which changes no file: commentary is never topology, so it is a way of looking at the diagram rather than a way of changing it.

A zone drawn round its members has no box to move — it is wherever those devices are — so dragging it is refused with that sentence rather than quietly turned into a rectangle. Everything else lands as one create-annotation, set-annotation or delete-annotation batch through /api/ops, which is one entry in the changes drawer and one Ctrl-Z; the rules are in docs/editing.md.

Without a screen

The rendered SVG is inert by default: Graphviz emits shapes, not semantics. This page adds them from the same records the info box is built from, so there is one description of an element rather than two that drift:

The interface follows prefers-color-scheme with a palette per scheme — every colour clears 4.5:1 against its own background, which a single palette used on both cannot — and honours prefers-reduced-motion. Where a colour carries meaning it is never alone: the diff legend prints +, ~ and , the same sigils the diagram draws, and the three diff line styles differ as well as the three hues.

tests/test_browser.py runs axe-core over the page on every CI run and fails on a new WCAG 2.1 AA violation, and drives one end-to-end test — create a device, cable it, undo both — without dispatching a single mouse event.

The bindings

Everywhere

Keys Command Where Needs What it does
Ctrl-K / Ctrl-Shift-P Command palette anywhere Every command on this page, searched by name — and every element address and file path in the inventory, so one field is also 'go to'.
? / F1 Keyboard shortcuts anywhere This table, rendered from the bindings the page actually registered.
ContextMenu / Shift-F10 Open the context menu the diagram What the pointer's right-click offers, on whatever the diagram has focused — the element, the link, or the canvas itself when nothing is.
palette only Take the guided tour anywhere a folder Sixty seconds that create a device, cable it up, move its document, show the YAML that changed and undo the lot — on a throwaway copy of this inventory, so nothing here is written to your files.
Escape Close what is open anywhere The palette, the reference, a prompt, the changes drawer, the inspector — in that order.
Alt-1 Focus the inventory list anywhere a folder The file list. Arrow keys move down it; Enter opens a file.
Alt-2 Focus the YAML pane anywhere The text of the open document. Escape leaves it again.
Alt-3 Focus the diagram anywhere Puts a focus ring on an element and turns on the gestures below.
Alt-4 Focus the diagram outline anywhere The diagram as a list a screen reader can read straight through: one line per element, with what it is linked to.
Ctrl-Enter Render now anywhere Draw the diagram again without waiting for the editor to settle.
Ctrl-Shift-Enter Validate the inventory anywhere Re-run the checks and move focus to the problems list.

Moving around

Keys Command Where Needs What it does
ArrowRight / ArrowLeft / ArrowUp / ArrowDown Move to the adjacent element the diagram Steps to the nearest element in that direction, preferring one this element is linked to — so a whole path can be walked with one hand.
l / Shift-L Cycle this element's links the diagram Focuses each cable or tunnel that terminates here in turn, so a link can be inspected or removed without a pointer. Tab is left alone: the diagram is one stop on the page's tab order, never a trap.
Home First element the diagram The first element of the outline, which is the diagram in reading order.
End Last element the diagram The last element of the outline.
Enter Open the inspector the diagram a focused element Everything known about the focused element, and — in a session — the document that declares it, opened at its line.
Space Pin the inspector the diagram a focused element Keeps the inspector up, and tells the other tabs what this one is looking at.
v Selection tool anywhere The default cursor: a drag on the paper draws a rubber band, and a drag on a shape moves it. Hold Alt to pan without leaving it.
h Pan tool anywhere The hand cursor: a drag anywhere moves the diagram, over a shape or a handle as readily as over the paper, so nothing can be nudged by accident while it is being read. Hold Alt to band without leaving it.
Ctrl-G Go to element… anywhere The palette, opened over element addresses alone.
Ctrl-O Open file… anywhere a folder The palette, opened over the inventory's file paths alone.
Ctrl-F / / Search with a query anywhere The selector language: 'kind = switch and not has vrf', 'within 2 hops of fw-edge', 'interface[address in 10.20.0.0/16]'. A bare word is a name match, as it always was. See docs/query.md.
Alt-Enter Select what the query matched anywhere Puts every match in the selection, so a query feeds straight into a bulk edit, an alignment or a delete. Enter does it too, from the box.
palette only Clear the query anywhere Empties the box, stops highlighting, and un-narrows the drawing.
Ctrl-A Select everything in this view the diagram Every element and link the diagram is drawing, including the ones culled off screen. The canvas only — Ctrl-A in the YAML pane is still the text.
Ctrl-Shift-A Clear the selection anywhere Escape does this too, before it closes anything else.
Shift-ArrowRight / Shift-ArrowLeft / Shift-ArrowUp / Shift-ArrowDown Extend the selection the diagram Steps the way the arrow keys do — preferring an element this one is linked to — and adds what it lands on, so a trunk and everything hanging off it can be collected without a pointer.

Editing the inventory

Keys Command Where Needs What it does
n Create an element… the diagram --write Asks for a kind and a name, and writes the document. 'netviz edit create'.
c Connect this element… the diagram --write Cables the focused element to another, port to port. 'netviz edit connect'.
Delete / Backspace Delete the selection the diagram --write Removes everything selected, or the focused element when nothing is, and everything that cannot survive it: the cables, the tunnels over them, the notes anchored to them, the coordinates that placed them. Asks once when that is more than you named, and writes the lot as one change. 'netviz edit delete --cascade' / 'disconnect'.
Ctrl-C Copy the selection the diagram a folder Puts the selected elements on the system clipboard as JSON — the documents themselves, plus any cable whose two ends are both selected. Paste it into another netviz window, or into a text editor to read it. 'netviz edit copy'.
Ctrl-X Cut the selection the diagram --write Copy, and then delete what was copied — as one change, so one Ctrl-Z puts the documents back. Asks first, listing what goes.
Ctrl-V Paste the diagram --write Writes the clipboard fragment into this inventory: new documents, with free names, the internal cables rewired to the copies, and positions offset from the originals — or dropped where you last right-clicked. A fragment from another inventory pastes the same way.
Ctrl-D Duplicate the selection the diagram --write Copy and paste in one keystroke, without touching the system clipboard: each selected element gets a sibling called 'sw1-copy' beside it. 'netviz edit duplicate'.
F2 Rename the focused element… the diagram --write Renames it and every reference to it. 'netviz edit rename'.
e Set a field… the diagram --write A dotted path and a YAML value, on every selected element at once — or on the focused one when nothing is selected. 'netviz edit set'.
palette only Remove a field… anywhere --write 'netviz edit unset', across the whole selection as one change.
palette only Move to another file… anywhere --write Moves the selected documents into a different file, together. 'netviz edit move'.
palette only New namespace… anywhere --write Makes a namespace by putting something in it — the selection, moved there, or a new element created there. A namespace is a folder and a folder netviz would read is one holding a document, so an empty one is not a thing the inventory can record.
palette only Move into a namespace… anywhere --write Re-homes the selection into another namespace: the typed form of dragging it into that container's box. The documents are rewritten into the folder and every reference to them is re-spelled. 'netviz edit move'.
palette only Disconnect a cable… anywhere --write Removes a cable, leaving both devices. 'netviz edit disconnect'.
b Add a bend to the focused link the diagram --write Drops a waypoint half way along the link, which the route then passes through. Double-clicking the line does the same at the point clicked.
Shift-B Straighten the focused link the diagram --write Clears every bend, leaving the link to run directly between its two devices. The routing style and the label position are kept.
r Change how the link is routed… the diagram --write Spline, orthogonal or straight, on this link alone. Clearing it takes the view's default back. Honoured by 'netviz render' as well as here.
Shift-R Pin the route the renderer worked out the diagram --write Writes the bends netviz computed to keep this link clear of the boxes it passes into the layout document, so they become bends you placed: they stop being recomputed, they get a grab handle each, and moving a device no longer moves them. Refuses on a link that needed no detour, since there would be nothing to pin.
palette only Put the link's label back on the line anywhere --write Undoes a nudged label, leaving it half way along the route where the renderer puts one nobody has moved.
Shift-N Add a note to the diagram… the diagram --write Drops a note where the pointer is — or in the middle of the view when the keyboard asks — and opens it for typing. Right-clicking an element or a link anchors the note to it instead, so it follows what it is about. 'netviz edit create-annotation'.
Shift-E Edit the note's text… the diagram --write A text box over the note itself, in the markdown subset §21 defines. Ctrl-Enter or clicking away writes 'spec.text'; Escape abandons it and writes nothing. Double-clicking the note does the same.
i Add an interface… the diagram --write 'netviz edit add-interface'.
palette only Remove an interface… anywhere --write 'netviz edit remove-interface'.
Ctrl-Shift-Y Style inspector anywhere a folder How the selection is drawn (§22), which layer each value came from, and the controls to change it. A change is written to spec.style, so the picture and the YAML stay one thing.
palette only Restyle the selection anywhere --write Open the style inspector on what is selected.

Arranging the diagram

Keys Command Where Needs What it does
palette only Align left anywhere --write Every selected element onto the leftmost one's left edge.
palette only Align centres anywhere --write Onto the vertical axis half way across the selection.
palette only Align right anywhere --write Onto the rightmost one's right edge.
palette only Align top anywhere --write Onto the topmost one's top edge.
palette only Align middles anywhere --write Onto the horizontal axis half way down the selection.
palette only Align bottom anywhere --write Onto the bottommost one's bottom edge.
palette only Distribute horizontally anywhere --write Equal gaps between the boxes, left to right, with the two outermost left where they are. Needs three.
palette only Distribute vertically anywhere --write The same, top to bottom.
palette only Snap to the grid anywhere --write Rounds each selected element's position to the pitch this inventory sets in 'netviz.toml' ([editor] grid, 20 points by default).

The view

Keys Command Where Needs What it does
Ctrl-Shift-F Draw only what the query selects anywhere Narrows the drawing itself rather than highlighting inside it — the same narrowing 'netviz render --select' does.
palette only Switch layer… anywhere Physical, l1, l2, l3, ipam, overlay, routing, rack, power, identity.
] Next layer anywhere The next entry of the layer menu.
[ Previous layer anywhere The previous entry of the layer menu.
Alt-I Toggle IP addresses anywhere Whether the picture prints addresses. The inspector shows them either way.
Alt-V Toggle VLANs anywhere Whether the picture prints VLAN membership.
Alt-G Toggle namespace grouping anywhere Collapse each namespace into one box.
f Fold or unfold this namespace the diagram Draws a namespace box as the single node it stands for, or opens it again — the container the pointer picked, or the one holding the focused element. The same folding 'netviz render --collapse' does. A view, not an edit: nothing is written, and how much of a diagram somebody wants to look at is not a fact about the network.
Alt-N Toggle annotations anywhere Whether the notes, areas and legends of §21 are drawn. They are commentary, never topology, so hiding them changes nothing the tool concludes — only how much of the picture is somebody's explanation.
Alt-K Toggle icons anywhere Whether each device is drawn as its icon or as a plain shape. The theme is the one 'netviz web --icons' named, or the set that ships with netviz; which of them exist is the command line's to say, because a theme is a directory, but whether this drawing uses one is a question about the picture.
Alt-S Toggle strict anywhere Report warnings as errors.
palette only Filter by VLAN… anywhere Keep only elements participating in the VLANs given.
Alt-F Failure mode anywhere a folder Click an element and everything it would isolate from the gateways greys out; the status line names the count. Reads only — nothing is written, and Escape or the same key puts the diagram back.
0 Fit the diagram anywhere Undo the panning and zooming.
Plus / = Zoom in anywhere Around the middle of the canvas, so nothing jumps off screen.
Minus Zoom out anywhere Around the middle of the canvas.

Files and history

Keys Command Where Needs What it does
Ctrl-S Save the open file anywhere --write Writes it back, stating the hash it was opened at.
Ctrl-Z Undo anywhere --write The session's stack, not the browser's: it puts files back on disk.
Ctrl-Shift-Z / Ctrl-Y Redo anywhere --write Applies the last undone change again.
Ctrl-B Changes drawer anywhere a folder This session's changes, and the diagram repainted as the diff they add up to.
palette only Copy the equivalent commands anywhere a folder The session as a 'netviz edit' script somebody else can review or run.
Ctrl-Shift-H History timeline anywhere a folder A scrubber over the commits that changed this inventory. The diagram becomes the diff the selected commit carries against its parent, arranged as that revision arranged it.
Alt-ArrowLeft Older revision anywhere a folder One commit back along the timeline. Stops the playback if it is running.
Alt-ArrowRight Newer revision anywhere a folder One commit forward along the timeline.
Alt-P Play the history anywhere a folder Step through the range by itself, a frame at a time, until the newest revision or until one that will not load.

The scratchpad

netviz web                                  # opens on the netviz init example
netviz web devices/sw-office.yaml           # seeded from one file
kubectl get cm topology -o jsonpath={..yaml} | netviz web   # or from a pipe

The left pane holds a document stream — one or more documents separated by --- — and re-renders about half a second after you stop typing. Nothing is written. The seed is read once, at startup; after that the stream lives in the browser and every pass happens in memory, so the command cannot damage the file it was seeded from and equally will not save your work: copy the text out before you close the tab.

A stream has no folders and therefore no namespaces, and no file to write back to. That is why --write is refused here and why a folder opens a session instead.

Both faces

Hovering a node or a link opens an info box holding what the diagram has no room for: every interface with its type, MAC, MTU, addresses and VLAN mode; every link that terminates on the element, what it runs to and over which port; and, at layer 3, the prefix a subnet node stands for and who is addressed in it. Everything it shows is the same data netviz render -f json exports — the records are that export — so the two cannot drift apart, and they are the same records a committed SVG carries as tooltips (docs/rendering.md). The element under the pointer and everything it touches are lifted out of the diagram while the box is open; click to pin the box, click again or press Esc to let go.

Beyond that: the layer, the VLAN filter and the display toggles are in the header and apply on the next render; the canvas zooms with the wheel and pans with a drag under the pan tool, the middle button, or Alt; and the splitter between the panes moves.

Broken text still draws. netviz render refuses an inventory with errors unless --force, because a diagram that disagrees with the files misinforms whoever is shown it. Here the diagram is the feedback and text being edited is wrong most of the time, so every problem is listed with its file and line and whatever resolved is drawn anyway.

netviz.toml decides how this machine draws: the [render] table and --profile of the inventory named by -i — the current directory by default — supply the settings this command has, --icons above all. A session also reads the [validate] table of the folder it has open, so the problems it lists are the ones netviz validate would list in that tree; a stream has no folder of its own to look in and uses the built-in defaults plus the strict toggle in the header.

Icons

The icons box in the header draws every device as its picture instead of as a plain shape, and unticking it puts the shapes back — no restart, no flag, and no reload. It is the question somebody asks while looking at a diagram, so it is answered where they are looking.

The split is deliberate, and it is the same one --write makes. Which themes exist is the command line's to say, because a theme is a directory of images on this machine and a page has no business naming one: --icons DIR puts your own directory on the list, and --icons cisco — or nothing at all — leaves the set that ships with netviz. Whether this drawing uses one is the browser's, because that is a fact about the picture and about nobody's filesystem. So the switch offers exactly what the server was started with, /api/state says what that is, and a request naming anything else is refused with the list it could have named.

A directory theme is offered to the page as custom, not as its path: the switch has to be able to ask for it again after turning it off, and the name it uses to do that should say nothing about the filesystem the server is running on. Only this server maps custom back to the directory.

--icons therefore sets where the switch starts rather than overriding it: a session started with --icons cisco opens with icons on and can still turn them off, and one started without can still turn them on. ?icons=cisco, ?icons=custom and ?icons=none do the same thing in an address bar, like every other view toggle.

The server

The same restrictions apply as to the watch preview — loopback by default, so publishing the interface with --host is an explicit act and is warned about; a fixed set of routes; no request path ever turned into a file name; a Host header check that keeps a loopback bind from being reached through a rebound DNS name — plus three of its own: a request body is capped at 1 MB, the SVG is parsed and stripped of anything that could execute or navigate before it is put into the page, and no write route exists at all without --write on a loopback bind. It is a development server: do not put it on a hostile network.

The event stream and presence, against the same threat model

Both are new surface, and both are held to the rules above rather than excused from them.

The default port is 8081, one above the watch preview's, so a watch run and an editing session can be open at the same time. --port 0 lets the operating system choose one instead. --open — on by default — points the default browser at the page once the server is listening; --no-open prints the URL and leaves the browser alone, which is what you want over SSH.

Arguments

Argument Required Count Default
[SOURCE] no 1

Options

Flag Value Default Meaning
--host ADDRESS 127.0.0.1 Address to bind. The default keeps the interface on this machine; an inventory describes internal topology, so publishing it is an explicit act.
--port INTEGER, 0-65535 8081 Port to bind. 0 lets the operating system choose one.
--open, --no-open --open Open the interface in the default browser once it is listening.
--icons THEME|DIR Draw each element as an icon instead of a plain shape, and start the toolbar's icon switch on. Built in: cisco, none. Which themes exist is named here rather than in the browser, because a theme is a directory on this machine; whether a drawing uses one is the switch's, and it can turn this theme off and back on.
--theme NAME|PATH Apply a stylesheet to the diagram (§22). The style inspector shows the resolved appearance and which layer each value came from. Built in: blueprint, mono, none. Chosen here rather than in the browser, because it names a file on this machine.
--write, --read-only --read-only Let the browser change the inventory. Only for a SOURCE folder, only on a loopback bind, and never by default: an editor that can write is a decision.
--profile NAME Apply the [profile.NAME] block of netviz.toml on top of its [render] table. Explicit flags still win over both.
--show-config off Print the settings this invocation resolves to, and where each one came from, then exit without doing any work.

Exit codes

The interface is ended by Ctrl-C, which is how the command is meant to finish. Text that does not parse is reported in the page, not by the process.

Code Meaning
0 The server ran and was stopped with Ctrl-C.
2 Usage error, an unusable netviz.toml, or --write where it cannot be given.
3 A SOURCE folder could not be read.
6 The address could not be bound — usually something else on port 8081.

See also