netviz GitHub

netviz watch

netviz watch re-renders whenever a file in the inventory changes, optionally serving the result on a page that reloads itself. Every cycle is the same load, validate and render netviz render performs, followed by a timestamped status line and any findings. It is the command to leave running in a second terminal while you edit YAML in the first.

Synopsis

netviz [GLOBAL OPTIONS] watch [OPTIONS]

The status line

One line per cycle, with the time, the status and what came of it:

09:41:02  ok       23 nodes, 26 edges → topology.svg (128 ms)
09:41:37  invalid  1 error; keeping the render from before
errors (1):
  sites/hq/links.yaml#0:12  E001  cable 'sites/hq/cbl-07' endpoint sw-hq:port9: no element named 'sw-hq' is declared in this inventory

A failed cycle changes nothing. The file written by --output keeps its last valid contents and the preview keeps serving the last valid diagram, so a half-typed document never blanks the picture you are working from — which is why the line says keeping the render from before rather than reporting a size of zero. Nothing ends the loop except Ctrl-C: a syntax error, a deleted root, a --neighbors-of target that no longer resolves are all statuses, not crashes.

With --output the file is rewritten atomically, so a reader — a browser with the SVG open, a static site build — sees the old diagram or the new one, never half of each. Naming a file inside the tree being watched is fine: the render's own output is excluded from the watch, which is what keeps the loop from feeding itself forever.

Giving neither --output nor --serve is allowed and warned about: each render is then checked and discarded, which is occasionally what you want and usually not what you meant.

What triggers a render

Only YAML documents, netviz.toml and .netvizignore trigger a render; an editor swap file or a rendered diagram does not, and neither does anything under a directory the loader skips (.git/, _drafts/, …) — the same rule the loader itself applies, so what the watcher reacts to is what the inventory contains. A single-file inventory is watched through its directory, because editors replace a file rather than rewrite it and a watch on the file itself would not survive the first save.

--debounce is how long a burst of filesystem events is collected before re-rendering. One editor save is several events — a temporary file, a rename, a metadata change — and re-rendering each of them would render three times and show the middle one.

The default is 300 ms on Linux and 700 ms on macOS and Windows, because the window has to be wider than the span those events arrive over and that span belongs to the backend rather than to the editor. inotify delivers each event as it happens, so one save lands within a millisecond or two. FSEvents coalesces and delivers on its own schedule, and ReadDirectoryChangesW spreads a save-by-rename noticeably further — often as two bursts, which at 300 ms would be two renders for one keystroke.

Raise it for a large inventory, lower it if you want the diagram to keep up with you and do not mind the occasional double render.

Every render option applies

Every filter and display option of netviz render applies here too — --tooltips, --link-template and --element-ids included, which is what makes watch -f svg -o topology.svg keep an interactive diagram up to date. -f html works the same way, repeated --layer included, so watch -f html -o topology.html --serve gives you the whole interactive page, re-rendered as you type. -f defaults to svg here rather than dot, because a live preview wants a picture.

docs/rendering.md explains the layers, the filters, the aggregation and the display options themselves; this command adds nothing to them and changes none of their defaults.

The live preview

--serve also hosts the render over HTTP. The page polls once a second and swaps the diagram in when it changes — polling rather than server-sent events keeps the client a dozen lines of dependency-free JavaScript.

The preview is bound to loopback and stays there unless you say otherwise. An inventory describes internal network topology — addresses, VLANs, what is plugged into what — so --host is the explicit act of publishing it, and doing so prints a warning. The server answers GET and HEAD on five fixed routes, never turns a request path into a file name, and refuses a request that reached a loopback preview under a foreign Host header. It is a development server: do not put it on a hostile network.

--host and --port describe that server, so both require --serve; given without it they are a usage error rather than a flag that appeared to work and never did. --port 0 lets the operating system choose one, and the address actually bound is printed.

netviz watch --serve                                   # preview at http://127.0.0.1:8080/
netviz watch -f svg -o topology.svg                    # just keep a file up to date
netviz watch --serve --layer l2 --vlan 10 --title vlan10
netviz watch --serve --host 0.0.0.0 --port 9000        # deliberate, and warned about

Arguments

Takes no positional arguments.

Options

Flag Value Default Meaning
-f, --format [dot|svg|html|png|pdf|mermaid|json] svg Output format. Defaults to svg, which is what a live preview wants.
-o, --output FILE Rewrite this file after every successful render.
--namespace NS Keep only elements in this namespace or below it. Repeatable.
--vlan VID Keep only elements participating in this VLAN. Repeatable.
--kind [switch|router|firewall|hub|computer|server|adapter|patchpanel|pdu|user|group] Keep only elements of this kind. Repeatable.
--name GLOB Keep only elements whose name matches this glob. Repeatable.
--neighbors-of NAME Keep only the neighbourhood of this element.
--depth INTEGER, >= 0 1 How many hops --neighbors-of reaches.
--select QUERY Keep only the elements this query selects, e.g. "kind = switch and not has vrf". The flags above are sugar for the equivalent query and are combined with it; 'netviz query --explain' prints which. See docs/query.md.
--collapse NS Replace this namespace and everything under it with one node, labelled with what it holds. Links crossing the boundary attach to it; links inside it are counted rather than drawn. Repeatable.
--collapse-depth N Collapse every namespace N levels deep, counted from the shallowest one that branches: '--collapse-depth 1' is the site-level overview of a tree laid out as sites//.
--bundle-links, --no-bundle-links Draw parallel links between the same pair of elements as one edge, with the count in the label. Members of a declared 'lag' interface are bundled either way unless --no-bundle-links is given, since the inventory already says they are one logical link.
--show-ips, --no-show-ips --show-ips Print configured IP addresses on the nodes.
--show-vlans, --no-show-vlans --show-vlans Annotate nodes and links with VLAN membership.
--annotations, --no-annotations --annotations Draw the notes, areas and legends the inventory declares for this view. Turn them off for a diagram that should carry the topology and nothing written about it — a printed page for an audit — and leave them on for one that is being read rather than checked, where the callout is the reason the screenshot is worth attaching to the ticket.
--group-by-namespace off Draw each namespace as a visual group.
--icons THEME|DIR Draw each element as an icon instead of a plain shape. Built in: cisco, none. A directory of images named after element kinds (router.svg, switch.png, ...) also works. Graphviz formats only.
--theme NAME|PATH Apply a stylesheet: selectors by kind, name, namespace, role or label, each mapping onto a style block. Built in: blueprint, mono, none. A path to a 'kind: theme' YAML file also works. An element's own spec.style still wins.
--style, --no-style --style Honour the styles the inventory and the theme declare. --no-style draws the plain diagram from the built-in palette alone, which is the way to read a topology whose stylesheet is in the way. Icons are unaffected: use --icons none.
--tooltips, --no-tooltips --tooltips Carry the full detail of each element — interfaces, addresses, VLANs, cabling — as hover text. Reaches a reader in svg output; png and pdf have nowhere to put it.
--link-template URL Link each element back to the YAML that declares it, e.g. 'https://git.example.com/net/blob/main/{file}#L{line}'. Placeholders: {file}, {line}, {name}, {namespace}, {kind}. dot and svg only.
--element-ids off Give every node, edge and namespace a stable id derived from its name, so the diagram can be deep-linked and styled from outside. dot and svg only.
--max-addresses N 4 Longest address list spelled out under a node before it is abbreviated to 'and N more'. 0 prints the count alone.
--rankdir [tb|lr|bt|rl] TB, top to bottom Layout direction. A wide network reads better left to right; a deep one top to bottom. Honoured by the Graphviz backends and by mermaid.
--routing [spline|orthogonal|straight] whatever the inventory's layout documents say, else spline How links are drawn between the bends they are pinned through: 'spline' is the curve Graphviz draws, 'orthogonal' right angles, 'straight' segment to segment. A default: a link that pins a style of its own keeps it. Honoured by the Graphviz backends, the JSON export and the editor. 'netviz layout --write' records it in the view it arranges, so the choice is the inventory's rather than the command line's from then on.
--avoid, --no-avoid avoid Route orthogonal links around the boxes they are not attached to instead of straight across them. Only applies to an arranged diagram drawn with '--routing orthogonal': a spline has nothing to route around, and an unarranged one is routed by Graphviz, which already avoids nodes. A bend you placed yourself is never moved — routing fills the segments between them. '--no-avoid' is the local Z-and-L every orthogonal diagram was drawn with before this existed.
--title TEXT Caption for the diagram.
--layer [physical|l1|l2|l3|ipam|overlay|routing|rack|power|identity|netns|security] l1 l1 draws the physical topology; l2 annotates it with VLANs; l3 draws IP subnets and the elements addressed in them; ipam draws the address plan — the same prefixes without the devices, nested inside the blocks they came out of and showing how full each is; overlay draws the tunnels; routing draws the BGP sessions and OSPF adjacencies, clustered by VRF; physical adds the patch panels l1 splices out; rack draws a front elevation per rack; power draws the PDUs and the feeds into everything they power; identity draws the users and groups; netns opens each machine up into the network stacks inside it, joined by their veth pairs; security draws the firewall zones and what the policy lets cross between them. Repeatable for -f html, which draws each layer and puts a switcher over them.
--strict off Treat warnings as errors.
--force off Proceed even when validation failed. The result may not match the files.
--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.
--serve off Also host the render over HTTP, on a page that reloads itself.
--host ADDRESS 127.0.0.1 Address --serve binds to. The default keeps the preview on this machine; an inventory describes internal topology, so publishing it is an explicit act.
--port INTEGER, 0-65535 8080 Port --serve binds to. 0 lets the operating system choose one.
--debounce MS 300 on Linux, 700 on macOS and Windows How long a burst of filesystem events is collected before re-rendering.

Exit codes

A watch is ended by Ctrl-C, which is how the command is meant to finish and not a failure. Everything an inventory can do wrong is a status line instead of an exit status.

Code Meaning
0 The loop ran and was stopped with Ctrl-C.
2 Usage error: --host or --port without --serve, an unusable netviz.toml.
6 --serve could not bind its address — usually a preview already running on that port.

See also