v0.3.5
Discovery on Windows no longer lists devices that have left the network. The desktop app stays responsive with results of 20,000 rows, and its CSV export now makes control characters safe the way the CLI does. MCP clients see progress on long scans and can cancel them, and Windows names Felix Stubner as the desktop app publisher.
Added
- The terminal UI honours
NO_COLOR, and the CLI honoursTERM=dumbandCLICOLOR_FORCE. The CLI's tables already went plain forNO_COLORand for output that is not a terminal. The terminal UI ignoredNO_COLORand always drew in 24-bit colour. It now draws without colour whenNO_COLORis set, keeping bold, and shows the selected row in/configreversed instead of shaded. The CLI also stops colouring forTERM=dumb, andCLICOLOR_FORCE=1keeps colour when the output is piped, forless -Ror a CI log.netscli --helpnow lists these, andNETSCLI_HISTORY, at the end.
- The MCP server reports progress and can be cancelled. Discover, port scans and sweeps send progress notifications to clients that ask for them, so a long sweep no longer looks stuck. A client that cancels a call now stops the scan, instead of it running to the end and holding one of the server's sixteen request slots.
Changed
- History is off unless you set
NETSCLI_HISTORY=1. Everydiscover,scan,inspect,sweep,dns,reverseandpcaprun, from the command line and the terminal UI, kept its whole result in~/.netscli/netscli.dbfor good, and nothing reads it back. Every other command created the file too, just by starting,completionsandmanincluded. NetsCLI now opens and creates nothing until the variable is set. With it set, history works as before, and on Linux and macOS the folder and file it creates are readable by you alone. A database an earlier version made is left where it is, and deleting it is up to you. Where no home folder can be found, the database and the terminal UI's default export folder no longer fall back to a.netsclifolder in the current directory. They say there is no home instead.
- Exit codes follow what happened.
0means the command worked,1that it ran and failed, and2that the command line was wrong. Until nowpingexited 0 when nothing answered,traceexited 0 whentracertortraceroutehad failed,doctorexited 0 with a dependency missing, and asking for two output formats exited 1 like a failed scan.pingandtracenow exit 1 in those cases,doctorexits 1 when libpcap is missing from a build that has packet capture, and two output formats, orping -c 0, exit 2 before anything runs.ping -c 0used to printloss=0.0%for a ping that sent nothing. Adns --record ALLwhere only some record types have records still exits 0. A script that ignored the exit code ofpingortracewill start to see 1, and the CLI page lists every code.
netscli mcp-service --installno longer installs a systemd unit. The unit could not work.netscli servespeaks MCP over stdin and stdout and stops when its input closes, and systemd gives a service no input, so the server exited at once andRestart=alwaysstarted it again every five seconds. An MCP client starts the server itself. The command now says so, points to the MCP setup guide, and exits 1. It also wrote the unit on Windows and macOS, where nothing could read it.--uninstalland--statusstay, on every system, to find and remove a unit an earlier version left behind. Runningmcp-servicewith no flag is now a usage error instead of a hint and exit 0.
- The terminal UI's
/discoverlooks up hostnames only when you add--resolve. It always looked them up, with no way to turn that off, where the CLI, the desktop app, the MCP server and/sweepall leave it out unless asked. Its summary line says which it did. The help for/sweeplisted--no-resolve, which does nothing, and not--resolve, which does. It now lists--resolve.
netscli pcapdoes not replace an existing capture file unless you add--force. It writescapture.pcapin the current folder by default and used to overwrite whatever was there, so a second capture quietly destroyed the first. A script that captures to the same name every time now needs--force.
- Enter on half a command name in the terminal UI finishes the name instead of running it.
/ethen Enter ran/export, the first suggestion, which writes a file, when the user was heading for/exit, and/dstarted a discovery of the default subnet. The name is now completed, asTabdoes, and a second Enter runs it.
- The desktop app stays responsive with large results. It drew every row of a result and redrew all of them on every keypress or scroll, so a full 4,096-port scan took about a tenth of a second per arrow key, and a 20,000-row result such as a long packet capture over half a second. It now draws only the rows on screen, so 4,096 rows respond as fast as 100, and 20,000 in about a twentieth of a second. Progress updates from a running scan are also sent ten times a second instead of once per port.
- Windows lists the desktop app's publisher as Felix Stubner. Installed apps showed "netscli", a default taken from the app's internal identifier, while the code signature, winget and the Microsoft Store listing all say Felix Stubner. Upgrading from an earlier version leaves one entry, under the new name.
- CSV files saved by the desktop app start with a UTF-8 byte-order mark. The app wrote plain UTF-8, and Excel on Windows reads a CSV that lacks the mark in its older code page, so a device called "Felix’s iPhone" would show as "Felix’s iPhone". Both CSV exports now begin with the mark, which is how Excel is told a file is UTF-8. The command line's CSV is meant for pipes and has none, and JSON exports are unchanged. A script that reads these files as plain UTF-8 will see the mark in front of the first header and needs
utf-8-sigor the equivalent.
- The desktop app's content security policy is tighter. It no longer allows images from data or blob URLs or from the asset protocol, and it forbids
<base>elements, form submission and plugin objects. The app uses none of them.
- The update notice says why an install cannot update itself. Installs from Scoop, the AUR or a .deb got the same "Update available" notice as everyone else, and its link opens the release page, which invites a manual download over a copy the package manager owns. The notice now carries the reason, for example "Installed with Scoop, so update it there".
- The update dialog says the app closes to finish. On Windows the app exits as the installer starts, and nothing said so if the installer was then declined or failed. The dialog now says NetsCLI closes to finish and should reopen by itself, and to open it again if it does not.
- The Packet Capture screen says no published installer includes it. It told people to use a PCAP-enabled desktop build, which reads as a download that does not exist. It now says the published installers are built without packet capture and that it needs a build made from source.
Fixed
- Text from the network could reorder or hide itself. The cleaning that makes a hostname, banner or
TXTvalue safe to print replaced control characters and let everything else through. That included right-to-left overrides, which make the text after them display backwards, and zero-width characters, which hide text or make two different names look the same. They are now replaced with.in the CLI's text, CSV and Markdown output, in service banners (which the desktop app and the MCP server share), in the computer name an SMB host reports, and in the terminal UI.--jsonand--yamlstill carry the text as it was received. A zero-width joiner inside an emoji in a name now shows as a dot as well.
- The terminal UI's
/export mdcould write an escape sequence into a file. The screen dropped control characters, but the export wrote the stored text as it was, so an mDNS name or aTXTvalue that carried an escape sequence ran when someone printed the file to a terminal. The text is now cleaned when a command's output is stored, and again when the file is written.
- The
scantable did not line up in a colour terminal. Rows were coloured first and padded second, and the colour codes count as characters, so a cell came out short and the rest of its row slid left. Output sent to a file or a pipe was always fine, which is why the tests never saw it.
- Pasting into the terminal UI on Linux and macOS ran commands. Typed in one key at a time, every pasted line break was a press of Enter, so pasting two lines ran the first and went on to the second. A paste now arrives as one piece of text with its line breaks turned into spaces. On Windows nothing changed, because the terminal library does not support it there.
- Terminal UI commands ignored words they could not read.
/ping host abcpinged four times,/mdns --timeout abcused 3000,/discover a bdropped theb, and/arp add 1.2.3.4with no MAC showed the ARP table. Each now answers with a usage error. Worst was/pcap eth0 --filter tcp port 80, which captured with the filtertcp, so every TCP packet, and said nothing./pcapnow reads quotes, as in--filter "tcp port 80", and says to use them when a filter has spaces and none./mdnsalso takes--timeout-ms, the CLI's name for it, and/arptakesdeleteas well asdel.
/export --outputin the terminal UI mangled Windows paths and ignored~. The path went through a splitter that treats a backslash as an escape, soC:\Users\me\out.mdbecameC:Usersmeout.md, a path relative to drive C. And~/scans/out.mdmade a folder named~. A backslash is now an ordinary character, and a leading~is your home folder.
- A crash in the terminal UI left no message. The panic message went to the alternate screen, which is thrown away when the UI closes, so a crash looked like the program closing by itself with exit code 101. The terminal is now put back first, so the message is where you can read it.
netscli ... | headended in a panic message. When the reader closed the pipe,scan 127.0.0.1 -p 1-4096 --json | headprintedfailed printing to stdoutand a panic, and exited 101. A reader that stops early now just ends the output, with exit 0, for the machine-readable formats and the large reports (scan,discover,sweep,inspectandpcap --read).
netscli servecould keep running for minutes after its client had gone. When the client closes its end, the server drops every request it was working on. A ping or capture already running on a blocking thread cannot be dropped, and the process waited for it, up to the request's own timeout, which the MCP tools allow to be ten minutes. The server now ends the process as soon as it has stopped, with every response already written.
netscli scan <name>did not say which address it scanned. A name can have several, the scan used the first, and nothing in the output said which. The text header now readsexample.com (93.184.216.34). The JSON is the same as before.
doctorsaid a standard build had libpcap installed. Standard builds have no packet capture, anddoctoris how people find that out, but it printed a tick. It now reports libpcap as not installed and not compiled in, and still exits 0.setupno longer offers to install it for such a build, where it could not help. Andsetup --print, which is meant to print only, no longer writes~/.netscli/config.json.
- Smaller things in what the CLI and the terminal UI print.
/inspectshowed the round-trip time as(RTT: Some(3)), left out a MAC address unless its maker was known, and never showed the host name.- The terminal UI's status line said
host n/ain most Linux and macOS terminals, because it read$HOSTNAME, which zsh does not export. It asks the system. - In the discover table a long vendor name pushed the hostname column out of line, a name with an accent was measured in bytes, and a single host was "1 hosts".
sweephad the same plural. mdns --timeout-ms 999999waits 30 seconds and then said nothing was found within 999999ms.--helpsaid-jdefaults to 256 when/configcan change that, gave_http._tcpas an mDNS type when only_http._tcp.local.works, and gave a PowerShell completions example that wrote to a file path that is not one.
- The CLI and terminal UI pages match the program. The CLI page showed scan output from before 0.3.4, and did not list the exit codes, the environment variables or the limits that cut a value to fit. The operations page said
inspectwith no ports is a host-only check, when it checks 22, 80 and 443. The terminal UI page described the real screen with an illustration that did not match it, and listed neither the keys nor/arp add.
- Discovery on Windows listed devices that had left the network. The Windows device table keeps an entry for a while after its device goes, and discovery reported those entries as devices. On one network that was 4 of 26, none of which answered anything afterwards. Devices found only in the table are now asked again directly, which can add about 2 seconds and only when the table holds such entries, and are listed only if they answer. Devices that ignore ping still answer, so they are still found.
- The desktop app's CSV export defuses control characters, as the CLI's does. A banner containing an escape sequence went into the file as it was, and a cell such as a tab followed by a number skipped the guard that stops a spreadsheet reading it as a formula. Both exports are now tested against the same list of cases.
- The desktop app no longer leaves a program running after looking for the CLI. To offer MCP setup it runs each
netscliit finds with--versionand gives up after 3 seconds. A program still running at that point was left behind. It is now stopped.
- Console windows no longer open when the desktop app looks up names or traces a route on Windows. Discover and Sweep run
ping -afor every host that answers, Trace Route runstracert, and Settings runsnetscli --versionfor each candidate it finds. The app has no console of its own, so each of those opened one. With Windows Terminal as the default terminal that is a window titled with the tool's path, open for as long as the tool runs. Called from a program with no console, a name lookup opened that window, and with the fix it opened none. The ARP commands already started this way.
- A route trace no longer fails on a Windows set to another language. On a Windows in a language with accented letters, such as German or French,
tracertcan print a byte that is not valid UTF-8, and reading its output as UTF-8 ended the whole trace with "trace stdout read failed", hops already printed included. The output is now decoded leniently, so the odd character shows as a replacement mark and the trace completes.
- A trace refuses a target that the trace tool would read as an option. The target goes to
tracert,tracerouteortracepathas a plain argument, sonetscli trace -- -dhanded the tool a flag. A target that is empty or starts with-or/now gets an error before anything runs. A host name or an address never starts with either, and Windows'tracertreads a leading/as an option too.
- File dialogs in the desktop app no longer freeze the window. Open Result Bundle, Choose Folder and the Save dialog for exports waited for the dialog on the thread that draws the window, which the dialog library says not to do. On Windows, Open Result Bundle left the window unable to answer anything for as long as its dialog was open, and Windows marked it as not responding after about five seconds. The others wait the same way. They now wait off that thread, and the window keeps answering.
- A stalled update download no longer traps the update dialog. The dialog cannot be closed while an update downloads, and nothing limited how long a download could take, so one that stalled kept the dialog open until the app was quit. The update check now gives up after 30 seconds and the download after 10 minutes, and the dialog then shows its message and a link to the release page.
- The desktop app announces failed runs and progress to screen readers. A failed run's message and the progress bar were silent at the default settings, because the only live regions were the toasts, which are off by default. The error message is now an alert, and a hidden status line says when a run starts, at each quarter of the way, and when it finishes with its summary or is stopped.
- Escape no longer stops a scan when it only closes a dialog or menu. Closing the About dialog, the update dialog or a context menu with Escape also cancelled the scan running underneath it. Escape now closes what is open and leaves the scan alone.
- A damaged save-settings file no longer stops every export. One unreadable
gui-save-settings.jsonmade every export and capture fail until the file was deleted by hand, and the Settings controls could not repair it. It now reads as the defaults, and the next change writes a good file over it. The file is written through a temporary one, so an interruption cannot leave it half written.
- A damaged result file no longer crashes the desktop window, and the error screen is usable. A result bundle whose entries lacked fields passed the checks and then broke the table while it was drawn, which ended the session. Such a file is now refused with a message. The error screen was black on near-black on a light theme and gave no way to move or close the window. It is now readable and has the window buttons.
- The command the desktop app copies can no longer carry a shell command from a result file. The command strip, its copy button and the History entry are built from the tab's form values, and opening a result file fills those from the file. A host such as
1.1.1.1 && calcwas one click from the clipboard as a working command. Values with spaces or shell syntax are now quoted as one argument, and a value no quoting can make safe (one with a$, a backtick, a double quote, a%or a!in it) is left out. A capture filter containing a double quote is now left out too, where it was escaped before.
- Smaller accessibility fixes in the desktop app. Error toasts stay until they are dismissed instead of leaving after under two seconds. The tab close buttons no longer add a Tab stop each to the tab strip. Workspace search tells a screen reader which result is current. The update dialog is no longer read out again at every step of a download. The tab spinner stops turning when the system asks for reduced motion.
- MCP clients built on the official SDK can connect. Every reply the MCP server sent carried both a result and an error, one of them empty, which the protocol doesn't allow. The official TypeScript SDK drops a reply like that, so a client built on it waited for the answer to its first message and timed out. Replies now carry one or the other, and CI connects to the server with the SDK whenever the code changes.
- Cancelling an MCP call works when the server is busy. When a 17th call arrived with 16 running, the server stopped reading what the client sent until one finished. A cancel, which is how a client frees a slot, went unread, and so did the client closing the connection. The server now keeps reading. Up to 16 more calls wait for a slot, and any past that are refused at once with an error.
- Cancelling or disconnecting stops an MCP packet capture. A capture ran on after its call was cancelled, timed out or lost its client, for up to an hour, and gave its slot back straight away, so cancelling and restarting got past the limit of four captures. It now stops, and gives the slot back once it has. Background captures stop when the client goes too. In a test on Windows, a server whose client left mid-capture exited within 0.3 seconds, where before it was still running after 20. A
capture_pcapcall with onlymaxPacketscould also run for an hour. It now stops after the 10 seconds the tool advertises.
- mDNS discovery no longer leaves its listener running. A service type without its final dot, such as
_http._tcp.local, or a cancelled MCP call left the listener running in the background, its network sockets open and any searches it had started still repeating, until the program exited. It now always stops.
- An MCP result too large to send keeps its file path and counts. A result over 1 MiB that wasn't a plain list was replaced by an error, so a large packet capture lost its file name, packet count and job status along with its packets. Now only its longest list is cut to fit. A background capture also reports its file name as soon as it starts.
- Smaller MCP server fixes.
- The server answers
ping, which clients use to check that a server is alive. It answered with an error. - It agrees a protocol version it supports, instead of whatever version the client asked for.
- Text from scanned hosts in HTTP headers and mDNS records is cut to 256 characters, as banners already were.
discover_mdnstakes at most 32 service types, and browses each once.- An over-long request line no longer has its tail read as the next message.
- A request that reuses the id of one just answered can always be cancelled. A race could leave it impossible to cancel.
- The server answers
- Smaller scan fixes.
- A port behind a firewall that rejects connections with an ICMP message reads as filtered, as nmap reports it, instead of as an error.
- The HTTP probe sends IPv6 targets a valid
Hostheader, with the address in brackets. The old one was malformed, and a strict server can refuse it. - A stray
data/oui.jsonin the folder netscli ran from no longer replaces or empties the vendor list. - On a network wider than /16, the default /24 is the one around this machine's address on that network. It could come from another interface or fall back to 192.168.1.0/24.
- Windows discovery re-checks at most 64 devices at once. Checked all at once, a very long device table could drop devices that were there.
- The example in the
netscli-coreREADME compiles.
Security
- MCP packet captures never overwrite a file or follow a symlink.
capture_pcapwrotecapture.pcapin the server's working directory, often your project, and replaced any file of that name. On Linux and macOS, acapture.pcapsymlink in a repository could send the capture through to wherever it pointed, as root if the server ran as root. Captures now create a new file and refuse a name that is taken, symlinks included, and withoutoutputFileeach capture gets a new name.
- DNS lookups no longer go to Cloudflare behind your back. When your own DNS servers could not answer a lookup, including when a name simply had no records of the type asked for, NetsCLI asked Cloudflare's public resolver (1.1.1.1) the same question. That sent names you looked up to a third party, unencrypted and without saying so, and the privacy page said the opposite. It affected
dnsand every command that turns a host name into an address, in the CLI, the terminal UI, the desktop app and the MCP server. Lookups now go only to the DNS servers your computer is set up to use. Some home routers refuse record types other than A and AAAA, anddnsnow reports that refusal instead of quietly asking someone else.NETSCLI_DNS_FALLBACKno longer does anything. The privacy page now says what 0.3.4 and earlier did.