Architecture
Technical overview of Scanopy's system design and components.
Technical overview of Scanopy's system design, components, and data flows.
Components
Server
Purpose: Central hub for data storage, API, and web UI serving
Responsibilities:
- Store network discovery data in PostgreSQL
- Serve REST API for daemons and UI
- Generate topology visualizations
- Manage user authentication and sessions
- Orchestrate scheduled discoveries
- Handle organization and user management
- Provide real-time updates via Server-Sent Events
Implementation:
- Language: Rust
- Framework: Axum (async web framework)
- Database: PostgreSQL 17 with sqlx
- Authentication: tower-sessions + OIDC (openidconnect crate)
- Frontend bundling: Integrated Svelte build in Docker image
Runs as: Docker container (recommended) or standalone binary
Daemon
Purpose: Distributed discovery agent that scans networks and reports findings
Responsibilities:
- Scan IPv4 addresses on configured subnets
- Detect open TCP ports
- Identify services via pattern matching
- Connect to Docker socket for container discovery
- Report host interfaces and capabilities
- Send heartbeats to maintain connection
- Execute scheduled discovery tasks
Implementation:
- Language: Rust
- Network scanning: Custom async TCP scanner with tokio
- Docker API: bollard crate for Docker socket communication
- Service detection: Pattern matching engine with 200+ definitions
- Configuration: JSON file + environment variables + CLI args
Runs as: Docker container (Linux only) or standalone binary (all platforms)
UI
Purpose: Web-based interface for viewing and managing network data
Responsibilities:
- Display interactive topology diagrams
- Provide CRUD interfaces for all entities
- Monitor discovery sessions in real-time
- Manage users and organizations
- Configure discovery schedules
- Export topology visualizations
Implementation:
- Framework: Svelte 5 + SvelteKit
- State management: Svelte stores with derived reactivity
- Visualization: @xyflow/svelte for topology rendering
- Forms: svelte-forms with custom validation
- Styling: Tailwind CSS
- Real-time: Native EventSource for SSE
Runs as: Static files served by the server (bundled in Docker image)
Data Flows
Discovery Flow (Pull Mode)
1. User triggers discovery (manual or scheduled)
│
▼
2. Server creates discovery session
│
▼
3. Daemon polls server for work, retrieves discovery session
│
▼
4. Daemon executes scan:
├─ Network Scan: Scans IP range, detects ports/services
├─ Docker Scan: Queries Docker socket for containers
└─ Self-Report: Reports own capabilities
│
▼
5. Daemon sends discovered entities to server (batched)
│
▼
6. Server processes and stores entities:
├─ Creates/updates hosts
├─ Creates/updates interfaces
├─ Creates/updates services
├─ Associates with subnets
└─ Logs discovery metadata
│
▼
7. Server broadcasts progress updates via SSE
│
▼
8. UI receives real-time updates and refreshes display
│
▼
9. Discovery completes, final status recordedDiscovery Flow (Push Mode)
Same as Pull mode, except in step 3 the server initiates the connection to the daemon instead of the daemon polling.
Discovery Pipeline
Network Scan
┌──────────────────────────────────────────────────────────┐
│ Initialization │
│ 1. Determine target subnets │
│ 2. Calculate total IP count │
│ 3. Establish concurrent scan limit │
└───────────────────┬──────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ Per-IP Scan (Parallel) │
│ 1. TCP port scan (common + custom ports) │
│ 2. HTTP endpoint probing │
│ 3. Collect responses and headers │
└───────────────────┬──────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ Service Pattern Matching │
│ 1. Match port numbers │
│ 2. Match HTTP endpoints │
│ 3. Check headers and content │
│ 4. Apply MAC vendor patterns │
│ 5. Assign confidence score │
└───────────────────┬──────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ Host Assembly │
│ 1. Perform reverse DNS lookup │
│ 2. Determine hostname (DNS > service > IP) │
│ 3. Group services by interface │
│ 4. Associate with subnet │
└───────────────────┬──────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ Report to Server │
│ 1. Send discovered host + interfaces + services │
│ 2. Server stores in database │
│ 3. Server broadcasts update via SSE │
└──────────────────────────────────────────────────────────┘Docker Discovery
┌──────────────────────────────────────────────────────────┐
│ Connect to Docker Socket │
│ - Verify socket accessibility │
│ - Authenticate (if required) │
└───────────────────┬──────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ List Running Containers │
│ - Get container IDs, names, labels │
│ - Extract port mappings │
│ - Identify networks │
└───────────────────┬──────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ Inspect Docker Networks │
│ - Enumerate bridge networks │
│ - Map containers to network subnets │
│ - Extract gateway IPs │
└───────────────────┬──────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ Match Services to Containers │
│ - Use port mappings to identify services │
│ - Check container labels for hints │
│ - Apply same pattern matching as network scan │
└───────────────────┬──────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ Create Container-Host Relationships │
│ - Link containers to Docker host │
│ - Set virtualization metadata │
│ - Create subnet for Docker bridge │
└───────────────────┬──────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ Report to Server │
│ - Send containers as hosts │
│ - Send Docker subnets │
│ - Link to host via virtualization field │
└──────────────────────────────────────────────────────────┘For implementation details, see the source code.