[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80274":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":9,"rankLanguage":9,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":24,"hasPages":22,"topics":25,"createdAt":9,"pushedAt":9,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":15,"starSnapshotCount":15,"syncStatus":16,"lastSyncTime":29,"discoverSource":30},80274,"NetMap","xoriin\u002FNetMap","xoriin","NetMap is a self-hosted tool that gives you a proper overview of your home lab or small network. Map out your devices, track IPs, watch for things going down, and dig into firewall logs — all from one place, running on your own hardware.",null,"TypeScript",104,10,86,1,0,2,14,17,18,55.82,"GNU General Public License v3.0",false,"main",true,[],"2026-06-12 04:01:27","\u003Cdiv align=\"center\">\n  \u003Cimg src=\"frontend\u002Fpublic\u002Ffavicon.svg\" width=\"96\" alt=\"NetMap\"\u002F>\n  \u003Ch1>NetMap\u003C\u002Fh1>\n  \u003Cp>Self-hosted network visibility and operations for home labs and small environments.\u003C\u002Fp>\n\n  [![Docker Pulls](https:\u002F\u002Fimg.shields.io\u002Fdocker\u002Fpulls\u002Fxoriin\u002Fnetmap?logo=docker&logoColor=white&color=1d9ab0)](https:\u002F\u002Fhub.docker.com\u002Fr\u002Fxoriin\u002Fnetmap)\n  [![Image Size](https:\u002F\u002Fimg.shields.io\u002Fdocker\u002Fimage-size\u002Fxoriin\u002Fnetmap\u002Flatest?logo=docker&logoColor=white&color=1d6472)](https:\u002F\u002Fhub.docker.com\u002Fr\u002Fxoriin\u002Fnetmap)\n  [![Version](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fv\u002Ftag\u002Fxoriin\u002Fnetmap?label=version&logo=github&logoColor=white&color=1d9ab0)](https:\u002F\u002Fgithub.com\u002Fxoriin\u002Fnetmap\u002Ftags)\n  [![Build](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Factions\u002Fworkflow\u002Fstatus\u002Fxoriin\u002Fnetmap\u002Fdocker-aio.yml?label=build&logo=github-actions&logoColor=white)](https:\u002F\u002Fgithub.com\u002Fxoriin\u002Fnetmap\u002Factions\u002Fworkflows\u002Fdocker-aio.yml)\n  [![Stars](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002Fxoriin\u002Fnetmap?logo=github&logoColor=white&color=091420)](https:\u002F\u002Fgithub.com\u002Fxoriin\u002Fnetmap\u002Fstargazers)\n  [![Issues](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fissues\u002Fxoriin\u002Fnetmap?logo=github&logoColor=white&color=1d6472)](https:\u002F\u002Fgithub.com\u002Fxoriin\u002Fnetmap\u002Fissues)\n  [![Last Commit](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flast-commit\u002Fxoriin\u002Fnetmap?logo=github&logoColor=white&color=1d9ab0)](https:\u002F\u002Fgithub.com\u002Fxoriin\u002Fnetmap\u002Fcommits\u002Fmain)\n  [![License](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-GPL--3.0-091420)](LICENSE)\n\u003C\u002Fdiv>\n\n---\n\nNetMap is a self-hosted tool that gives you a proper overview of your home lab or small network. Map out your devices, track IPs, watch for things going down, and dig into firewall logs — all from one place, running on your own hardware.\n\nIt started as a personal project to scratch an itch: one application that actually knows what's on your network, where it sits, and whether it's behaving. Built to drop straight into a Compose stack alongside your other self-hosted services with no cloud accounts, no subscriptions, and no phoning home.\n\nEverything runs in a single container. The web UI, API, database, and syslog receiver are all bundled together — nothing to orchestrate beyond the one service.\n\n---\n\n## Screenshots\n\n\u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fffd0c6d9-072f-41c1-bd4e-15c3737ede6b\" width=\"800\" alt=\"Overview\" \u002F>\n\u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Ff58ae91d-6b8e-40cb-95e5-f0a9975e97a6\" width=\"800\" alt=\"Topology\" \u002F>\n\u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fb6a666bb-ca75-4732-9416-4da65afcecfe\" width=\"800\" alt=\"Monitoring\" \u002F>\n\u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F13713071-f86e-432c-a503-d6069616109b\" width=\"800\" alt=\"IPAM\" \u002F>\n\n---\n\n## Contents\n\n- [Screenshots](#screenshots)\n- [Features](#-features)\n- [Installation](#-installation)\n  - [Quick start](#quick-start)\n  - [Full compose file](#full-compose-file)\n  - [Generating secrets](#generating-secrets)\n  - [First login](#first-login)\n- [Configuration reference](#️-configuration-reference)\n- [Ports](#-ports)\n- [Upgrading](#-upgrading)\n- [Account lockout recovery](#-account-lockout-recovery)\n- [Reverse proxy setup](#-reverse-proxy-setup)\n- [Firewall syslog ingestion](#-firewall-syslog-ingestion)\n- [Alert notifications](#-alert-notifications)\n- [Access control](#-access-control)\n- [How it works](#️-how-it-works)\n- [Tech stack](#-tech-stack)\n- [API](#-api)\n- [License](#license)\n\n---\n\n## ✨ Features\n\n### Topology canvas\nDraw your network visually. Add devices, draw links between them, group things into VLANs or logical clusters, and annotate everything. The canvas uses a force-directed layout that you can manually arrange and lock — positions are persisted so it looks exactly the same every time you come back. Supports multiple named sites so you can separate a home network, a lab VLAN, and an off-site location without cluttering a single view.\n\n### Device inventory\nA searchable, filterable table of every device you've added. Bulk-edit device types, statuses, and sites in one go. Device types include server, workstation, laptop, switch, router, firewall, access point, camera, phone, VPN, and cloud endpoint — each with a matching icon on the topology canvas. Custom icon packs are also supported if you want to extend the defaults.\n\n### Monitoring\nContinuous background polling for every device in your inventory:\n- **Live ping** — ICMP round-trip with RTT history graphed per device\n- **TCP port checks** — define one or more ports per device and watch their status independently\n- **Heartbeat strip** — last 30 poll results shown inline as a colour-coded bar so you can spot intermittent outages at a glance\n- **Uptime tracking** — rolling calculation of availability percentage\n- Monitoring interval is configurable per instance; the background thread runs independently of API request load\n\n### IPAM (IP address management)\n- Define subnets and assign them to VLANs\n- Track individual IP allocations with notes, device associations, and assignment type (static \u002F DHCP \u002F reserved)\n- Import DHCP leases directly from your router's lease file\n- Visual IP grid shows at a glance which addresses in a subnet are in use, available, or reserved — with per-cell tooltips showing the full record\n- Conflict detection flags duplicate assignments before they cause problems\n\n### Firewall logs\n- Receive syslog over UDP and TCP (port 5514 by default) from pfSense, OPNsense, Unifi, or any RFC-5424\u002F3164-compatible source\n- Live-tail the stream in the browser, search by IP, protocol, port, or action\n- Each event is matched against your device inventory and linked so you can jump straight to a device's topology card from a log line\n- Configurable sender allowlist so only your firewall can submit logs\n- Retention window keeps the database lean (default: 7 days)\n\n### Network discovery\nRun an Nmap scan against a subnet and import discovered hosts straight into your inventory. Detected hostnames, MAC addresses, and open ports are pre-populated on the new device record. Scans run in the background and their status is visible in the UI — you don't have to sit and wait.\n\n### Built-in network tools\nNo more SSHing into a jump box to run a quick check. NetMap includes:\n- **Ping** — ICMP to any host (private targets by default; public targets can be enabled by a SuperAdmin)\n- **Traceroute** — hop-by-hop path to a host\n- **TCP connect** — test whether a port is reachable\n- **DNS lookup** — forward and reverse resolution\n- **Subnet calculator** — break down any CIDR block into its address range, broadcast, usable hosts, and more\n\n### Alerts\nConfigure rules that fire when a device goes down, comes back up, or trips a monitoring threshold. Each rule can fan out to multiple channels:\n- **ntfy** — push to any ntfy topic (self-hosted or ntfy.sh)\n- **Telegram** — bot message to a chat or channel\n- **Signal** — via a Signal API relay\n- **Email** — SMTP with TLS\n\nRules have a configurable cooldown period so you don't get paged every 30 seconds for a flappy device.\n\n### Access control\nFour built-in roles with granular permission sets — no one gets more access than they need:\n\n| Role | What they can do |\n|------|-----------------|\n| **SuperAdmin** | Full access: manage users, roles, system settings, and all data |\n| **NetworkAdmin** | Manage topology, devices, IPAM, and monitoring. Cannot manage users or roles |\n| **SecurityAnalyst** | Read-only on topology and inventory; full access to firewall logs and the security workspace |\n| **Viewer** | Read-only everywhere |\n\nRole permissions are customisable in Admin → Roles if the defaults don't match your setup.\n\n### Exports & reporting\n- PDF reports for device inventory and monitoring summaries\n- CSV and JSON exports for any table view\n- Full database backup and restore through the admin UI\n- Audit log of all write operations (who changed what, and when)\n\n---\n\n## 🚀 Installation\n\n### Quick start\n\nYou need Docker and Docker Compose. That's it.\n\n**1. Create a directory for NetMap:**\n\n```bash\nmkdir -p \u002Fopt\u002Fnetmap && cd \u002Fopt\u002Fnetmap\n```\n\n**2. Create a `docker-compose.yml`:**\n\n```yaml\nservices:\n  netmap:\n    image: xoriin\u002Fnetmap:latest\n    container_name: netmap\n    environment:\n      PUID: 1000\n      PGID: 1000\n      TZ: \"America\u002FNew_York\"\n      SECRET_KEY: \"replace-with-generated-secret\"\n      MASTER_KEY: \"replace-with-generated-fernet-key\"\n      TRUSTED_HOSTS: '[\"*\"]'\n    volumes:\n      - \u002Fopt\u002Fnetmap\u002Fdata:\u002Fapp\u002Fdata\n    ports:\n      - \"8080:8080\"\n      - \"5514:1514\u002Fudp\"\n      - \"5514:1514\u002Ftcp\"\n    cap_add:\n      - NET_RAW\n    restart: unless-stopped\n```\n\n**3. Generate your secrets** (run once, paste the output into the compose file):\n\n```bash\n# SECRET_KEY\npython3 -c \"import secrets; print(secrets.token_urlsafe(48))\"\n\n# MASTER_KEY\npython3 -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\"\n```\n\n> **Important:** Keep both keys stable. Changing `SECRET_KEY` invalidates all active sessions. Changing `MASTER_KEY` makes any encrypted data stored by NetMap unreadable.\n\n**4. Start the container:**\n\n```bash\ndocker compose up -d\n```\n\n**5. Open the UI:**\n\n```\nhttp:\u002F\u002Flocalhost:8080\n```\n\nOn first run you'll be prompted to create your admin account. Once that's done, you're in.\n\n---\n\n### Full compose file\n\nThe quick start above uses sensible defaults. Here's the full compose file with every option exposed and commented:\n\n```yaml\nservices:\n  netmap:\n    image: xoriin\u002Fnetmap:latest\n    container_name: netmap\n    environment:\n      APP_ENV: production\n      DATABASE_URL: sqlite:\u002F\u002F\u002F\u002Fapp\u002Fdata\u002Fnetmap.db\n      DATA_DIR: \u002Fapp\u002Fdata\n\n      # Match these to your host user so the bind mount is writable.\n      # Run `id` in a terminal to find your UID and GID.\n      PUID: 1000\n      PGID: 1000\n\n      # Container timezone — affects log timestamps and scheduled tasks.\n      # Use a tz database name, e.g. Europe\u002FLondon, America\u002FNew_York.\n      # TZ: UTC\n\n      # Port the web UI listens on. Change if 8080 is already taken.\n      APP_PORT: 8080\n\n      # Public URL for password-reset links in emails.\n      # Set this if you're behind a reverse proxy.\n      # APP_URL: http:\u002F\u002Fnetmap.example.com:8080\n\n      # Required — generate these before first start (see above).\n      SECRET_KEY: \"replace-with-generated-secret\"\n      MASTER_KEY: \"replace-with-generated-fernet-key\"\n\n      # [\"*\"] works for LAN installs and anything behind a reverse proxy.\n      # Lock to your exact hostname for internet-facing deployments.\n      TRUSTED_HOSTS: '[\"*\"]'\n\n      # Set true once TLS is terminating in front of this container.\n      SECURE_HSTS_ENABLED: \"false\"\n      AUTH_COOKIE_SECURE: \"false\"\n\n      # IPs\u002FCIDRs of your reverse proxy so forwarded headers are trusted.\n      TRUSTED_PROXY_IPS: '[\"127.0.0.1\"]'\n\n      LOG_LEVEL: info\n\n      # How long to keep monitoring events and firewall logs (days).\n      EVENT_RETENTION_DAYS: \"7\"\n      FIREWALL_LOG_RETENTION_DAYS: \"7\"\n\n      # Syslog receiver — disable if you don't use it.\n      SYSLOG_ENABLED: \"true\"\n      SYSLOG_UDP_ENABLED: \"true\"\n      SYSLOG_TCP_ENABLED: \"true\"\n      SYSLOG_HOST: 0.0.0.0\n      SYSLOG_UDP_PORT: \"1514\"\n      SYSLOG_TCP_PORT: \"1514\"\n      # Only accept logs from these IPs\u002Fsubnets (unset = accept all):\n      # SYSLOG_SENDER_ALLOWLIST: '[\"192.168.1.1\",\"10.0.0.0\u002F8\"]'\n\n      # Active tools (ping, traceroute, TCP) are private-target-only by default.\n      # Set to \"true\" to allow public targets; SuperAdmins can also change this\n      # at runtime in Admin → System.\n      ACTIVE_NETWORK_PUBLIC_TARGETS_ENABLED: \"false\"\n\n    volumes:\n      - \u002Fopt\u002Fnetmap\u002Fdata:\u002Fapp\u002Fdata  # change the host path to suit your setup\n\n    ports:\n      - \"8080:8080\"       # Web UI and API\n      - \"5514:1514\u002Fudp\"   # Syslog UDP\n      - \"5514:1514\u002Ftcp\"   # Syslog TCP\n\n    cap_drop:\n      - ALL\n    cap_add:\n      - NET_RAW            # Required for ICMP ping and traceroute\n    security_opt:\n      - no-new-privileges:true\n\n    restart: unless-stopped\n\n    logging:\n      driver: \"json-file\"\n      options:\n        max-size: \"10m\"\n        max-file: \"5\"\n```\n\n---\n\n### Generating secrets\n\nIf you don't have Python installed locally, you can generate the keys inside a temporary container:\n\n```bash\n# SECRET_KEY\ndocker run --rm python:3.12-slim python3 -c \"import secrets; print(secrets.token_urlsafe(48))\"\n\n# MASTER_KEY\ndocker run --rm python:3.12-slim python3 -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\"\n```\n\n---\n\n### First login\n\n1. Navigate to `http:\u002F\u002F\u003Cyour-host>:8080`\n2. NetMap detects no users exist and redirects to account setup\n3. Enter a username and password for your SuperAdmin account\n4. You're in — start adding devices or run a discovery scan to populate your inventory automatically\n\n---\n\n## ⚙️ Configuration reference\n\nAll configuration is done via environment variables in your compose file. There is no config file to edit inside the container.\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `TZ` | `UTC` | Container timezone. Use a tz database name (e.g. `Europe\u002FLondon`, `America\u002FNew_York`). Affects log timestamps and scheduled tasks. |\n| `SECRET_KEY` | — | **Required.** Signs session tokens. Generate once, keep stable. |\n| `MASTER_KEY` | — | **Required.** Fernet key for encrypting stored secrets. Generate once, never change. |\n| `PUID` | `1000` | UID the container process runs as. Match your host user for correct bind mount permissions. |\n| `PGID` | `1000` | GID the container process runs as. |\n| `APP_PORT` | `8080` | Port the web UI and API listen on. |\n| `APP_URL` | *(derived)* | Public URL used in password-reset emails. Set this if you're behind a reverse proxy. |\n| `TRUSTED_HOSTS` | `[\"*\"]` | JSON array of accepted `Host` header values. Use `[\"*\"]` for LAN; lock down for internet-facing. |\n| `CORS_ORIGINS` | *(derived from APP_URL)* | JSON array of allowed CORS origins. |\n| `SECURE_HSTS_ENABLED` | `false` | Set `true` to send HSTS headers. Enable only when TLS is terminating upstream. |\n| `AUTH_COOKIE_SECURE` | `false` | Set `true` to mark auth cookies as `Secure`. Enable with HTTPS. |\n| `TRUSTED_PROXY_IPS` | `[\"127.0.0.1\"]` | IPs\u002FCIDRs of upstream proxies whose forwarded headers are trusted. |\n| `LOG_LEVEL` | `info` | Uvicorn log level: `debug`, `info`, `warning`, `error`, `critical`. |\n| `EVENT_RETENTION_DAYS` | `7` | Days to keep monitoring poll events. |\n| `FIREWALL_LOG_RETENTION_DAYS` | `7` | Days to keep syslog events. |\n| `SYSLOG_ENABLED` | `true` | Enable\u002Fdisable the syslog receiver entirely. |\n| `SYSLOG_UDP_ENABLED` | `true` | Enable\u002Fdisable UDP syslog. |\n| `SYSLOG_TCP_ENABLED` | `true` | Enable\u002Fdisable TCP syslog. |\n| `SYSLOG_UDP_PORT` | `1514` | Internal UDP port (map it to `5514` externally in compose). |\n| `SYSLOG_TCP_PORT` | `1514` | Internal TCP port. |\n| `SYSLOG_SENDER_ALLOWLIST` | *(unset = accept all)* | JSON array of IPs or CIDRs allowed to send logs. |\n| `ACTIVE_NETWORK_PUBLIC_TARGETS_ENABLED` | `false` | Allow ping\u002Ftraceroute\u002FTCP to public internet addresses. |\n| `ACCESS_TOKEN_MINUTES` | `15` | Lifetime of JWT access tokens. |\n| `IDLE_TIMEOUT_MINUTES` | `15` | Session idle timeout before re-authentication is required. |\n| `REFRESH_TOKEN_DAYS` | `7` | Refresh token lifetime. |\n| `AUTH_MAX_FAILED_ATTEMPTS` | `5` | Failed login attempts before lockout. |\n| `AUTH_LOCKOUT_MINUTES` | `15` | Duration of account lockout after failed attempts. |\n\n---\n\n## 🔌 Ports\n\n| Container port | Protocol | What it's for |\n|----------------|----------|---------------|\n| `8080` | TCP | Web UI and REST API (configurable via `APP_PORT`) |\n| `1514` | UDP | Syslog ingest (map to `5514` externally by convention) |\n| `1514` | TCP | Syslog ingest |\n\nThe `NET_RAW` capability is required for ICMP ping and traceroute. Without it those tools will silently fail.\n\n---\n\n## 💾 Upgrading\n\nPull the latest image and recreate the container. The database schema is migrated automatically on startup — no manual steps required.\n\n```bash\ndocker compose pull\ndocker compose up -d\n```\n\nBack up your data directory before upgrading if you want a rollback option:\n\n```bash\ncp -r \u002Fopt\u002Fnetmap\u002Fdata \u002Fopt\u002Fnetmap\u002Fdata.bak-$(date +%Y%m%d)\n```\n\nNetMap also has a built-in backup tool under Admin → Database that exports the full database to a downloadable file.\n\n---\n\n## 🔓 Account lockout recovery\n\nIf a SuperAdmin account is locked out after too many failed login attempts, the in-app unlock button (Admin → Users → Unlock) requires an active admin session and cannot be used while locked. Recovery is done directly against the database from the Docker host.\n\nRun this on the machine running the container (replace `netmap` with your container name if you changed it):\n\n```bash\ndocker exec netmap python3 -c \"\nimport sqlite3\nconn = sqlite3.connect('\u002Fapp\u002Fdata\u002Fnetmap.db')\nconn.execute(\\\"UPDATE login_throttle_state SET failed_attempts=0, locked_until=NULL WHERE subject LIKE 'user:%'\\\")\nconn.commit()\nconn.close()\nprint('Done.')\n\"\n```\n\nThis clears the lockout for all users. To target a specific account, replace `LIKE 'user:%'` with `= 'user:yourusername'` (the username is always stored lowercase in the lockout table).\n\n> **Note:** Restarting the container does not clear lockouts — they are persisted in the database by design.\n\n---\n\n## 🔒 Reverse proxy setup\n\nNetMap works out of the box behind nginx, Caddy, Traefik, Nginx Proxy Manager, and any other standard reverse proxy. Proxy to port `8080` and leave everything else at defaults for a LAN install.\n\n**For an HTTPS\u002Fpublic-facing install**, make these additional changes once TLS is working:\n\n```yaml\nenvironment:\n  APP_URL: \"https:\u002F\u002Fnetmap.example.com\"\n  TRUSTED_HOSTS: '[\"netmap.example.com\"]'\n  CORS_ORIGINS: '[\"https:\u002F\u002Fnetmap.example.com\"]'\n  SECURE_HSTS_ENABLED: \"true\"\n  AUTH_COOKIE_SECURE: \"true\"\n  TRUSTED_PROXY_IPS: '[\"127.0.0.1\"]'  # add your proxy's IP if not localhost\n```\n\n**Example Caddy config:**\n\n```\nnetmap.example.com {\n    reverse_proxy localhost:8080\n}\n```\n\n**Example nginx location block:**\n\n```nginx\nserver {\n    listen 443 ssl;\n    server_name netmap.example.com;\n\n    location \u002F {\n        proxy_pass         http:\u002F\u002F127.0.0.1:8080;\n        proxy_set_header   Host $host;\n        proxy_set_header   X-Real-IP $remote_addr;\n        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header   X-Forwarded-Proto $scheme;\n    }\n}\n```\n\n---\n\n## 📡 Firewall syslog ingestion\n\nPoint your firewall's remote logging at your NetMap host on port `5514`.\n\n**pfSense \u002F OPNsense:**\nSystem → Logging → Remote → add a new target with your NetMap host IP and port `5514`, protocol UDP or TCP.\n\n**Unifi \u002F UnifiOS:**\nSettings → System → Remote Logging → enable syslog, set the server to your NetMap host and port `5514`.\n\n**Any RFC-5424 or RFC-3164 compatible source works.** Once logs are flowing they appear in the Security workspace where you can live-tail, search, and filter them. Each event is automatically matched against your device inventory by IP address so you can jump straight to a device record from a log entry.\n\nTo restrict which IPs can send logs:\n\n```yaml\nSYSLOG_SENDER_ALLOWLIST: '[\"192.168.1.1\", \"10.0.0.0\u002F8\"]'\n```\n\nLeave it unset to accept from anywhere on your network.\n\n---\n\n## 🔔 Alert notifications\n\nAlerts fire when a monitored device changes state (online → offline, offline → online) or when a monitoring threshold is breached. Configure rules in Admin → Alerts.\n\nEach rule specifies:\n- Which devices or groups it applies to\n- Which event types it triggers on\n- One or more notification channels\n- A cooldown period (minimum time between repeated alerts for the same device)\n\n**Supported channels:**\n\n| Channel | What you need |\n|---------|--------------|\n| **ntfy** | A topic URL on ntfy.sh or your self-hosted ntfy instance. Optional access token for private topics. |\n| **Telegram** | A bot token and a chat\u002Fchannel ID. Create a bot via [@BotFather](https:\u002F\u002Ft.me\u002FBotFather). |\n| **Signal** | A Signal API relay URL (e.g. [signal-cli-rest-api](https:\u002F\u002Fgithub.com\u002Fbbernhard\u002Fsignal-cli-rest-api)). |\n| **Email** | SMTP hostname, port, sender address, and credentials. TLS is supported. |\n\n---\n\n## 👥 Access control\n\nUser management lives in Admin → Users. Invite users by creating their account and assigning a role — they set their password on first login.\n\n| Role | Topology | Inventory | IPAM | Monitoring | Firewall logs | Tools | Admin |\n|------|----------|-----------|------|------------|---------------|-------|-------|\n| **SuperAdmin** | ✓ full | ✓ full | ✓ full | ✓ full | ✓ full | ✓ full | ✓ full |\n| **NetworkAdmin** | ✓ full | ✓ full | ✓ full | ✓ full | read | ✓ full | — |\n| **SecurityAnalyst** | read | read | read | read | ✓ full | ✓ | — |\n| **Viewer** | read | read | read | read | read | — | — |\n\nRole permissions are editable in Admin → Roles if you need something different from the defaults.\n\n---\n\n## 🏗️ How it works\n\nNetMap is a single all-in-one container running three cooperating processes managed by `tini`:\n\n```\n┌─────────────────────────────────────────┐\n│  Container (aio)                        │\n│                                         │\n│  ┌──────────┐   ┌──────────────────┐   │\n│  │  nginx   │   │  uvicorn         │   │\n│  │  :8080   │──▶│  FastAPI :8000   │   │\n│  │  (static)│   │  (API + WS)      │   │\n│  └──────────┘   └────────┬─────────┘   │\n│                           │             │\n│               ┌───────────┼──────────┐  │\n│               │           │          │  │\n│          ┌────▼───┐  ┌────▼────┐ ┌──▼──┐│\n│          │Monitor │  │ Syslog  │ │SQLite││\n│          │thread  │  │ server  │ │ DB  ││\n│          └────────┘  └─────────┘ └─────┘│\n└─────────────────────────────────────────┘\n```\n\n- **nginx** serves the pre-built React bundle as static files and reverse-proxies `\u002Fapi\u002F*` requests to uvicorn. This avoids CORS issues and lets nginx handle static asset caching efficiently.\n- **uvicorn** runs the FastAPI application on an internal port. All API logic, authentication, database access, and WebSocket connections go through here.\n- **Monitoring thread** — a Python background thread (`threading.Thread`) wakes on a configurable interval and ICMP-pings every device that has monitoring enabled. Results are written to the SQLite database and pushed to any connected WebSocket clients in real time.\n- **Syslog server** — a second background component binds UDP\u002FTCP ports and parses incoming syslog frames. Parsed events are stored in SQLite and broadcast to the security workspace via WebSocket.\n- **SQLite** — the only persistence layer. The schema is managed through an in-house migration runner that applies numbered SQL migration files in order on startup, so upgrades are automatic and require no external tooling.\n\n**Authentication** uses short-lived JWT access tokens (default 15 min) rotated via HTTP-only refresh tokens (default 7 days). Passwords are hashed with Argon2id. CSRF protection is applied to all state-changing endpoints via a double-submit cookie pattern.\n\n**The frontend** is a single React application compiled to a static bundle at build time. It talks exclusively to `\u002Fapi\u002Fv1\u002F*` and uses WebSockets for live monitoring updates and syslog tailing. There is no server-side rendering — the API is the only backend surface.\n\n---\n\n## 🧰 Tech stack\n\n### Backend\n\n| Library | Purpose |\n|---------|---------|\n| **Python 3.12** | Runtime |\n| **FastAPI** | Web framework and API routing |\n| **uvicorn** | ASGI server |\n| **SQLAlchemy 2** | ORM and database abstraction |\n| **SQLite** | Embedded database (no separate DB server required) |\n| **Argon2-cffi** | Password hashing (Argon2id) |\n| **python-jose** | JWT signing and verification |\n| **pydantic-settings** | Environment variable parsing and validation |\n| **dnspython** | DNS lookups for the built-in tools |\n| **defusedxml** | Safe XML parsing (used in syslog processing) |\n| **reportlab** | PDF report generation |\n| **nmap** | Network discovery scans (system package) |\n| **iputils-ping \u002F traceroute** | ICMP ping and traceroute (system packages) |\n\n### Frontend\n\n| Library | Purpose |\n|---------|---------|\n| **React 18** | UI framework |\n| **TypeScript 5** | Type-safe JavaScript |\n| **Vite 7** | Build tool and dev server |\n| **Cytoscape.js** | Interactive topology canvas (graph rendering and layout) |\n| **Tabler Icons** | Icon set used throughout the UI |\n| **Lucide React** | Supplementary icon set |\n| **DOMPurify** | Sanitises any HTML before rendering to prevent XSS |\n\n### Infrastructure\n\n| Component | Purpose |\n|-----------|---------|\n| **nginx** | Serves static frontend assets; reverse-proxies API requests |\n| **tini** | PID 1 init process — reaps zombie processes correctly |\n| **gosu** | Drops privileges from root to the configured PUID\u002FPGID at startup |\n| **Docker** | Container runtime |\n\n---\n\n## 🌐 API\n\nThe full REST API is documented at `\u002Fapi\u002Fdocs` (Swagger UI) once the container is running. Every operation available in the UI is also available through the API with the same authentication — log in via `\u002Fapi\u002Fv1\u002Fauth\u002Flogin` to get a session token and use it in the `Authorization: Bearer \u003Ctoken>` header.\n\nThe OpenAPI schema is available at `\u002Fapi\u002Fopenapi.json` if you want to generate a client in another language.\n\n---\n\n## License\n\nGPL-3.0 — see [LICENSE](LICENSE) for details.\n\n---\n\nParts of this project were built with assistance from [Claude](https:\u002F\u002Fclaude.ai) (Anthropic). All code is reviewed and owned by the project author.\n","NetMap 是一个自托管工具，用于提供家庭实验室或小型网络的概览。其核心功能包括设备映射、IP 跟踪、监控设备状态以及分析防火墙日志，所有这些功能都集成在一个单一容器中运行，便于部署和管理。该工具采用 TypeScript 开发，支持 Docker 部署，确保了在没有云账户、订阅服务的情况下也能独立运作。适用于需要对本地网络环境进行有效管理和监控的小型企业和个人用户。","2026-06-11 04:00:08","CREATED_QUERY"]