Features • Installation • Usage • Schema • Platforms • Restraint
herald reads platform YAML configs, sweeps a list of IPs, and outputs NDJSON findings. It is the HTTP application-layer auth probe in the NuClide chain: after aimap answers "what service is this?" and scanner answers "what version?", herald answers "is this open?"
Population-scale surveys of AI/LLM infrastructure (Open WebUI, Dify, Flowise, Langfuse, LiteLLM, AnythingLLM, LibreChat, LobeChat, RAGFlow, OpenHands, Phoenix) follow a repeating pattern:
shodan download -> extract IP:port list -> hit 2-3 endpoints per host
-> parse JSON field -> classify open / closed -> structured output
Every survey used to ship a one-off probe script. herald collapses that work to a YAML config: one file per platform, declaring the endpoints, the JSON field paths to inspect, the values that constitute an "open" finding, and any extra fields to extract.
- Declarative YAML probes. New platform is a new file, no code changes
- 11 platforms shipped: Open WebUI, Dify, Flowise, Langfuse, LiteLLM, AnythingLLM, LibreChat, LobeChat, RAGFlow, OpenHands, Phoenix
- 4 match types: dot-path field equals value, field exists, top-level array non-empty, body substring
- 3 extract types: dot-path field, array count, indexed-array field
- Go concurrency, 1,140 Langfuse hosts in under 60 seconds at 50 workers
- NDJSON output. Pipe to
visorlog ingest,jq,wc -l, or anywhere else - Single static binary, standard library plus
gopkg.in/yaml.v3 - Reads metadata only. Does not authenticate, register accounts, or invoke LLM completions
go install -v github.com/nuclide-research/herald@latestOr build from source:
git clone https://github.com/nuclide-research/herald
cd herald
go build -o herald .Requires Go 1.21 or later.
# List available platforms
herald -list
# Sweep
cat ip-port.txt | herald -platform dify
# With explicit options
cat ip-port.txt | herald \
-platform open-webui \
-workers 50 \
-timeout 7 \
-platforms ./platformsInput format: one IP:PORT:SCHEME per line.
124.220.32.195:80:http
206.206.192.179:3000:http
85.214.93.104:4000:http
Output format: NDJSON, one finding per line.
{"platform":"langfuse","ip":"206.206.192.179","port":3000,"scheme":"http","probe_id":"signup_open","finding":"SIGNUP_OPEN","severity":"medium","ts":"2026-06-06T08:33:37Z"}
{"platform":"langfuse","ip":"206.206.192.179","port":3000,"scheme":"http","probe_id":"health_open","finding":"HEALTH_OPEN","severity":"info","fields":{"version":"3.132.0"},"ts":"2026-06-06T08:33:37Z"}Example (platforms/dify.yaml):
name: dify
description: "Dify LLM app development platform"
default_ports: [80, 443, 3000, 8080]
probes:
- id: signup_open
endpoint: /console/api/system-features
match:
field: is_allow_register
value: true
finding: SIGNUP_OPEN
severity: high
extract:
- field: sso_enforced_for_signin
as: sso
- field: "license.status"
as: licenseMatch types:
fieldplusvalue: dot-path field equals expected valuefieldplusexists: true: field exists at patharray_nonempty: true: top-level array (ordataarray) has elementsbody_contains: raw substring match against response body
Extract types:
fieldplusas: extract dot-path field valuearray_count: count elements in top-level arrayarray_fieldplusindexplusas: extract a field from a specific array element
| Platform | Probes | Validated against |
|---|---|---|
| dify | 3 | 1,600-host Cat-DF survey (2026-06-06) |
| open-webui | 4 | 5,097-host Cat-OW survey (2026-06-06) |
| flowise | 1 | 841-host Cat-FW survey (2026-06-06) |
| langfuse | 2 | 1,140-host Cat-LF survey (2026-06-06) |
| litellm | 2 | 2,209-host Cat-05 survey (2026-06-06) |
| anythingllm | 1 | 232-host survey (2026-06-06, all closed) |
| librechat | shipped | LibreChat OSS survey |
| lobechat | shipped | LobeChat OSS survey |
| ragflow | shipped | RAGFlow OSS survey |
| openhands | shipped | OpenHands OSS survey |
| phoenix | shipped | Arize Phoenix survey |
| Tool | Role |
|---|---|
| aimap | TCP/TLS fingerprinting plus service deep enumeration, "what is this?" |
| scanner | Liveness plus banner plus version, "is this alive, what version?" |
| herald | HTTP application-layer auth probe, "is this open?" |
The three are complementary. herald fills the gap between "this is Dify" (aimap) and "this Dify has auth disabled" (formerly manual, now declarative YAML).
- Security with Go (Packt, 2018): channel-semaphore concurrency pattern, custom TLS transport,
url.Parsepath injection - Powerful Command-Line Applications in Go (Pragmatic, 2021): HTTP client architecture, NDJSON output, sentinel errors
- Hacking APIs (No Starch, 2022): the public-system-info endpoint discovery pattern that motivates Dify's
/console/api/system-featuresprobe
herald reads metadata. It does not authenticate, register accounts, or invoke LLM completions. The probes hit endpoints that are intentionally public (config endpoints, sign-in pages, version disclosures) and classify the host based on what the operator's configuration reveals.
Where a finding requires the next step (for example, confirming that is_allow_register: true actually allows registration), that step is performed manually with explicit operator or CERT coordination. Never automated at population scale.
- aimap, AI/ML infrastructure fingerprint scanner
- scanner, full-handshake banner stage between passive discovery and deep enumeration
- glance, schema-only sensitivity analyzer for sealed corpora
- VisorLog, finding ledger and ingest pipeline
- BARE, semantic exploit-module ranking over scanner findings
MIT. Part of the NuClide toolchain. Contact: nuclide-research.com