nettopo
A Python CLI that reads saved Cisco show command outputs (files only — no live
device connections in v1) and generates network diagrams in draw.io format with
Cisco icons, plus the intermediate data as CSV tables.
Four diagram views are produced from the same parsed data model:
- L2 — physical/link-layer topology from CDP/LLDP, with interface labels and MLAG.
- STP — per-VLAN Rapid-PVST spanning-tree state (root, bridge IDs, priorities, port roles/states).
- HSRP — first-hop redundancy per SVI (virtual IP, priority, active/standby/listen).
- BGP — BGP neighbor (session) graph.
nettopois a working title — seePROJECT_SPEC.mdsection 1.
Status
Phase 5 — HSRP. nettopo hsrp -i <dir> now renders real per-VLAN first-hop-redundancy
draw.io diagrams: each HSRP group is drawn as a virtual gateway carrying its virtual IP,
with a link to every router that offers it labeled with that router's SVI, role and
priority — green for active, amber for standby — and the active router highlighted. It
takes the same --vlan N / --group-mode per-vlan|strict|topology / --all options as
stp, and hsrp.csv is now populated. nettopo stp -i <dir> (Phase 4) renders per-VLAN
spanning-tree diagrams: the root bridge is highlighted, links are colored by forwarding vs
blocking port state and labeled with role/state at each end. nettopo l2 -i <dir>
(Phase 3) renders devices styled with Cisco icons (render/icons.py) by inferred
DeviceRole, with per-end interface labels, --endpoints all|network-only filtering and
--link-mode physical|port-channel (MLAG links drawn once per bundle). Output is
post-processed by lucidify by default (--no-lucidify to skip it), which keeps each
end's label attached to its own end of the link rather than merging both into one, and
normalizes the geometry flag N2G gets wrong so the labels survive a Lucidchart import.
bgp and all still report "not implemented". All three working views are shown
end to end under Example diagrams, generated from the capture set in
examples/campus/. See the delivery plan in
PROJECT_SPEC.md and the
open issues for the phased build-out.
Pending manual step: Cisco
mxgraph.cisco19.*draw.io icons are a confirmed requirement but may degrade on Lucidchart import (Lucid uses a different shape library, and these icons are drawn by draw.io's own code rather than looked up as named stencils). This still needs a real Lucid import to validate, which requires interactive Lucid access this project's automation doesn't have — see the checklist indocs/architecture.md.
Installation
pip install nettopo
Pre-releases (0.3.0rc1 and the like) are published from the same pipeline but pip
skips them unless asked: pip install --pre nettopo, or pin the exact version. Each
release is published to PyPI by .github/workflows/publish.yml when a v* tag is
pushed. To install from source for development:
git clone https://github.com/isrferna/nettopo.git
cd nettopo
pip install -e ".[dev]"
Requires Python 3.11+.
Example diagrams
examples/campus/ is a complete, self-contained capture set for a
six-switch campus: two core switches joined by a port-channel, two distribution
switches, two access switches, plus an edge router, two ESXi hosts and a third access
switch that no capture covers — ten devices in total. It is the source of every diagram
below except the last, which comes from the smaller
examples/hsrp-quad/. Every image is nettopo's own output, and the
.drawio files behind them are committed next to the captures. Regenerate them with:
nettopo l2 -i examples/campus -o examples/campus/diagrams
nettopo l2 -i examples/campus -o examples/campus/diagrams --endpoints network-only --link-mode port-channel
nettopo stp -i examples/campus -o examples/campus/diagrams --all
nettopo hsrp -i examples/campus -o examples/campus/diagrams --all
nettopo hsrp -i examples/hsrp-quad -o examples/hsrp-quad/diagrams --all
The PNGs are exports of those same files, made with
draw.io Desktop's CLI — leaving out -t/--transparent is what
gives them a white background:
drawio -x -f png -s 2 -b 20 -o examples/campus/diagrams/l2/l2_full.png examples/campus/diagrams/l2/l2_full.drawio
A PNG is a snapshot. Open the
.drawioin draw.io for the editable diagram — draggable nodes, and the hover tooltips that carry a port-channel's member interfaces.
nettopo l2 — physical topology
Ten nodes, thirteen links; each link is labeled with the physical interface at each end,
and each node is drawn with the icon for its role. The role comes from the chassis the
device reports — cisco C9500-16X for the core pair, cisco WS-C2960X for the access
switches, cisco ISR4331/K9 for edge-rtr, VMware ESX for the two ESXi hosts — falling
back to the capabilities its neighbors advertised over CDP/LLDP when the platform names
nothing recognizable. A legend names every role the diagram actually contains.
Source: l2/l2_full.drawio.
nettopo l2 --endpoints network-only --link-mode port-channel
The same topology with the two flags applied: the ESXi hosts are gone (they advertise
neither Router nor Switch), and the two physical core links have collapsed into the
single bundle both switches call Po1. Its member interfaces move into the link's
draw.io hover tooltip (Gi1/0/1 — Gi1/0/1, Gi1/0/2 — Gi1/0/2), which the PNG below
cannot show but the .drawio does. Eight nodes, ten links.
Source:
l2/l2_network-only_port-channels.drawio.
nettopo stp --vlan 10 — spanning tree for the user VLAN
Switches only: edge-rtr and the two ESXi hosts are gone. core-sw1 is the root bridge
(gold card); every other node carries its bridge MAC and effective priority.
acc-sw3 has no capture of its own, so it is drawn faded, in italics, and labeled with its
name alone — it is included because the port facing it (dist-sw2 Gi1/0/22) is not an Edge
port, the same filter that keeps the ESXi hosts out. Green links forward at both ends;
red links have a blocked end. The three blocked ports are what breaks the two loops in
the topology. The legend lists only the markings this particular tree uses.
Source: stp/stp_vlan10.drawio.
Note that the core-to-core link is drawn once, labeled Po1: spanning tree treats a
bundle as one logical port, and show spanning-tree only ever names Po1 while
CDP/LLDP only ever name Gi1/0/1/Gi1/0/2. show etherchannel summary is what joins
the two — drop it from core-sw1.txt/core-sw2.txt and this link disappears.
nettopo stp --vlan 30 — the same network, a different tree
VLAN 30 is rooted on core-sw2 (spanning-tree vlan 30 root primary, with core-sw1
as secondary). Same devices, same cabling, different tree: the blocked ports move from
Gi1/0/2 to Gi1/0/1 on both distribution switches, and the core port-channel reverses
role. This is why the view renders one diagram per VLAN rather than a single "STP
diagram".
Source: stp/stp_vlan30.drawio.
What --group-mode does to this capture set
The example has four VLANs, and VLANs 10, 20 and 99 produce the identical picture above
— only VLAN 30 differs. VLAN 99 is the interesting case: its tree matches 10 and 20
exactly, but nobody configured core-sw2 as its secondary root, so its priorities
differ. That is precisely the line between the two grouping modes:
| Command | Files written into output/stp/ |
|---|---|
nettopo stp -i examples/campus --all |
stp_vlan10, stp_vlan20, stp_vlan30, stp_vlan99 — four identical-looking diagrams for three identical trees |
nettopo stp -i examples/campus --all --group-mode strict |
stp_vlans-10_20, stp_vlan30, stp_vlan99 — 99 splits off on its priority |
nettopo stp -i examples/campus --all --group-mode topology |
stp_vlans-10_20_99, stp_vlan30 — two diagrams, one per distinct tree |
nettopo hsrp --vlan 10 — who answers for the default gateway
The core pair also runs HSRP on VLANs 10, 20 and 30, aligned with the spanning trees:
core-sw1 is active where it is root, core-sw2 is active for VLAN 30. The rounded box
is not a device — it is the virtual gateway 10.10.10.1, the address the VLAN's hosts are
configured with. Each router carries its own SVI address (10.10.10.2, 10.10.10.3)
under its name, so both halves of the first hop are on the page, and links to the gateway
labeled with its SVI, HSRP role and priority: green for the router currently answering,
amber for the one waiting to take over. The active router carries the same gold card the
STP view gives a root bridge.
Source: hsrp/hsrp_vlan10.drawio.
--group-mode works the same way it does for stp, over the HSRP roles and priorities
instead of the spanning tree. VLANs 10 and 20 have the same active/standby split but were
configured with different priorities, so:
| Command | Files written into output/hsrp/ |
|---|---|
nettopo hsrp -i examples/campus --all |
hsrp_vlan10, hsrp_vlan20, hsrp_vlan30 |
nettopo hsrp -i examples/campus --all --group-mode strict |
unchanged — 10 and 20 differ on priority |
nettopo hsrp -i examples/campus --all --group-mode topology |
hsrp_vlans-10_20, hsrp_vlan30 — priorities ignored, so 10 and 20 collapse |
A group with four members — active, standby, and two listening
A two-switch gateway pair is the common case, but an HSRP group can have any number of
members and only ever two of them are named: one active, one standby. Everybody else sits
in Listen, watching the hellos and waiting for an election.
examples/hsrp-quad/ is a small capture set for exactly that — VLAN
50 spans two buildings, each with a pair of layer-3 switches, and all four share one
group:
| Router | SVI address | Priority | State |
|---|---|---|---|
bldg-a-sw1 |
10.20.50.2 |
150 | Active — highlighted, green link |
bldg-a-sw2 |
10.20.50.3 |
140 | Standby — amber link |
bldg-b-sw1 |
10.20.50.4 |
110 | Listen — neutral link |
bldg-b-sw2 |
10.20.50.5 |
100 | Listen — neutral link |
Source: hsrp/hsrp_vlan50.drawio.
The two listeners are why every node carries its own address and not just the virtual
one. show standby brief reports the active and standby routers by address — that is
what its Active and Standby columns are — but it never names anyone else, so
10.20.50.4 and 10.20.50.5 appear nowhere in any of the four captures' HSRP output.
They come from show ip interface brief instead, which is why that command is worth
capturing even though the HSRP view needs no topology. The legend lists only the two
colors this group uses; a neutral link means the router is in the group but neither
answering for it nor next in line.
Usage
nettopo [--version] [--log-level LEVEL] <command> [options]
Every command reads a directory of saved capture files (see
Preparing captures below) and writes into an output directory,
creating it if needed. parse, l2, stp, and hsrp are implemented; bgp and all
are scaffolded but not yet implemented (running them prints '<command>' is not implemented yet. and exits with status 1 — see Status).
Global options
| Option | Values | Default | Meaning |
|---|---|---|---|
--version |
flag | — | Print the installed version and exit, without a subcommand: nettopo --version. Read from the installed distribution's metadata, so after bumping pyproject.toml re-run pip install -e . for it to catch up. |
--log-level |
DEBUG, INFO, WARNING, ERROR, CRITICAL |
INFO |
Logging verbosity. Goes before the subcommand: nettopo --log-level DEBUG l2 -i ./captures. |
Options common to every command
| Option | Values | Default | Meaning |
|---|---|---|---|
-i, --input |
directory path | (required) | Directory of saved device captures to read. See Preparing captures. |
-o, --output |
directory path | ./output |
Directory to write generated output into (created if it doesn't exist). |
--platform |
an ntc-templates platform name | cisco_ios |
Fallback platform used to pick the right parsing template when a device's own platform can't be detected from its show version output. IOS and IOS-XE both use cisco_ios. |
--no-lucidify |
flag | off | Skip the link-label post-process (render/lucidify.py) on generated .drawio files, leaving N2G's raw per-end label cells in place. Only affects commands that render diagrams (l2, stp, hsrp; later bgp, all) — parse ignores it. |
nettopo parse
Ingests captures, builds the data model, and writes every CSV table — no diagrams. This is the primary debugging aid: if a diagram looks wrong, inspect the CSVs first to localize the fault to parsing vs. rendering.
nettopo parse -i ./captures -o ./output
Writes output/csv/devices.csv (including each device's serial, from its own
show version or from the name a Nexus neighbor advertises; its platform, from its own
show version or from the CDP/LLDP report of a neighbor; and its management IP, from
CDP's Management address(es) block or LLDP's management TLV), interfaces.csv,
neighbors.csv, vlans.csv, stp.csv (base and effective bridge priority — see
PROJECT_SPEC.md §6), hsrp.csv (one row per group member,
with the group's virtual IP beside that member's SVI, priority, role and preempt flag),
and header-only bgp.csv until Phase 6 adds that parser.
One row per device and per adjacency, regardless of how many names its neighbors use for
it: CDP and LLDP disagree constantly (nxos-core1 vs nxos-core1(FDO21120U5D) vs
nxos-core1.example.com), and those spellings are correlated into one device before the
model is built — see
PROJECT_SPEC.md §5.2.
nettopo l2
Renders the physical/link-layer topology (from CDP/LLDP) as a draw.io diagram: nodes are
devices styled with Cisco icons by inferred role, and links carry per-end interface
labels. Needs show cdp neighbors detail and/or show lldp neighbors detail in the
captures — see Which commands each subcommand
needs.
| Option | Values | Default | Meaning |
|---|---|---|---|
--endpoints |
all, network-only |
all |
all includes every discovered device. network-only drops any device that isn't a source capture and wasn't reported by a neighbor with Router/Switch CDP/LLDP capabilities (i.e. drops hosts/phones, keeps the network). |
--link-mode |
physical, port-channel |
physical |
What a drawn link represents. physical: one link per discovered adjacency, labeled with the physical interface at each end. port-channel: adjacencies belonging to the same bundle collapse into a single link labeled Po150 (MLAG), with the member interfaces in the link's hover tooltip. Links with no port-channel on either end are drawn the same way in both modes, so this option only changes the diagram where bundles actually exist. |
nettopo l2 -i ./captures --endpoints network-only --link-mode port-channel
Writes output/l2/l2_full.drawio (endpoints all) or
output/l2/l2_network-only.drawio (endpoints network-only); under --link-mode port-channel the filename gains a _port-channels suffix
(l2_full_port-channels.drawio), so both link modes of the same topology can live side
by side in one output directory.
Port-channel mode needs show etherchannel summary (IOS/IOS-XE) or show port-channel summary (NX-OS) in the capture — without it no bundles are known and the diagram is
identical to physical. Bundle membership is also exported in interfaces.csv
(po_id, po_members), which is the quickest way to check whether that command was
captured and parsed. The tooltip is a draw.io feature: hover the link in draw.io to see
which physical ports the bundle carries.
nettopo stp
Renders per-VLAN Rapid-PVST spanning-tree diagrams: switches only, root bridge
highlighted, links colored by forwarding (green) vs. blocking (red) port state and
labeled with role/state at each end. Node labels show the bridge MAC and effective
priority (base priority is in stp.csv).
Needs show spanning-tree and a neighbor-discovery command (show cdp neighbors detail / show lldp neighbors detail) in the captures: spanning-tree output alone never
says who is on the other end of a port, so without CDP/LLDP the diagram has nodes and no
links. If the switches are joined by port-channels, show etherchannel summary
(IOS/IOS-XE) or show port-channel summary (NX-OS) is required as well — spanning-tree
names the bundle (Po1) while CDP/LLDP name its members (Gi1/0/1), and only the bundle
table connects the two. See Which commands each subcommand
needs.
Each port-channel is drawn as a single link labeled with the bundle name, with the member interfaces in the link's hover tooltip: spanning-tree treats a bundle as one logical port, so drawing one line per member would misrepresent it.
Switches that appear only in a neighbor's CDP/LLDP output — no capture of their own — are included faded, in italics and labeled with their name alone, since there is no bridge data to show for them. They are reached only through ports that are not Edge/PortFast, which is what keeps phones, access points and servers out of a spanning-tree diagram.
If the root bridge is one of those uncaptured switches, it is highlighted only when an LLDP chassis address matches the root address exactly. Without a match the run logs a warning naming the root's MAC and highlights nothing, rather than guessing.
A diagram with several nodes and no links is always reported as a warning. To see why each
individual link was dropped, raise the log level — note that --log-level is a global
option, so it goes before the subcommand:
nettopo --log-level DEBUG stp -i ./captures --all
| Option | Values | Default | Meaning |
|---|---|---|---|
--vlan |
VLAN id, e.g. 10 |
(none) | Restrict to a single VLAN's diagram. Mutually exclusive with --group-mode. |
--group-mode |
per-vlan, strict, topology |
per-vlan |
How to group VLANs that would render identically. per-vlan: one diagram per VLAN, no grouping. strict: group VLANs whose root, bridge priorities, and port roles/states all match. topology: group VLANs whose root and port roles/states match, ignoring configured priorities (a looser match than strict). Mutually exclusive with --vlan. |
--all |
flag | off | Write every resulting diagram (one per VLAN, or one per group under --group-mode) into output/stp/. |
Either --vlan or --all is required — with neither, there's no single sensible
default to render out of a potentially many-VLAN model, so the command exits with an
error asking you to pick one.
nettopo stp -i ./captures --vlan 10 # one diagram, VLAN 10 only
nettopo stp -i ./captures --all # one diagram per VLAN
nettopo stp -i ./captures --all --group-mode strict # grouped by exact match
nettopo stp -i ./captures --all --group-mode topology # grouped by topology only
Filenames: output/stp/stp_vlan10.drawio for a single VLAN; for a group, a sorted,
filesystem-safe name listing every VLAN it covers, e.g.
output/stp/stp_vlans-10_20_30.drawio.
nettopo hsrp
Renders per-VLAN HSRP diagrams: which routers offer the VLAN's default gateway, which one
currently answers for it, at what priority, and on which address. Needs only show standby brief in the captures — unlike stp, this view draws no physical links, so no
neighbor-discovery command is required (show version still helps, by naming each device
from its own capture rather than from its filename).
Each HSRP group is drawn as a virtual gateway: a plain rounded box labeled with the
VLAN, the group number and the virtual IP. It is deliberately not given a Cisco icon,
because it is an address rather than a device. Every router running that group links to
it, labeled Vl10 active/150 — its SVI, HSRP role and priority — with the link colored
green when that router is active for the group and amber when it is the standby. The
active router itself is highlighted with a gold card.
Each router is also labeled with its own SVI address in that VLAN, beside the virtual
one, which is what tells you which box a given traceroute hop or ping reply actually is.
That address comes from show ip interface brief (or show interfaces), not from show standby brief — the brief output names only the active and standby routers, and only by
address, so a member that is merely listening cannot be placed from it at all. Add one of
those two commands to the captures and every member gets its address; without them the
nodes are labeled with their names alone rather than a guess.
One VLAN is one diagram, including when its SVI carries several groups: two gateways load-sharing a VLAN (each active for one group, standby for the other) is one picture with two virtual gateways in it, not two pictures.
HSRP running on a routed port or a subinterface rather than an SVI is skipped with a warning naming the interface: diagrams and CSV rows alike are keyed by VLAN, and such a group has no VLAN to be keyed by.
| Option | Values | Default | Meaning |
|---|---|---|---|
--vlan |
VLAN id, e.g. 10 |
(none) | Restrict to a single VLAN's diagram. Mutually exclusive with --group-mode. |
--group-mode |
per-vlan, strict, topology |
per-vlan |
How to group VLANs that would render identically. per-vlan: one diagram per VLAN, no grouping. strict: group VLANs whose members, roles and priorities all match. topology: group VLANs whose members and roles match, ignoring configured priorities. Mutually exclusive with --vlan. |
--all |
flag | off | Write every resulting diagram (one per VLAN, or one per group under --group-mode) into output/hsrp/. |
As with stp, either --vlan or --all is required.
nettopo hsrp -i ./captures --vlan 10 # one diagram, VLAN 10 only
nettopo hsrp -i ./captures --all # one diagram per VLAN
nettopo hsrp -i ./captures --all --group-mode topology # grouped, priorities ignored
Filenames follow the same rule as stp: output/hsrp/hsrp_vlan10.drawio for a single
VLAN, output/hsrp/hsrp_vlans-10_20_30.drawio for a group.
A grouped diagram is rendered from the lowest VLAN in its group, and the virtual IP is
exactly what differs between the VLANs it covers — so the gateway node names the VLAN its
address belongs to (VLAN 10 group 10) rather than leaving the reader to assume the
address applies to all of them. hsrp.csv has the per-VLAN detail.
nettopo bgp, nettopo all — not yet implemented
These subcommands parse their arguments but every run currently exits with status 1 and
logs '<command>' is not implemented yet.. They land in Phases 6–7 — see
PROJECT_SPEC.md §14.
Preparing captures
The -i/--input directory holds one text file per device. Each file concatenates the
outputs of several show commands, each preceded by that device's prompt line
(hostname#show ...) — this is how nettopo both identifies the device as a source
device (as opposed to one only ever mentioned by a neighbor) and splits the file back
into per-command sections. tests/fixtures/captures/ has worked examples. Files are read
as utf-8-sig, so a leading UTF-8 BOM is handled transparently.
Which commands each subcommand needs
Nothing is mandatory: a missing command means the data it feeds is simply absent (empty CSV columns, missing links, a view with nothing to draw), never an error. The table below is what each subcommand needs to produce a complete result.
| Subcommand | Required | Optional, adds |
|---|---|---|
parse |
— | every command in the next table; each fills its own CSV table or columns |
l2 |
show cdp neighbors detail and/or show lldp neighbors detail |
show version (device naming) · show etherchannel summary / show port-channel summary (only for --link-mode port-channel) |
stp |
show spanning-tree and show cdp neighbors detail / show lldp neighbors detail |
show version (device naming) · show etherchannel summary / show port-channel summary (required when links are bundled) · show lldp neighbors detail (to identify a root bridge outside the captures) |
hsrp |
show standby brief |
show ip interface brief / show interfaces (each member's own SVI address on its node) · show version (device naming) |
bgp |
show ip bgp summary — not yet implemented (Phase 6) |
stpneeds CDP/LLDP too.show spanning-treereports a device's own bridge and port roles/states, but never says who is on the other end of a port — on its own it yields nodes and no edges. The STP view draws its links from the CDP/LLDP topology and labels each end with the spanning-tree state found there, so without a neighbor discovery command you get a diagram of disconnected boxes.
stpneeds the bundle table when links are bundled. The join between the two sources above is the interface name, and for a port-channel the two sources disagree: spanning-tree only ever saysPo1, CDP/LLDP only ever sayGi1/0/1.show etherchannel summary(orshow port-channel summary) is what maps one to the other. Without it, a fully bundled network produces the same disconnected boxes — every switch drawn, not one line between them.
What each command contributes, whichever subcommand you run:
show command |
Fills |
|---|---|
show version |
platform, model, os and serial in devices.csv, and the canonical hostname every neighbor's spelling is correlated onto — without it that name falls back to the prompt line, which is usually the same |
show cdp neighbors detail |
Links between devices (neighbors.csv, and the links in both the L2 and STP diagrams), plus each neighbor's role/icon, platform and management IP |
show lldp neighbors detail |
The same, for neighbors that don't speak CDP. Where both describe one adjacency, CDP wins |
show ip interface brief |
interfaces.csv: admin/operational state and IP per interface — including the SVI addresses the HSRP diagrams label their routers with |
show interfaces |
interfaces.csv: descriptions, precise IP/prefix, link state. Richer than show ip interface brief and wins where the two overlap |
show vlan brief |
vlans.csv |
show spanning-tree |
stp.csv and the STP diagrams: bridge IDs, base and effective priority, per-port role/state |
show standby brief |
hsrp.csv and the HSRP diagrams: each group's virtual IP, and each member's SVI, priority, role and preempt flag |
show etherchannel summary (IOS/IOS-XE) · show port-channel summary (NX-OS) |
po_id/po_members in interfaces.csv, and the bundles l2 --link-mode port-channel collapses links onto |
A capture covering everything implemented today:
sw1-access#show version
...
sw1-access#show cdp neighbors detail
...
sw1-access#show lldp neighbors detail
...
sw1-access#show ip interface brief
...
sw1-access#show interfaces
...
sw1-access#show vlan brief
...
sw1-access#show spanning-tree
...
sw1-access#show standby brief
...
sw1-access#show etherchannel summary
...
Three things that trip captures up
- Run each command with no arguments. A prompt line has to match the command in
full, so
show spanning-tree vlan 10is not recognized asshow spanning-treeand that section is skipped silently. Capture the unfiltered output and letnettopodo the per-VLAN split. - Abbreviations are fine.
show ver,show cdp neigh det,show ip int brandshow spanall match, so a capture taken by typing short forms works as-is. - One section per command per file. Only the first matching section is read; a command captured twice contributes its first occurrence only.
The prompt line drives all of this, so keep it — the filename is only a fallback for
naming a device whose file has no prompt line at all. Both hostname# and hostname>
work.
See PROJECT_SPEC.md §9 for the CLI design rationale.
Documentation
examples/— runnable capture sets, including the one behind the diagrams above.PROJECT_SPEC.md— scope, architecture, data model, CLI, delivery plan.docs/architecture.md— components, call flow, design decisions.CLAUDE.md— engineering conventions (workflow, coding principles, security review).CHANGELOG.md— release history.
Contributing
Work is tracked as GitHub issues, one per delivery-plan phase (see PROJECT_SPEC.md
section 14). Each change lands via a pull request from a feat/…/fix/… branch into
main; direct pushes to main are disabled. See CLAUDE.md for the full
workflow and coding conventions.
Security
This tool makes zero network connections by design — it only reads local capture
files. See PROJECT_SPEC.md for the
full OWASP-adapted security review.
License
Metadata
Release files for nettopo 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nettopo-0.5.0.tar.gz | 114.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nettopo-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 189.5 kB
Release files / nettopo-0.5.0.tar.gz
| Download URL | nettopo-0.5.0.tar.gz |
|---|---|
| Size | 114.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d049cde7e9ba5906f014f4fbbe0d3b87909845789c3a0afaf51f4a1572e8f812
|
|
BLAKE2b-256 checksum How to use checksums |
7c3b33b26d5646ba66d024be262207e22a3e8cb15a7875d425d99bfd2362a9be
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.
Transparency logRelease files / nettopo-0.5.0-py3-none-any.whl
| Download URL | nettopo-0.5.0-py3-none-any.whl |
|---|---|
| Size | 75.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
52c5464498cff7a521ca0090da4476a20d59cba5c416f9f44403edf2a0ee98a8
|
|
BLAKE2b-256 checksum How to use checksums |
25d6b3e995d75be6ba1f13a66fdf1f64256a06c6195125d01777dd9c84c4eee8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.
Transparency log