UbhiTS/netryx

GitHub: UbhiTS/netryx

Stars: 0 | Forks: 0

# Netryx — Signal Cartography A feature-rich, **web-based** network scanner and discovery tool. It finds every device on your network, identifies what each one is, maps their open ports and services, and gives you a **clickable URL** for anything running a web interface. The UI is a polished dark "constellation atlas" dashboard with card, table, and interactive topology-map views. The engine is a single Python file with **zero external dependencies** (standard library only). It runs a small local web server and serves the dashboard in your browser. Nothing is sent to the cloud — all scanning and data stay on your machine. It's also **scriptable and agent-ready**: a one-shot JSON CLI, a Model Context Protocol (MCP) server for AI assistants, a documented HTTP/OpenAPI surface, and proactive rogue-device alerts. See [Automation & AI agents](#automation--ai-agents). ## Files | File | Purpose | |------|---------| | `netryx.py` | The scanning engine + local web server + HTTP/MCP API | | `ui.html` | The dashboard (served by the engine — **keep it next to `netryx.py`**) | | `netryx_mcp.py` | Stdio MCP server for AI agents (keep next to `netryx.py`) | | `openapi.yaml` | OpenAPI 3.0 description of the HTTP API (also served at `/openapi.json`) | | `run.bat` | Double-click launcher for Windows | | `build.bat` | Builds a standalone `Netryx.exe` (bundles `ui.html`) | | `Dockerfile`, `docker-compose.yml`, `.dockerignore` | Run it as a container on a NAS/server | | `DESIGN_PHILOSOPHY.md`, `signal_cartography.png/.pdf` | The visual design language behind the UI | ## Why it's "a local web app" and not pure in-browser JavaScript A scanner running *entirely* inside a browser tab physically cannot probe your LAN — browsers sandbox raw network access for security (no ICMP ping, no ARP, no arbitrary TCP scans). Netryx uses the standard, capable design: a tiny local engine does the real scanning and serves the web UI you open in your browser. ## Option A — run locally (easiest, no install) python netryx.py # launch + open browser python netryx.py --port 9000 # choose a port python netryx.py --no-browser # don't auto-open the browser python netryx.py --host 0.0.0.0 # listen on all interfaces (use with care) python netryx.py --scan 192.168.1.0/24 --json # one-shot scan, no server ## Option B — prebuilt single-file app (no Python needed) Every release ships self-contained binaries (UI **and** MCP server bundled in) on the [Releases page](../../releases/latest) — grab the one for your OS: | Platform | Asset | Run it | |---|---|---| | **Windows** | `Netryx.exe` | double-click (unsigned → SmartScreen *More info → Run anyway*) | | **macOS** (Intel & Apple Silicon) | `netryx-macos-universal.tar.gz` | `tar -xzf … && xattr -dr com.apple.quarantine netryx && ./netryx` | | **Linux** (any distro) | `Netryx--x86_64.AppImage` | `chmod +x …AppImage && ./…AppImage` | | **Linux** (Ubuntu/Debian) | `netryx__amd64.deb` | `sudo apt install ./netryx_*_amd64.deb` then run `netryx` | | **Linux** (portable) | `netryx-linux-x86_64.tar.gz` | `tar -xzf … && ./netryx` | The binaries are unsigned. macOS Gatekeeper needs the quarantine flag cleared (the `xattr` above) or a right-click → **Open** the first time; for fully frictionless launch you'd need Apple notarization. Data is stored per-user (`%APPDATA%\Netryx`, `~/Library/Application Support/Netryx`, `~/.local/share/Netryx`); set `NETRYX_DATA` to override. To build the Windows `.exe` yourself, run **`build.bat`** on a Windows box with Python (PyInstaller can't cross-compile, which is why each OS is built on its own runner in CI). ## Option C — Docker on your NAS / server The app is container-ready. From this folder: mkdir -p netryx-data sudo chown -R 10001:10001 netryx-data # see "data directory ownership" below docker compose up -d --build Then open **`http://:8765`** from any browser on your network. **Host networking is required.** A bridged container sits on its own virtual network and cannot see your real LAN — no device discovery, no ARP, no mDNS/SNMP. The provided `docker-compose.yml` sets `network_mode: host` and adds the `NET_RAW` capability so ICMP ping works. **You do not need `--privileged`.** Netryx only uses ordinary sockets and the `ping` command — it never reconfigures the network or sends raw packets. `NET_RAW` is the only capability it benefits from (for ICMP), and even that is optional: without it, discovery still works via TCP connect probing — you just lose ICMP ping and the TTL-based OS guess. (`NET_ADMIN` is not needed.) - On **Synology** (Container Manager) or **QNAP** (Container Station), import this project and make sure the container uses **host** network mode. If your NAS UI won't allow host mode, the app will still load but device discovery will be limited to the container's own network. - Data (scan history, device names/notes, the downloaded vendor DB, baseline and events) persists in `./netryx-data` on the host via the mounted volume. That folder must be owned by uid **10001** (the container user) — see the ownership note above. - Change the port with the `NETRYX_PORT` env var if 8765 is taken. Plain `docker` equivalent: docker build -t netryx . mkdir -p netryx-data && sudo chown -R 10001:10001 netryx-data docker run -d --name netryx --network host \ --cap-add NET_RAW \ -e NETRYX_PORT=8765 -v "$PWD/netryx-data:/data" \ --restart unless-stopped netryx ## Features **Discovery & identification** - Auto-detects your subnet (editable — scan any CIDR, e.g. `10.0.0.0/24`) - Concurrent ping sweep **plus a TCP fallback**, so it finds devices that block ping - MAC address resolution from the ARP table - Vendor lookup from the MAC, with a **one-click "Download full" button** that fetches the complete IEEE OUI database for exhaustive vendor names - Reverse-DNS hostnames - **mDNS / Bonjour** discovery — surfaces Chromecasts, AirPlay, printers, Apple devices, Sonos, HomeKit, etc., with friendly names and service types - **SNMP** (v2c) queries managed switches, printers and access points for their system name and description - **NetBIOS** and **SSDP/UPnP** probing for Windows names and smart-device models - OS guess (TTL) and device-type guess (ports + vendor + mDNS + SNMP) - Round-trip latency **Ports, services & exposure** - Parallel TCP connect scanning — **Quick** (~90 ports), **Extended** (1–1024), **Full** (1–65535) — with service names and banner grabbing - **Web URL detection** — HTTP/HTTPS ports become clickable links that open the device's web UI (80 → `http://ip`, 443 → `https://ip`, plus 8080/8443/8123/…) - **Exposure scoring** — each device gets a risk tier (none → critical) based on risky open ports (Telnet, RDP, SMB, VNC, exposed databases, unauth Docker, …) **Views & workflow** - **Table** (default), **Cards**, and an interactive **Topology map** with multiple layouts and Obsidian-style floating physics - Search, filter (web-only / open-ports / new / named), and sort - **Live monitoring**: auto re-scan on a timer with **new-device detection** and **desktop notifications** (browser Notification API) - **Scan history + change detection** — every scan is saved; reload and compare - **Wake-on-LAN**, **custom names/notes** per device, **CSV/JSON export** ## Automation & AI agents Everything below is pure standard library — no extra installs. ### One-shot CLI scan (no server) Run a single scan and print the result — perfect for cron jobs and scripts: python netryx.py --scan 192.168.1.0/24 # table output python netryx.py --scan 192.168.1.0/24 --json # machine-readable JSON python netryx.py --scan 192.168.1.0/24 --ports --profile quick --json python netryx.py --scan "10.0.0.0/24, 10.0.5.10" --no-snmp --no-mdns Every device carries a `risk` assessment (`none`/`low`/`medium`/`high`/`critical`) derived from its open ports. Exit code is non-zero if the targets are invalid. ### MCP server for AI agents (stdio) `netryx_mcp.py` is a [Model Context Protocol](https://modelcontextprotocol.io) server, so assistants like Claude can scan and reason about your network. Point your MCP client at it: command: python args: ["/full/path/to/netryx_mcp.py"] Example `claude_desktop_config.json`: { "mcpServers": { "netryx": { "command": "python", "args": ["C:\\path\\to\\netryx_mcp.py"] } } } Tools exposed: `network_info`, `scan_network`, `list_devices`, `get_device`, `find`, `whats_new`, `exposure_report`, `scan_ports`, `wake_device`, `name_device`, `scan_history`, `get_baseline`, `set_baseline`, `check_rogues`, `recent_events`. The MCP server shares Netryx's data directory, so it sees the same scan history your web UI produces. ### Remote MCP over HTTP The web server also speaks MCP at `POST /mcp` (JSON-RPC 2.0), so a remote agent can reach a Netryx running on your NAS. Authorize it with an **API token** (create one in the dashboard under **Settings → API tokens**, or use the legacy `NETRYX_TOKEN` env var): curl -X POST http://nas:8765/mcp \ -H "Authorization: Bearer nsk_your_token" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' See [Security & access control](#security--access-control) for the full picture. A `?token=` query parameter is also accepted (avoid it where requests get logged). ### OpenAPI The full HTTP API is described at `GET /openapi.json` and `GET /openapi.yaml` (also committed as `openapi.yaml`). Load it into Swagger UI, Postman, or an agent's tool layer. ### Proactive monitoring: baseline + rogue alerts Approve your current network as a known-good **baseline**, then every later scan is diffed against it. The first time an **unapproved device** or an **unapproved open port** appears, Netryx logs an event and (optionally) pushes it out. - Manage the baseline: `POST /api/baseline {"action":"set"|"approve"|"clear"}`, or the `set_baseline` MCP tool. - Read recent events: `GET /api/events`, or the `recent_events` MCP tool. - Deliver alerts to a **webhook** (HTTP POST) and/or **MQTT** by setting env vars (below). Each alert fires once (de-duplicated) until you re-approve the baseline. Pair this with **Live monitoring** in the UI (or a cron'd `--scan`) for continuous rogue-device detection with push notifications. ### Real-time events & push Netryx emits events the moment they're detected and pushes them over four transports, all fed by one event log. Every event has the shape: { "id": 42, "time": 1700000000.0, "kind": "rogue_device", "severity": "critical", "data": { "ip": "192.168.1.55", "name": "...", "open_ports": [23, 445] } } Event kinds (and severity): | kind | severity | fired when | |------|----------|-----------| | `rogue_device` | critical | a device not in the known-good baseline appears | | `new_open_port` | high | an unapproved port opens on a baselined device | | `exposure_alert` | high | a device reaches the **critical** risk tier (Telnet/RDP/SMB/exposed DB, …) | | `device_missing` | warning | a baselined device disappears (reserved) | | `scan_complete` | info | a scan finishes (carries device/new/new-port counts) | Consume them however suits the client: `GET /api/events?limit=N` returns the recent log for pull-based clients. The dashboard's **Events** panel subscribes to the SSE stream and surfaces high/critical alerts as live toasts (and desktop notifications if enabled). ### Environment variables | Variable | Purpose | |----------|---------| | `NETRYX_HOST` | Bind address (default `127.0.0.1`; Docker uses `0.0.0.0`) | | `NETRYX_PORT` | Port (default `8765`) | | `NETRYX_NO_BROWSER` | Don't auto-open a browser | | `NETRYX_DATA` | Data dir (history, names, vendor DB, baseline, events, tokens) | | `NETRYX_USER` | Admin username (default `admin`); seeds first launch | | `NETRYX_PASS` | Seeds/overrides the admin password (default login is `admin`) | | `NETRYX_TRUST_LOCALHOST` | `0` (default) prompts everywhere; `1` skips auth for `127.0.0.1` | | `NETRYX_OPEN` | `1` disables auth entirely (trusted segments only) | | `NETRYX_SECURE_COOKIES` | `1` adds `Secure` to the session cookie (set when behind an HTTPS proxy) | | `NETRYX_SESSION_DAYS` | Login session lifetime in days (default `30`) | | `NETRYX_TOKEN` | Legacy static bearer token (managed tokens in the UI are preferred) | | `NETRYX_WEBHOOK` | URL to POST events to | | `NETRYX_MQTT` | MQTT broker `host` or `host:port` | | `NETRYX_MQTT_TOPIC` | MQTT topic (default `netryx/events`) | | `NETRYX_MQTT_USER` / `NETRYX_MQTT_PASS` | MQTT credentials (optional) | ## Security & access control Netryx is **secure by default**. On first launch it creates an admin login of **`admin` / `admin`** and requires it for the **whole** app — the dashboard, every `/api/*` endpoint, and `/mcp`. Change it immediately under **Settings** in the dashboard. **Sign in:** - **Humans** — sign in on a styled **login page** (a session cookie keeps you signed in; **Sign out** lives in the dashboard header). Change the username/password under **Settings → Admin login**; the new credentials are hashed (PBKDF2) and persisted to `netryx-data/auth.json`, so they survive restarts — and changing the password no longer signs you out. You can also seed the initial password with `NETRYX_PASS` (and `NETRYX_USER`), which additionally works as a recovery/override login. - **Agents & scripts** — create **API tokens** under **Settings → API tokens**. Each token is named, shows when it was created and last used, and is **long-lived by default** (set an expiry in days if you want one). Token values stay **viewable**, so you can copy one back into an agent's config later. Use them with `Authorization: Bearer ` on the API and `/mcp`. Tokens live in `netryx-data/tokens.json` (gitignored) — treat it as a secret. **Prompting & localhost.** By default you're prompted everywhere, including on the machine running Netryx (`NETRYX_TRUST_LOCALHOST=0`). For a frictionless local desktop, set `NETRYX_TRUST_LOCALHOST=1` to skip the prompt for `127.0.0.1` while still requiring it from other devices. **Run fully open** on a genuinely trusted segment with `NETRYX_OPEN=1`, which disables auth entirely (a banner reminds you it's off). ### HTTPS with an nginx reverse proxy Netryx serves plain HTTP, so passwords and tokens travel in cleartext. On a NAS or any untrusted segment, put it behind a reverse proxy that terminates TLS: server { listen 443 ssl; server_name netryx.example.lan; ssl_certificate /etc/nginx/certs/netryx.crt; ssl_certificate_key /etc/nginx/certs/netryx.key; location / { proxy_pass http://127.0.0.1:8765; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # Pass the caller's credentials (Bearer token / Basic) through: proxy_set_header Authorization $http_authorization; } # The SSE/long-poll endpoints accept a token in the query string (EventSource # can't set headers). Don't write those URLs to the access log. location /api/events/ { access_log off; proxy_pass http://127.0.0.1:8765; proxy_set_header Host $host; proxy_buffering off; # stream Server-Sent Events without buffering proxy_read_timeout 1h; } } Set `NETRYX_SECURE_COOKIES=1` once TLS is terminating in front, so the session cookie is only ever sent over HTTPS. **Gotcha:** nginx connects to Netryx from `127.0.0.1`. Keep `NETRYX_TRUST_LOCALHOST` at its default (`0`) so proxied requests are still authenticated — setting it to `1` would make every proxied request look local and skip auth. Two working patterns: 1. **App-enforced auth** (keeps in-app token management): set `NETRYX_PASS` and/or create API tokens, set `NETRYX_TRUST_LOCALHOST=0`, and let nginx pass `Authorization` through (as above). nginx only does TLS. 2. **Proxy-enforced auth**: let nginx do its own `auth_basic`, and leave Netryx trusting localhost. Simpler, but you lose per-token management. Either way, don't expose Netryx directly to the internet. ## Notes & tips - **Run as Administrator / root** for the most complete ARP and discovery results. - **Allow it through your firewall** on private networks the first time. - The local launcher binds to `127.0.0.1` only. The Docker/`--host 0.0.0.0` modes expose it to your whole LAN — appropriate for a NAS, but don't expose it to the internet. If you do expose `/mcp`, set `NETRYX_TOKEN`. - **Full** port scans (65,535 ports/host) are thorough but slow — best used on a single host via the per-device **Scan ports** button. - The downloaded vendor database is saved in the data folder and loaded automatically on the next scan. ## Ethical use Only scan networks you own or are authorized to test. ## Continuous integration This repo ships a GitHub Actions workflow (`.github/workflows/build.yml`) that runs on every push and pull request: - **Docker image** → built and pushed to the GitHub Container Registry (GHCR) as `ghcr.io//netryx:latest` (and a `:` tag). Pull and run it on your NAS with: mkdir -p netryx-data && sudo chown -R 10001:10001 netryx-data docker run -d --name netryx --network host \ --cap-add NET_RAW \ -v "$PWD/netryx-data:/data" --restart unless-stopped \ ghcr.io//netryx:latest The container runs as uid **10001**, so the `netryx-data` volume must be writable by that uid (the `chown` above) — otherwise nothing persists. See "data directory ownership" under Option C. - **Standalone Windows .exe** → built with PyInstaller on a Windows runner and uploaded as a build **artifact** on every run. Pushing a version tag (e.g. `git tag v1.0.0 && git push --tags`) also publishes the `.exe` on a GitHub **Release**. No secrets are required — the workflow authenticates to GHCR with the built-in `GITHUB_TOKEN`. After the first successful run, make the GHCR package public from your repo's *Packages* page if you want others to pull it. ## Privacy Your scan results are local only. `.gitignore` excludes `netryx_data/` (IPs, MACs, hostnames, device names/notes, scan history, baseline, events, API tokens and the admin login) and common secret files so they're never committed.