Skip to content

Architecture

System Overview

flowchart TB
    subgraph Ingest["① Ingest Layer (Node.js :4000)"]
        WS[WebSocket /ws/events]
        REST[REST POST /events]
        ZOD[Zod Schema Validation]
        RL[Token Bucket Rate Limiter]
        WS --> ZOD
        REST --> RL --> ZOD
    end

    subgraph Stream["② Message Bus (Redis Streams)"]
        RS[(threats:stream\nXADD / XREAD BLOCK)]
    end

    subgraph Detection["③ Detection Engine (Go)"]
        CLAM[ClamAV TCP\nINSTREAM scan]
        VT[VirusTotal v3\nHash lookup]
        ABUSE[AbuseIPDB v2\nIP reputation]
        ENG[Engine\nSeverity + MITRE]
        CLAM --> ENG
        VT   --> ENG
        ABUSE --> ENG
    end

    subgraph Cache["Redis Cache"]
        LRU[(LRU 50k entries\n1h TTL)]
    end

    subgraph DB["④ Persistence (PostgreSQL + TimescaleDB)"]
        HT[(events hypertable\n7-day retention)]
        CA[(threat_summary_1m\ncontinuous aggregate)]
        HT --> CA
    end

    subgraph Viz["⑤ Visualization (Next.js :3000)"]
        GLOBE[Three.js Globe\n500 arcs @ 60fps]
        GRAPH[D3 Force Graph]
        FEED[react-virtual Feed\n100k rows]
        WS2[WebSocket\nfan-out route]
        WS2 --> GLOBE & GRAPH & FEED
    end

    subgraph Obs["⑥ Observability (OpenTelemetry)"]
        OTEL[OTLP Spans\ningest → scan → verdict]
    end

    ZOD --> RS
    RS --> Detection
    RS --> WS2
    Detection --> DB
    Detection --> Cache
    Detection --> OTEL

Component Responsibilities

Ingest (apps/ingest/)

Component Role
server.ts Express app, REST POST /events, /health
ws.ts WebSocket server, per-client rate limiting
schema.ts Zod schemas — rejects malformed events before Redis
ratelimiter.ts Token bucket, per-IP, in-memory
redis.ts XADD threats:stream publisher

Detection Engine (internal/)

Package Role
detector Orchestrates all scanners, computes severity + MITRE tactic
clamav TCP INSTREAM protocol, parses OK/FOUND/ERROR
virustotal v3 /files/{hash} lookup, aggregates engine counts
abuseipdb v2 /check with Redis LRU cache
cache Redis wrapper, TTL-based, Ping healthcheck
otel OTLP tracer provider, stdout exporter (swap for prod collector)

Dashboard (apps/dashboard/)

Component Role
Globe.tsx Three.js WebGL, instanced mesh particles, Earth texture, arc severity coloring
ThreatGraph.tsx D3 force-directed, drag, zoom, IP → event filter
EventFeed.tsx react-virtual, 100k rows, filter bar, severity/tactic multi-select
StatsSidebar.tsx Live metrics, severity breakdown, event detail panel
useEventStream.ts Custom hook, useSyncExternalStore, WS reconnect, 50k event cap
app/api/ws/route.ts Redis XREAD BLOCK fan-out, 30s heartbeat

Data Flow

Event source
  → POST /events (or WS /ws/events)
  → Zod validation
  → Redis XADD threats:stream
  → [detector consumes XREAD]
  → ClamAV + VirusTotal + AbuseIPDB (parallel)
  → severity + MITRE tactic computed
  → INSERT INTO events (TimescaleDB)
  → [dashboard WS route reads same stream]
  → broadcast to all connected SOC clients
  → Globe arc + Feed row + Graph node update

Latency Budget

Stage Budget Actual (p99)
Ingest validation < 1ms ~0.3ms
Redis XADD < 2ms ~0.8ms
Detection engine (orchestration) < 50ms 221 ns
ClamAV scan (network) < 30ms ~5–15ms
VirusTotal lookup (network) < 200ms ~80–150ms
AbuseIPDB lookup (cached) < 1ms ~0.5ms
Dashboard WebSocket fan-out < 5ms ~1–3ms