Skip to content

Repository files navigation

Mycelium

Continuous threat exposure management platform. Seed it with a domain, IP, or ASN and it maps your full attack surface as a graph — discovering subdomains, open ports, services, certificates, and CVEs by orchestrating open-source recon tools as isolated microservice workers.

Every finding is a node. Every causal relationship is a directed edge. The result is a queryable graph you can traverse from seed to CVE in a single query.

Early-stage implementation. The core architecture, worker model, and repository layout are established. Additional connectors, processing stages, and production hardening are still in development. Read what follows as the design it is being built to, not a description of a finished system.


How it works

  1. You POST a scan with a seed (e.g. example.com, type Domain)
  2. The engine dispatches jobs to workers over NATS JetStream
  3. Workers run recon tools, publish findings back to the engine
  4. The engine merges nodes and edges into Neo4j, deduplicates, and dispatches downstream jobs for newly discovered nodes
  5. The cycle continues until no new nodes are produced
  6. Query the graph or view results via the API / Neo4j Browser
Domain ──RESOLVES_TO──► IP ──HAS_PORT──► Port ──RUNS_SERVICE──► Service
  │                                                                  │
  └──HAS_MX──► Hostname                              VULNERABLE_TO──► CVE
  │
  └──HAS_NS──► Hostname ──RESOLVES_TO──► IP ──HAS_PORT──► Port ...

Stack

Component Technology
API + engine Go
Graph database Neo4j 5 Community
Message bus NATS JetStream
Workers Go (one binary per tool)
Frontend React + Vite (Phase 4)
Deployment Docker Compose

Quickstart

Prerequisites: Docker + Docker Compose

git clone https://github.com/DevelopSolutionsLLC/mycelium
cd mycelium
cp config.example.yaml config.yaml

Edit config.yaml — set a strong admin_key, change the Neo4j password if needed.

make up       # starts neo4j, nats, api, worker-dns
make logs     # tail all service logs

The API is live at http://localhost:8080. Neo4j Browser is at http://localhost:7474.

Create a tenant and get an API key:

curl -X POST http://localhost:8080/api/tenants \
  -H "Authorization: Bearer <your-admin-key>" \
  -H "Content-Type: application/json" \
  -d '{"name": "acme", "plan": "pro"}'
# Response includes api_key — save it, it's shown once

Start a scan:

curl -X POST http://localhost:8080/api/scans \
  -H "Authorization: Bearer <tenant-api-key>" \
  -H "Content-Type: application/json" \
  -d '{"seed": "example.com", "seed_type": "Domain"}'

Check results:

curl http://localhost:8080/api/scans/<scan-id>/graph \
  -H "Authorization: Bearer <tenant-api-key>"

Or open Neo4j Browser at http://localhost:7474 and run:

MATCH path = (d:Domain)-[*1..6]->(n)
WHERE d.value = 'example.com'
RETURN path LIMIT 100

Scaling workers

Each worker type is a separate container with independent resource limits. Scale any of them without touching the others:

make scale svc=worker-dns n=4
make scale svc=worker-nuclei n=2

API reference

Method Endpoint Auth Description
GET /api/health none Health check
POST /api/tenants admin key Create tenant, returns API key
POST /api/tenants/keys admin key Add API key to existing tenant
POST /api/scans tenant key Start a scan
GET /api/scans tenant key List scans
GET /api/scans/:id tenant key Scan status + node count
GET /api/scans/:id/graph tenant key Full node/edge graph for the scan

Seed types: Domain, Hostname, IP, URL, ASN, Email


Workers

Worker Status Triggers Produces
dns ✅ Built Domain, Hostname IP, Hostname (MX/NS)
http 🔜 Phase 2 Hostname, IP URL, Certificate, Service
certs 🔜 Phase 2 Domain Hostname, Certificate
subfinder 🔜 Phase 2 Domain Hostname
nmap 🔜 Phase 3 IP Port, Service
nuclei 🔜 Phase 3 URL, Port CVE
shodan 🔜 Phase 3 IP Port, Service, CVE

Adding a worker

Every worker implements a single interface:

type Worker interface {
    Name()    string
    Subject() string
    Process(ctx context.Context, job model.JobMsg) ([]model.Finding, error)
}

Create workers/<name>/main.go, call base.Run(), add a Dockerfile, add the service to docker-compose.yml. The engine routes jobs to it automatically based on the NATS subject.


Project structure

cmd/myco/          — API server + engine entrypoint
internal/
  model/           — shared types (Node, Edge, Finding, Job, Result, Tenant, Scan)
  graph/           — Neo4j client (tenant-scoped queries, MERGE helpers)
  natsconn/        — NATS JetStream client (streams, pub/sub)
  engine/          — result consumer, deduplication, job dispatch
  api/             — Chi router, auth middleware, REST handlers
  config/          — YAML config loader
workers/
  base/            — Worker interface + Run() harness
  dns/             — DNS resolution worker
ui/                — React + Vite frontend (Phase 4)
correlations/      — YAML correlation rules (Phase 3)
docker-compose.yml
Makefile
config.example.yaml

Multi-tenancy

All data is namespace-isolated by tenant_id. Every node in Neo4j carries a tenant_id property and every query filters by it — tenants cannot see each other's data. API keys are hashed (SHA-256) at rest; the plain key is returned once at creation and never stored.

Plans: free (5 scans, 1K nodes), pro (50 scans, 50K nodes), enterprise (500 scans, 500K nodes).


Development

# Run tests
go test ./...

# Build everything
go build ./...

# Local dev (requires neo4j + nats running separately)
cp config.example.yaml config.yaml
go run ./cmd/myco -config config.yaml

# UI dev server (proxies /api → localhost:8080)
cd ui && npm install && npm run dev

About

Continuous threat exposure management engine that maps attack surface as a graph — Go, NATS JetStream, Neo4j

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages