scanorama

module
v0.23.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 9, 2026 License: MIT

README

Scanorama

CI Security Go Report Card codecov

Scanorama is a self-hosted network scanning and discovery tool. It discovers hosts on your networks, tracks port states, OS fingerprints, MAC vendors, and response times over time, and surfaces everything in a dense operator dashboard. A Go backend handles scanning, scheduling, and persistence; a React frontend provides the UI.


Features

Discovery & Scanning
  • Network discovery via nmap — ping sweep, TCP probe, or ARP, with optional OS fingerprinting
  • Port scanning with five scan modes: connect (TCP), SYN stealth, version detection, aggressive, and comprehensive full-range
  • Scan profiles — named, reusable configurations for ports, scan type, and options; profiles are cloneable
  • Scheduled jobs — cron-based scheduling for both discovery and scan jobs, with enable/disable and next-run preview
  • Worker pool — configurable number of concurrent scan workers; admin panel shows live worker status and queue depth
  • Network exclusions — per-network or global IP/CIDR exclusion lists respected during all scan and discovery operations
Host Tracking
  • Persistent host inventory with full scan history per host
  • Status model — four states: up, down, unknown, gone; state transitions (e.g. up → gone) are recorded with timestamps
  • Response time capture — min, max, and average RTT per host, measured during discovery and scan probes; slow hosts flagged
  • Scan duration per host — how long each scan took for each individual host
  • Timeout tracking — timeouts recorded as distinct events, separate from clean "down" states
  • MAC vendor lookup — OUI resolution via MAC address during discovery; vendor name (Cisco, Raspberry Pi, Dell, etc.) displayed on the host list and in host detail
  • Hostname management — inline editing of hostnames from the host detail panel
Discovery Changelog
  • Diff view — after every discovery run, see exactly which hosts are new, gone, or changed (status change, new ports, etc.)
  • "Gone" host retention — hosts that stop responding are marked gone with a last-seen timestamp, not deleted
  • History comparison — compare any two completed discovery runs side by side: what appeared between run A and run B, what disappeared, what changed; edge cases handled (same-run short-circuits, different-network returns a clear error)
  • Discovery notifications — WebSocket notification fires when a scheduled discovery completes; a clickable toast summarises new/gone/changed counts and links to the diff view
  • Dashboard widget — Recent Discovery Changes card shows the diff from the latest completed run
Advanced Filtering
  • Filter builder UI on the hosts page with compound AND/OR logic
  • Fields: status, OS family, vendor, hostname, response time (ms), first seen, last seen, open port, scan count
  • Operators: is, is not, contains (text), >, <, between (numeric/date)
  • Sub-groups — one level of nesting for complex expressions like (status is up AND os_family is Linux) OR open_port is 443
  • Server-side SQL translation — filters are translated to parameterised SQL; port-based filtering uses a correlated EXISTS subquery against scan results
  • URL persistence — active filter serialised as base64url in ?filter= for shareable links
  • Saved presets — named filter presets stored in localStorage, loaded from a dropdown; up to 20 presets
Table Power-Ups (all list views)
  • Universal server-side sorting — every list view (Hosts, Scans, Networks, Schedules, Profiles, Exclusions) supports sortable columns
  • Multi-select — checkbox column with shift-click range selection and select-all-on-page
  • Bulk delete — delete multiple hosts in one action with confirmation
  • Bulk scan — select multiple hosts and launch a scan against the selection in one click
  • Column visibility — show/hide table columns per user preference (persisted in localStorage)
  • Keyboard navigation — arrow keys move between rows, Enter opens the detail panel, Space toggles selection, Escape dismisses
Real-Time & Admin
  • WebSocket streams — three channels: GET /api/v1/ws (general events including discovery), GET /api/v1/ws/scans (scan progress), GET /api/v1/ws/logs (live log tail)
  • Admin panel — system health, database status, uptime, build info, worker status table, and a streaming log viewer
  • Prometheus metrics — scan throughput, error rates, and operational counters exposed at /metrics
  • Structured logging — slog-based, text or JSON format, configurable level
API
  • Full REST API with X-API-Key authentication
  • Interactive Swagger UI at /swagger/
  • API key management via CLI or API

Requirements

Dependency Version Notes
Go 1.26+ Backend build
PostgreSQL 14+ Persistence
nmap 7.0+ Required at runtime
Node.js 20+ Frontend dev only

Quick Start

git clone https://github.com/anstrom/scanorama.git
cd scanorama

# Build the Go binary
make build

# Copy and edit config
cp config/environments/config.example.yaml config.yaml
# Set: database.host, database.username, database.password, api.api_keys

# Start a PostgreSQL instance (or use your own)
make dev-db-up

# Run the API server
./build/scanorama api

The API is available at http://localhost:8080 and the Swagger UI at http://localhost:8080/swagger/.


Development Setup

make dev starts everything — it builds the binary, ensures the dev database is running, starts the backend as root (required for nmap SYN scans), and launches the Vite dev server.

# First-time setup (installs Go tools + npm deps)
make dev-setup

# Start backend + frontend (hot reload on both)
make dev

# Frontend is served at http://localhost:5173 (proxies /api and /ws to :8080)
# Backend API is at http://localhost:8080

Other useful targets:

make test          # Run all Go tests
make test-unit     # Unit tests only (no DB required)
make lint          # golangci-lint
make check         # lint + tests in one pass
make dev-db-shell  # psql shell to the dev database
make dev-nuke      # Tear down the dev DB and delete all data
Frontend

The frontend is a React 19 + Vite 6 + TypeScript 5 single-page app, styled with Tailwind CSS 4, using TanStack Router and TanStack Query for routing and data fetching.

cd frontend
npm install
npm run dev      # Dev server at http://localhost:5173
npm test         # Vitest unit tests (835 tests)
npm run build    # Production build to frontend/dist/

Stack:

Layer Library
Framework React 19
Build Vite 6
Language TypeScript 5
Styling Tailwind CSS 4
Routing TanStack Router 1
Data fetching TanStack Query 5
Charts Recharts 3
Icons Lucide React
Validation Zod 4
Testing Vitest 4 + Testing Library + MSW 2

The design direction is dark-first and information-dense — built for people who stare at IP addresses all day.


Frontend Pages

Route Page Description
/ Dashboard Stat cards (active hosts, networks, scans), 7-day scan activity chart, recent discovery changes widget
/hosts Hosts Full host inventory with advanced filter builder, response times, vendor info, bulk ops, detail panel
/scans Scans Scan job list with status, launch new scans, stop/delete, results detail panel
/networks Networks Network CIDR management, enable/disable, per-network discovery and scan triggers
/discovery Discovery Discovery run list, diff view per run, history comparison panel, toast notifications
/schedules Schedules Cron-based scheduled discovery and scan jobs; create/edit/enable/disable
/profiles Profiles Scan profile CRUD; clone existing profiles as a starting point
/exclusions Exclusions Global and per-network IP/CIDR exclusion lists
/admin Admin System health, worker status table, live log viewer

CLI Reference

API Keys
# Create an API key
scanorama apikeys create --name "my-key"
# → sk_live_abc123...

export SCANORAMA_API_KEY=sk_live_abc123...

scanorama apikeys list
scanorama apikeys delete <id>
Discovery
# Discover a CIDR
scanorama discover 192.168.1.0/24

# Save the network and run discovery
scanorama discover 192.168.1.0/24 --add --name "home-lan"

# Discover all configured networks
scanorama discover --configured-networks

# Auto-detect and discover all local interfaces
scanorama discover --all-networks

# Discovery method (default: ping)
scanorama discover 10.0.0.0/24 --method tcp    # tcp probe
scanorama discover 10.0.0.0/24 --method arp    # ARP (local only)

# Enable OS fingerprinting during discovery (requires root)
scanorama discover 192.168.1.0/24 --detect-os
Scanning
# Scan a specific target
scanorama scan --targets 192.168.1.1

# Scan with explicit ports
scanorama scan --targets "192.168.1.1,192.168.1.10" --ports "22,80,443"

# Scan all known live hosts
scanorama scan --live-hosts

# Filter live hosts by OS family
scanorama scan --live-hosts --os-family windows

# Use a saved scan profile
scanorama scan --targets 192.168.1.1 --profile linux-server

# Scan types
scanorama scan --targets 192.168.1.1 --type connect       # TCP connect (default, no root)
scanorama scan --targets 192.168.1.1 --type syn           # SYN stealth (requires root)
scanorama scan --targets 192.168.1.1 --type version       # Service version detection
scanorama scan --targets 192.168.1.1 --type aggressive    # OS + version + scripts
scanorama scan --targets 192.168.1.1 --type comprehensive # Full 65535-port range
Scheduling
scanorama schedule list

# Schedule weekly discovery (every Sunday at 02:00)
scanorama schedule add-discovery "weekly-sweep" "0 2 * * 0" "10.0.0.0/8"

# Schedule a scan every 6 hours against live hosts
scanorama schedule add-scan "frequent-scan" "0 */6 * * *" --live-hosts

scanorama schedule remove weekly-sweep
Running the Server
scanorama api              # API server in the foreground
scanorama daemon start     # Start background daemon with API server
scanorama daemon status    # Check daemon status
scanorama daemon stop      # Stop daemon

Configuration

cp config/environments/config.example.yaml config.yaml
database:
  host: localhost
  port: 5432
  database: scanorama
  username: scanorama
  password: your_secure_password
  ssl_mode: prefer            # disable | require | verify-ca | verify-full | prefer
  max_open_conns: 25
  max_idle_conns: 5

scanning:
  worker_pool_size: 10
  default_ports: "22,80,443,8080,8443"
  default_scan_type: connect  # connect | syn | version | aggressive | comprehensive
  enable_os_detection: false  # requires root / CAP_NET_RAW
  max_scan_timeout: "10m"
  rate_limit:
    enabled: true
    requests_per_second: 100

api:
  enabled: true
  listen_addr: "127.0.0.1"
  port: 8080
  auth_enabled: true
  api_keys:
    - "your-api-key-here"
  cors:
    enabled: true
    allowed_origins: ["*"]

daemon:
  pid_file: "/var/run/scanorama.pid"
  work_dir: "/var/lib/scanorama"
  user: ""    # drop privileges to this user after binding (Linux)
  group: ""
  shutdown_timeout: "30s"

logging:
  level: info     # debug | info | warn | error
  format: text    # text | json
  output: stdout
Environment Variables

All configuration values can be overridden with environment variables:

Variable Config key
SCANORAMA_DB_HOST database.host
SCANORAMA_DB_PORT database.port
SCANORAMA_DB_NAME database.database
SCANORAMA_DB_USER database.username
SCANORAMA_DB_PASSWORD database.password
SCANORAMA_DB_SSLMODE database.ssl_mode
SCANORAMA_API_KEY API key for CLI / client authentication
SCANORAMA_LOG_LEVEL logging.level
SCANORAMA_LOG_FORMAT logging.format
SCANORAMA_USER daemon.user
SCANORAMA_GROUP daemon.group
SCANORAMA_PID_FILE daemon.pid_file

API Reference

All endpoints (except /api/v1/health and /api/v1/liveness) require an X-API-Key header.

System
GET  /api/v1/health
GET  /api/v1/liveness
GET  /api/v1/status
GET  /api/v1/version
GET  /api/v1/metrics
GET  /api/v1/admin/status
GET  /api/v1/admin/workers
GET  /api/v1/admin/logs
Hosts
GET    /api/v1/hosts                   List hosts (filtering, sorting, pagination, advanced filter)
POST   /api/v1/hosts                   Create host
DELETE /api/v1/hosts                   Bulk delete hosts (body: {"ids": [...]})
GET    /api/v1/hosts/{id}              Get host
PUT    /api/v1/hosts/{id}              Update host
DELETE /api/v1/hosts/{id}              Delete host
GET    /api/v1/hosts/{id}/scans        Get scan history for a host

Host list query parameters:

Parameter Description
page, page_size Pagination
sort_by, sort_order Column sort (asc/desc)
status Filter by status (up/down/unknown/gone)
os Filter by OS family
vendor Filter by MAC vendor
search Full-text search on IP and hostname
filter JSON-encoded advanced filter expression (see below)

Advanced filter expression format:

A filter is a JSON group node with op (AND/OR) and conditions — each condition is either a leaf or a nested group (max 3 levels deep, max 20 conditions per group).

{
  "op": "AND",
  "conditions": [
    { "field": "status", "cmp": "is", "value": "up" },
    { "field": "os_family", "cmp": "contains", "value": "Linux" },
    {
      "op": "OR",
      "conditions": [
        { "field": "open_port", "cmp": "is", "value": "22" },
        { "field": "open_port", "cmp": "is", "value": "2222" }
      ]
    }
  ]
}

Supported fields: status, os_family, vendor, hostname, response_time_ms, first_seen, last_seen, open_port, scan_count.
Supported operators: is, is_not, contains (text fields), gt, lt, between (numeric/date fields).

Scans
GET    /api/v1/scans                   List scans (filtering, sorting, pagination)
POST   /api/v1/scans                   Create scan job
GET    /api/v1/scans/{id}              Get scan
PUT    /api/v1/scans/{id}              Update scan
DELETE /api/v1/scans/{id}             Delete scan
GET    /api/v1/scans/{id}/results      Get scan results (per-host port data)
POST   /api/v1/scans/{id}/start        Start a pending scan
POST   /api/v1/scans/{id}/stop         Stop a running scan
Discovery
GET    /api/v1/discovery                              List discovery jobs
POST   /api/v1/discovery                              Create discovery job
GET    /api/v1/discovery/compare?run_a={id}&run_b={id} Compare two discovery runs
GET    /api/v1/discovery/{id}                         Get discovery job
GET    /api/v1/discovery/{id}/diff                    Get new/gone/changed hosts for a run
POST   /api/v1/discovery/{id}/start                   Start discovery
POST   /api/v1/discovery/{id}/stop                    Stop discovery

The /compare endpoint returns a DiscoveryCompareDiff object with new_hosts, gone_hosts, changed_hosts, and unchanged_count. Comparing a run with itself short-circuits to all-unchanged. Comparing runs from different networks returns 422.

Profiles
GET    /api/v1/profiles                List scan profiles
POST   /api/v1/profiles                Create profile
GET    /api/v1/profiles/{id}           Get profile
PUT    /api/v1/profiles/{id}           Update profile
DELETE /api/v1/profiles/{id}           Delete profile
POST   /api/v1/profiles/{id}/clone     Clone profile (returns new profile)
Schedules
GET    /api/v1/schedules               List scheduled jobs
POST   /api/v1/schedules               Create schedule
GET    /api/v1/schedules/{id}          Get schedule
PUT    /api/v1/schedules/{id}          Update schedule
DELETE /api/v1/schedules/{id}          Delete schedule
POST   /api/v1/schedules/{id}/enable   Enable schedule
POST   /api/v1/schedules/{id}/disable  Disable schedule
GET    /api/v1/schedules/{id}/next-run Next scheduled execution time
Networks
GET    /api/v1/networks                         List networks
POST   /api/v1/networks                         Create network
GET    /api/v1/networks/stats                   Aggregated stats across all networks
GET    /api/v1/networks/{id}                    Get network
PUT    /api/v1/networks/{id}                    Update network
DELETE /api/v1/networks/{id}                    Delete network
POST   /api/v1/networks/{id}/enable             Enable network
POST   /api/v1/networks/{id}/disable            Disable network
PUT    /api/v1/networks/{id}/rename             Rename network
POST   /api/v1/networks/{id}/discover           Start a discovery run on this network
GET    /api/v1/networks/{id}/discovery          List discovery jobs for this network
POST   /api/v1/networks/{id}/scan               Start a scan against this network's hosts
GET    /api/v1/networks/{id}/exclusions         List exclusions for this network
POST   /api/v1/networks/{id}/exclusions         Add an exclusion to this network
Exclusions
GET    /api/v1/exclusions              List global exclusions
POST   /api/v1/exclusions              Create global exclusion
DELETE /api/v1/exclusions/{id}         Delete exclusion
API Keys
GET    /api/v1/apikeys                 List API keys
POST   /api/v1/apikeys                 Create API key
DELETE /api/v1/apikeys/{id}            Revoke API key
WebSocket
GET  /api/v1/ws          General event stream (discovery updates, host changes)
GET  /api/v1/ws/scans    Scan progress stream
GET  /api/v1/ws/logs     Live log tail stream

Full interactive documentation at http://localhost:8080/swagger/.

Example: submit a scan and poll for results
# Create and start a scan
curl -s -X POST http://localhost:8080/api/v1/scans \
  -H "X-API-Key: $SCANORAMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "quick-check",
    "targets": ["192.168.1.0/24"],
    "scan_type": "connect",
    "ports": "22,80,443"
  }' | jq .id

# Start it
curl -s -X POST http://localhost:8080/api/v1/scans/<id>/start \
  -H "X-API-Key: $SCANORAMA_API_KEY"

# Poll for status
curl -s http://localhost:8080/api/v1/scans/<id> \
  -H "X-API-Key: $SCANORAMA_API_KEY" | jq .status

# Fetch results
curl -s "http://localhost:8080/api/v1/scans/<id>/results?limit=50" \
  -H "X-API-Key: $SCANORAMA_API_KEY" | jq .
Example: filter hosts with compound conditions
# Linux hosts that are up and have port 22 open
FILTER=$(printf '{"op":"AND","conditions":[{"field":"status","cmp":"is","value":"up"},{"field":"os_family","cmp":"is","value":"Linux"},{"field":"open_port","cmp":"is","value":"22"}]}')

curl -s "http://localhost:8080/api/v1/hosts?filter=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$FILTER")" \
  -H "X-API-Key: $SCANORAMA_API_KEY" | jq '.data | length'

Docker

The production Docker Compose stack runs Scanorama, PostgreSQL, and Nginx. Optional profiles add Prometheus and Grafana.

cd docker

# Create secrets
echo "strong_db_password" > secrets/postgres_password.txt
echo "your-api-key"       > secrets/api_key.txt

# Start core services
docker compose up -d

# With monitoring (Prometheus + Grafana)
docker compose --profile monitoring up -d

# With Redis cache
docker compose --profile cache up -d
Service Port Notes
Nginx 80 / 443 Reverse proxy with TLS
Scanorama API 8080 Direct access
PostgreSQL 5432 Internal only
Prometheus 9090 --profile monitoring
Grafana 3000 --profile monitoring

Database Migrations

Migrations run automatically on startup. The schema is managed via numbered SQL files in internal/db/.

Migration Description
001_initial_schema.sql Base schema (hosts, scans, networks, profiles, schedules, exclusions)
002_host_targets.sql Host target associations for scan jobs
003_discovery_network_link.sql Link between discovery jobs and networks
004_host_status_model.sql gone status, host_status_events trigger for transition tracking
005_response_time.sql response_time_min_ms, response_time_max_ms, response_time_avg_ms on hosts
006_scan_duration.sql scan_duration_ms on port_scans
007_timeout_events.sql host_timeout_events table for timeout frequency tracking

Project Structure

scanorama/
├── cmd/
│   └── scanorama/          Entry point; Cobra CLI commands
├── internal/
│   ├── api/
│   │   ├── handlers/       HTTP handlers, request/response types, mocks
│   │   └── routes.go       Route registration
│   ├── db/                 Repository layer, models, migrations, filter expression engine
│   ├── scanning/           nmap integration, scan worker pool, result parsing
│   ├── discovery/          Discovery engine (ping/TCP/ARP, diff calculation)
│   ├── scheduler/          Cron-based job scheduler (robfig/cron)
│   ├── services/           Business logic layer between handlers and repositories
│   ├── metrics/            Prometheus metrics definitions
│   ├── logging/            slog wrapper
│   └── ws/                 WebSocket hub and broadcast logic
├── frontend/
│   ├── src/
│   │   ├── api/            Type-safe fetch client, TanStack Query hooks
│   │   ├── components/     Shared UI components (FilterBuilder, StatusBadge, etc.)
│   │   ├── lib/            Utilities (filter-expr, date formatting, WebSocket)
│   │   └── routes/         Page components, one file per route
│   └── package.json
├── config/environments/    Example and environment-specific config files
├── docker/                 Production Docker Compose stack
├── docs/
│   ├── planning/           ROADMAP.md, FRONTEND_PLAN.md
│   └── swagger/            Generated OpenAPI spec
└── test/                   Integration test helpers

License

MIT — see LICENSE for details.

Directories

Path Synopsis
cmd
cli
Package cli provides command-line interface commands for the Scanorama network scanner.
Package cli provides command-line interface commands for the Scanorama network scanner.
scanorama command
Package main provides the entry point for the Scanorama network scanning application.
Package main provides the entry point for the Scanorama network scanning application.
Package docs provides Swagger documentation for the Scanorama API.
Package docs provides Swagger documentation for the Scanorama API.
swagger
Package swagger Code generated by swaggo/swag.
Package swagger Code generated by swaggo/swag.
internal
api
Package api — HTTP response helpers, pagination, and query parameter utilities.
Package api — HTTP response helpers, pagination, and query parameter utilities.
api/handlers
Package handlers provides HTTP request handlers for the Scanorama API.
Package handlers provides HTTP request handlers for the Scanorama API.
api/handlers/mocks
Package mocks is a generated GoMock package.
Package mocks is a generated GoMock package.
api/middleware
Package middleware provides HTTP middleware functions for the Scanorama API server.
Package middleware provides HTTP middleware functions for the Scanorama API server.
auth
Package auth provides authentication utilities for the Scanorama API server.
Package auth provides authentication utilities for the Scanorama API server.
config
Package config provides configuration management for scanorama.
Package config provides configuration management for scanorama.
daemon
Package daemon provides the background service functionality for scanorama.
Package daemon provides the background service functionality for scanorama.
db
Package db provides database connectivity and data models for scanorama.
Package db provides database connectivity and data models for scanorama.
discovery
Package discovery provides network discovery functionality using nmap.
Package discovery provides network discovery functionality using nmap.
dns
Package dns provides cached DNS resolution for scanorama.
Package dns provides cached DNS resolution for scanorama.
errors
Package errors provides structured error handling for scanorama operations.
Package errors provides structured error handling for scanorama operations.
logging
Package logging provides structured logging functionality using Go's slog package.
Package logging provides structured logging functionality using Go's slog package.
metrics
Package metrics provides interfaces for metrics collection and monitoring.
Package metrics provides interfaces for metrics collection and monitoring.
metrics/mocks
Package mocks is a generated GoMock package.
Package mocks is a generated GoMock package.
profiles
Package profiles provides scanning profile management for scanorama.
Package profiles provides scanning profile management for scanorama.
scanning
Package scanning provides core network scanning functionality for Scanorama.
Package scanning provides core network scanning functionality for Scanorama.
scheduler
Package scheduler provides job scheduling and execution functionality for scanorama.
Package scheduler provides job scheduling and execution functionality for scanorama.
services
Package services provides business logic services for Scanorama.
Package services provides business logic services for Scanorama.
workers
Package workers provides a worker pool implementation for concurrent operations in scanorama.
Package workers provides a worker pool implementation for concurrent operations in scanorama.
test
helpers
Package helpers provides testing utilities for database connections, environment setup, and test data management for Scanorama integration tests.
Package helpers provides testing utilities for database connections, environment setup, and test data management for Scanorama integration tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL