Skip to content

Latest commit

 

History

History
253 lines (196 loc) · 12.5 KB

File metadata and controls

253 lines (196 loc) · 12.5 KB

AGENTS.md - NetsCLI

Instructions for AI coding agents working on this codebase.

Project Overview

NetsCLI is a cross-platform network scanning and diagnostics toolkit in Rust. Four interfaces share one core library: interactive TUI, CLI subcommands, desktop GUI (Tauri 2 + React 19), and an MCP server for AI agent integration.

Repository Layout

netscli/
  crates/
    netscli-core/       # Core networking library (all scan/discovery logic)
    netscli-mcp/        # MCP server (JSON-RPC 2.0 over stdio)
  apps/
    netscli-cli/        # CLI binary + TUI (ratatui/crossterm)
    netscli-gui/        # Desktop GUI (Tauri 2 + React 19 + TypeScript)
      src/              #   React frontend
      src-tauri/        #   Tauri Rust backend
  scripts/              # OUI generator, install scripts (excluded from workspace)
  site/                 # Astro/Starlight landing page + docs (netscli.com)
  docs/                 # Repo-internal docs (ARCHITECTURE, PUBLISHING, RELEASE) + assets
  packaging/            # Distribution manifests and installer templates

The OUI dataset ships inside the core crate at crates/netscli-core/data/oui.min.json.gz.

Dependency flow: the netscli package (at apps/netscli-cli/) and netscli-gui/src-tauri depend on netscli-core. netscli-mcp wraps netscli-core. The netscli package also depends on netscli-mcp.

Crate name vs source directory: the CLI source lives at apps/netscli-cli/ to match the other workspace members (-core, -mcp, -gui), but the Cargo package is just netscli — so end users run cargo install netscli (matching the produced binary). Internally we use -p netscli.

For ownership boundaries and compatibility rules, see docs/ARCHITECTURE.md.

Build, Test, and Development Commands

# CLI/TUI (binary name: netscli)
cargo build -p netscli
cargo build -p netscli --features pcap   # with packet capture
cargo build --release -p netscli          # optimized build

# GUI -- use one of these two. See the warning below before reaching for
# `cargo build -p netscli-gui`.
cd apps/netscli-gui && npm install && npm run tauri:dev
cd apps/netscli-gui && npm run tauri build    # build installer

# Tests
cargo test --all                              # all workspace tests
cargo test -p netscli-core                    # targeted test run
./scripts/test-pcap.ps1                       # Windows PCAP tests with Npcap SDK env
cd apps/netscli-gui && npm run test:unit      # GUI helper tests
cd apps/netscli-gui && npm run build          # GUI typecheck + Vite build
cd apps/netscli-gui && npm run test:tauri-render  # Tauri render automation

# Linting & formatting
cargo fmt                                     # apply rustfmt
cargo clippy --all-targets -- -D warnings     # lint with warnings as errors
cd apps/netscli-gui && npm run lint           # ESLint for the GUI frontend

# OUI database refresh
cd scripts && cargo run --bin generate-oui

Running the desktop app

cargo build -p netscli-gui does not produce a runnable app. It produces a dev binary that loads devUrl from tauri.conf.json -- http://localhost:1420 -- rather than embedding the frontend. Launching target/debug/netscli-gui.exe on its own opens a window showing the browser's connection-error page, with the app's own title replaced by localhost. There is no message saying why, and the process runs and stays up, so it looks like it worked.

Use npm run tauri:dev. It runs the Vite dev server and the app together, which is why this never bites when you follow the command above.

If you do launch the binary directly -- the render-automation harness in e2e/tauri-render/ does -- Vite has to already be listening on 1420. How to tell the difference from outside the window:

# Blank window: the page is the dev URL and the title is the host, not the app
curl -s http://127.0.0.1:<debug-port>/json | grep -o '"title":"[^"]*"'
#   "title":"localhost"   -> dev server is down
#   "title":"NetsCLI"     -> the UI is up

One more trap in the same area: a frontend-only change does not make cargo build relink, so Finished in 1.35s after npm run build means the binary was not updated. That does not matter under tauri:dev, which serves the frontend live -- but it does matter for anything running the binary directly.

Toolchain

  • Rust 1.96.0 pinned in rust-toolchain.toml (includes rustfmt + clippy)
  • MSRV: 1.96
  • Cargo resolver: v2
  • GUI frontend: Node.js with npm, Vite, TypeScript

Key Dependencies

Crate/Lib Role
tokio (full) Async runtime, used everywhere
serde + serde_json Serialization throughout
serde_yaml_ng YAML output in CLI
anyhow Error handling in apps
thiserror Typed errors in library crates
sqlx (sqlite, async) Database for host records and scan history
pnet_packet/transport/datalink Raw networking
hickory-resolver Async DNS resolution
pcap Packet capture (optional feature)
clap 4.6 (derive) CLI argument parsing
ratatui 0.30 + crossterm 0.29 Terminal UI
ratatui-textarea 0.9 TUI input editing (replaced tui-textarea)
tauri 2.0 Desktop GUI framework
React 19 + Vite + TypeScript GUI frontend

Architecture Rules

Where to put new code

  • Network logic (scanning, pinging, DNS, etc.): crates/netscli-core/src/
  • New network operations: Add to the ops.rs facade and the matching ops/ family module so all interfaces get it
  • CLI subcommands: apps/netscli-cli/src/args.rs (clap) + cli_dispatch/ (handler)
  • TUI commands: apps/netscli-cli/src/tui/events/
  • TUI output formatting: apps/netscli-cli/src/tui_formatter/
  • CLI text output: apps/netscli-cli/src/cli_formatter/
  • MCP tool exposure: crates/netscli-mcp/src/server/
  • GUI backend commands: apps/netscli-gui/src-tauri/src/commands/
  • GUI frontend: apps/netscli-gui/src/

Do not add GUI-only, TUI-only, CLI-only, or MCP-only network logic. Interface layers should call netscli-core or expose a missing core operation through Ops.

Error handling pattern

  • Library crates (netscli-core): use thiserror for typed error enums
  • Application crates (netscli-cli, netscli-gui): use anyhow::Result
  • MCP server: JSON-RPC error codes (-32600 to -32603)

Async pattern

All network operations are async (tokio). Long-running operations accept progress callbacks for UI updates. Use tokio::spawn for concurrent work within operations.

Platform-specific code

  • Guard with #[cfg(unix)] / #[cfg(windows)]
  • Windows: ipconfig crate for interfaces, windows-sys for WinSock, tracert command
  • Unix: pnet_datalink/pnet_sys for raw sockets
  • Traceroute shells out on every platform (crates/netscli-core/src/trace.rs): tracert on Windows, traceroute with a tracepath fallback elsewhere. There are no manual ICMP TTL probes.

Core Modules (netscli-core/src/)

File Purpose
lib.rs Public API re-exports
ops.rs High-level operations facade (used by CLI, TUI, GUI, MCP)
common.rs Default constants (ports, timeouts, concurrency)
discover.rs Subnet host discovery (ping + DNS resolve)
scan/ TCP and UDP port scanning with concurrency
ping.rs ICMP/TCP ping with dual backends (raw ICMP + TCP fallback)
arp.rs ARP table retrieval + MAC vendor lookup
oui.rs MAC vendor database (compressed gzip JSON)
dns.rs DNS lookup (A, AAAA, CNAME, MX, NS, TXT, SRV, PTR, SOA, CAA)
inspect.rs Combined host analysis (ping + scan + resolve)
sweep.rs Full network sweep (discover + scan all hosts)
trace.rs Traceroute; shells out to the platform tool
mdns.rs mDNS/DNS-SD device discovery (behind mdns feature flag)
pcap.rs Packet capture (behind pcap feature flag)
stats.rs Real-time traffic monitoring (sysinfo)
db.rs SQLite persistence (hosts table, scan_history table)
error.rs Typed error enum shared by the core

CLI App Files (apps/netscli-cli/src/)

File Purpose
main.rs Entry point and TUI launcher
args.rs Clap argument definitions and subcommand enums
cli_dispatch.rs + cli_dispatch/ Subcommand dispatch and per-command handlers
commands.rs Per-command business logic shared by the CLI and TUI
output.rs + output/ --json/--yaml/--csv/--md output-format selection; output/rows.rs reads a serialized result back as rows, which csv.rs and markdown.rs render
tui/ Interactive TUI (state, events, runtime, widgets, command catalog)
tui_formatter.rs + tui_formatter/ TUI output formatting (ratatui Spans/Lines)
cli_formatter.rs + cli_formatter/ Plain-text CLI output formatting
tui_settings.rs TUI config persistence (~/.netscli/tui-settings.json)
tui_export.rs Session export (Markdown/JSON)
trace.rs Re-export of netscli_core::trace_route; the implementation lives in the core
setup.rs + setup/ First-run dependency wizard
mcp_service.rs Systemd service management (Linux)

Safety Limits

Enforced in ops/validation.rs (subnet size) and common/ports/ (port count), and re-checked inside the engines themselves so a direct engine call cannot bypass them. Do not weaken without discussion.

  • Max subnet size: /16 (65,536 hosts)
  • Max port count per scan: 4,096
  • Default concurrency: 256 simultaneous connections
  • Default timeouts: 1000ms ping, 500ms scan, 1500ms DNS

Feature Flags

  • pcap — Enables packet capture (requires libpcap/Npcap at runtime)
    • Must be enabled on all three: netscli-core, netscli-mcp, netscli-cli
    • Chain: netscli-cli/pcap → netscli-core/pcap + netscli-mcp/pcap → netscli-core/pcap
    • Off by default everywhere, including the published installers
  • mdns — Enables mDNS/DNS-SD discovery. Pure Rust (mdns-sd), no system dependency. Off in netscli-core's defaults, but on in netscli-mcp's and enabled explicitly by netscli, so every published build has it
  • db — Enables the db module and Database type (SQLite via sqlx). Off in netscli-core's defaults, enabled by netscli

Coding Style & Conventions

  • Rust: cargo fmt defaults, no custom rustfmt config
  • Prefer idiomatic Rust naming: snake_case modules/functions, CamelCase types
  • Crates use kebab-case names (e.g., netscli-core)
  • GUI frontend: TypeScript with React functional components, no class components
  • GUI lint: ESLint via apps/netscli-gui/eslint.config.js (npm run lint)
  • Output formats: CLI subcommands support --json and --yaml flags; list-shaped ones also take --csv and --md

Testing

  • Rust tests live under crates/**/tests/ and apps/netscli-cli/tests/
  • Prefer targeted runs while iterating: cargo test -p netscli-core
  • Add tests alongside the relevant crate when changing core logic
  • SQLite tests should use tempfile crate, not real user data
  • Keep public Rust APIs, CLI syntax, MCP schemas, Tauri command payloads, GUI data shapes, and SQLite schema stable during refactors unless the task explicitly asks for a public change

CLI Subcommands

discover, scan, inspect, sweep, ping, trace, dns, reverse, arp, interfaces, mdns, serve (MCP server), mcp-service, completions, man, setup, doctor — plus pcap when built with the pcap feature

config and export are TUI slash-commands only (/config, /export); they are not CLI subcommands. The authoritative list is the Commands enum in apps/netscli-cli/src/args.rs.

MCP Tools (9 default, 13 with pcap feature)

discover_network, scan_ports, ping_host, dns_lookup, get_arp_table, inspect_host, sweep_network, list_network_interfaces, discover_mdns — plus capture_pcap, start_pcap_capture, get_pcap_capture_status, get_pcap_capture_result when built with the pcap feature

Common Pitfalls

  • Network operations require appropriate OS permissions (admin/root for raw ICMP sockets)
  • The pcap feature requires libpcap (Linux/macOS) or Npcap (Windows) installed at runtime
  • SQLite database is created lazily at ~/.netscli/netscli.db
  • OUI database loading is lazy (OnceCell) — first ARP/vendor lookup triggers decompression
  • Windows and Unix code paths differ significantly in arp.rs, ping.rs, and trace.rs
  • User config directory: ~/.netscli/
  • OUI database path: NETSCLI_OUI_PATH env var or fallback paths

Commit Guidelines

  • Use clear, imperative commit subjects (e.g., "Add scan timeout flag")
  • PRs should describe user-facing impact, mention affected commands/modules, and note any required setup