OpenHunterAI: The Security Scanner That Refuses to Trust Itself
Hook
Most AI security scanners fail because they're too autonomous. OpenHunterAI succeeds by making autonomy impossible—every scan requires human approval, and findings flow through a sanitization layer before storage.
Context
The explosion of AI-powered security tools has created a dangerous pattern: autonomous agents that scan targets indiscriminately, upload sensitive findings to cloud services, and generate mountains of false positives with no audit trail. Traditional tools like Burp Suite and ZAP weren't designed for AI integration, while newer AI security platforms prioritize automation over accountability. The result is tools that security teams can't trust in compliance-sensitive environments or against production systems.
OpenHunterAI takes the opposite approach. Built by LumosLab Innovation, it's a local-first security testing orchestrator that treats AI as a capability requiring strict boundaries rather than a feature to maximize. The architecture enforces domain verification before scanning, requires human approval for execution plans, and runs all security tools in isolated Go workers that can't modify their own scope. Instead of promising autonomous red teaming, it delivers auditable security assessments where every finding traces back to a specific tool execution with sanitized evidence. For organizations that need to prove their security work didn't accidentally compromise production data or scan unauthorized targets, this architectural paranoia is the entire point.
Technical Insight
The core architectural decision is radical separation of concerns through message-driven orchestration. The TypeScript frontend and Node.js API service handle scan planning, scope definition, and human approval gates, but they never execute security tools directly. Instead, they publish work messages to a NATS message bus, which Go workers consume to perform actual assessments. This separation isn't about performance—it's about enforcing trust boundaries.
Here's what a scan initialization looks like in the TypeScript API layer:
// Domain verification must succeed before any scan plan is created
const domainProof = await verifyDomainOwnership(targetDomain, {
method: 'dns-txt',
challenge: generateChallenge()
});
if (!domainProof.verified) {
throw new ScopeViolationError('Domain ownership not proven');
}
// Construct deterministic execution plan
const scanPlan = {
id: generatePlanId(),
scope: {
domains: [targetDomain],
allowedPorts: [80, 443],
excludedPaths: ['/admin', '/api/internal']
},
adapters: [
{ tool: 'playwright', config: { maxDepth: 3 } },
{ tool: 'nuclei', templates: ['cves', 'exposures'] },
{ tool: 'zap-passive', config: { spiderDuration: 300 } }
],
requiresApproval: true
};
// Plan goes to database, NOT directly to execution
await storePendingPlan(scanPlan);
Notice what doesn't happen: the API service never invokes Playwright, Nuclei, or ZAP directly. The plan sits in a pending state until a human reviews the scope and adapter configuration. Only after approval does the orchestrator publish messages to NATS with the frozen plan. Go workers consume these messages through a bounded adapter interface:
type SecurityAdapter interface {
// Workers receive scope as immutable context
Execute(ctx context.Context, scope ScopeDefinition) ([]Finding, error)
// Adapters cannot modify scope or access secrets directly
Name() string
Capabilities() []string
}
// Playwright adapter implementation
type PlaywrightAdapter struct {
browserPool *playwright.BrowserPool
}
func (p *PlaywrightAdapter) Execute(ctx context.Context, scope ScopeDefinition) ([]Finding, error) {
// Scope is read-only - attempting to scan out-of-scope domains fails fast
if err := validateScope(scope); err != nil {
return nil, fmt.Errorf("scope validation failed: %w", err)
}
findings := []Finding{}
for _, domain := range scope.Domains {
// Actual browser-based reconnaissance happens here
page, err := p.browserPool.NewPage()
if err != nil {
continue
}
// All HTTP requests are checked against scope boundaries
page.Route("**/*", func(route playwright.Route) {
if !scope.AllowsURL(route.Request().URL()) {
route.Abort()
return
}
route.Continue()
})
// Evidence collection, not exploitation
response, err := page.Goto(fmt.Sprintf("https://%s", domain))
// ... extract headers, technologies, endpoints
}
return findings, nil
}
The critical architectural component is the evidence gateway that sits between worker findings and persistent storage. Raw findings from adapters flow through a sanitization layer that strips sensitive data, validates finding structure, and separates observed signals from analyst judgment:
// Evidence gate implementation
class EvidenceGateway {
async ingestFindings(rawFindings: AdapterFinding[]): Promise<SanitizedEvidence[]> {
return rawFindings.map(finding => {
// Strip any credentials or tokens accidentally captured
const sanitizedRequest = this.sanitizeHttpData(finding.request);
const sanitizedResponse = this.sanitizeHttpData(finding.response);
return {
findingId: generateFindingId(),
adapter: finding.adapter,
timestamp: finding.timestamp,
target: finding.target,
// Observable facts only
evidence: {
request: sanitizedRequest,
response: sanitizedResponse,
observedBehavior: finding.behavior
},
// Analyst judgment is separate and optional
assessment: null,
severity: null,
// Full reproduction context
executionContext: {
planId: finding.planId,
adapterVersion: finding.adapterVersion,
configuration: finding.adapterConfig
}
};
});
}
private sanitizeHttpData(data: HttpData): SanitizedHttpData {
// Remove authorization headers, API keys, session tokens
const cleaned = { ...data };
cleaned.headers = Object.fromEntries(
Object.entries(data.headers)
.filter(([key]) => !SENSITIVE_HEADERS.includes(key.toLowerCase()))
);
return cleaned;
}
}
This architecture enables the coding-agent skill feature, which is genuinely novel. The npm package installs into .agents/skills/ and exposes security assessment capabilities through standardized agent protocols (OpenAI's Model Context Protocol, Anthropic's Claude tools, GitHub Codex functions). When an AI coding assistant invokes the skill, it discovers the local OpenHunterAI instance, constructs a scope-limited scan request, and streams back findings—all without the AI agent having write access to anything:
// Coding agent skill interface
export const openHunterSkill = {
name: 'security-scan',
description: 'Perform scoped security assessment of web application',
parameters: {
target: { type: 'string', description: 'Domain to assess' },
depth: { type: 'number', default: 2 }
},
async execute(params: ScanParams): Promise<SkillResult> {
// Discovers local OpenHunterAI instance via .openhunter config
const hunter = await discoverLocalInstance();
// Agent can only request scans, not execute them directly
const scanRequest = await hunter.requestScan({
target: params.target,
scope: 'read-only',
adapters: ['playwright', 'nuclei-safe']
});
// Human approval gate - agent waits
const approved = await scanRequest.waitForApproval();
if (!approved) {
return { status: 'rejected', reason: 'User denied scope' };
}
// Stream findings back to agent context
for await (const finding of scanRequest.streamFindings()) {
yield finding;
}
}
};
The separation of deterministic tools (ZAP proxy analysis, Nuclei template matching) from AI-assisted adapters (OpenHack/Strix runtimes for exploitation chains) happens at the worker level. Each adapter declares its capabilities and required approvals. Deterministic tools can run automatically post-approval, while AI-assisted adapters require per-invocation confirmation because they might attempt authentication bypasses or exploit chains that could impact target systems.
Gotcha
The architecture's strength—strict boundaries and approval gates—is also its operational burden. You're running a full Docker Compose stack with NATS, Postgres, TypeScript API service, and Go worker containers just to orchestrate security tools you could invoke directly from shell scripts. For quick vulnerability scans or CI/CD integration, this overhead is absurd. The scope verification process assumes you control DNS or can place verification files on the target, which doesn't work for third-party assessments or bug bounty scenarios where you have authorization but not infrastructure control.
The bigger problem is that the differentiating features are incomplete. Nuclei integration ships without curated templates because the maintainers haven't decided which community templates are safe for autonomous execution. OpenHack and Strix runtimes for AI-assisted exploitation require separate installation with unclear licensing, making the 'AI red team' positioning aspirational. Out of the box, you get a heavyweight orchestrator for Playwright reconnaissance and ZAP passive scanning—capabilities you already have with simpler tools. The coding-agent skill is genuinely innovative, but it requires your AI assistant to support the skill protocol and you to keep a local OpenHunterAI instance running in Docker, which conflicts with the 'local means your machine' security posture when the agent is cloud-based. Alpha maturity warnings are prominent, and the documentation explicitly states that a clean report doesn't prove security, shifting liability entirely to users.
Verdict
Use if: you're building custom security testing workflows for compliance-sensitive environments where audit trails and evidence provenance matter more than speed, you already operate Docker-based infrastructure and understand NATS message patterns, you need to integrate security scanning into AI coding agents through standardized protocols, or you're solving the specific problem of preventing autonomous security tools from exceeding authorized scope. The architecture genuinely solves real problems with AI security tools going rogue. Skip if: you need production-ready vulnerability scanning today, you're looking for authenticated application testing or exploit development capabilities, you want simple CLI tools for CI/CD pipelines, you don't have operational expertise with container orchestration, or you expected 'AI red team' to mean autonomous exploitation rather than orchestrated tool execution with optional AI-assisted triage. Burp Suite Professional, standalone Nuclei, or even custom Playwright scripts will serve you better unless you're specifically solving for local-first evidence chains and AI agent integration.