Scan Engine
createScanEngine for advanced use. Configure models, TAP parameters, Best-of-N, the inspector and orchestrator, injection probe selection, and callbacks.
Use createScanEngine for more control than runSecurityScan provides. It accepts an EngineConfig and returns a ScanEngine with a runScan() method.
Basic usage
import { createScanEngine } from "zeroleaks";
const engine = createScanEngine({
scan: {
maxTurns: 20,
maxTreeDepth: 4,
branchingFactor: 3,
enableBestOfN: true,
bestOfNCount: 3,
},
});
const result = await engine.runScan(systemPrompt, {
onProgress: async (progress) => {
console.log(`Turn ${progress.turn}/${progress.maxTurns} (${progress.phase})`);
},
onFinding: async (finding) => {
console.log("Finding:", finding.technique, finding.severity);
},
});createScanEngine applies the defaults below to omitted fields. A field set to undefined or an empty string is treated as omitted. In 1.4.0, an explicit undefined erased the default; version 1.5.0 fixes this behavior.
EngineConfig
interface EngineConfig {
apiKey?: string; // defaults to OPENROUTER_API_KEY
scan?: Partial<ScanConfig>; // the options in the table below
attacker?: AttackerConfig; // per-agent overrides, e.g. { model }
evaluator?: EvaluatorConfig;
mutator?: MutatorConfig;
strategist?: StrategistConfig;
target?: TargetConfig; // { model?, apiKey? }
}Scan options
| Option | Type | Default | Description |
|---|---|---|---|
maxTurns | number | 25 | Maximum extraction turns |
maxTreeDepth | number | 4 | TAP tree depth |
branchingFactor | number | 3 | TAP branching factor |
pruningThreshold | number | 0.3 | Candidates scoring below this are pruned |
enableBestOfN | boolean | true | Generate Best-of-N variations of some attacks |
bestOfNCount | number | 3 | Variations per Best-of-N round |
attackerModel | string | anthropic/claude-opus-4.8 | Model for the attacker, strategist, and mutator |
evaluatorModel | string | anthropic/claude-sonnet-5 | Model for the evaluator, and for the inspector unless inspectorModel is set |
targetModel | string | anthropic/claude-sonnet-5 | Model that runs your system prompt |
inspectorModel | string | evaluator model | Model for the inspector |
injectionEvaluatorModel | string | evaluator model | Model for the injection compliance judge |
enableInspector | boolean | true | Create the defense inspector |
enableDefenseFingerprinting | boolean | false | Run the inspector on every response and call onDefenseDetected when it identifies a known guardrail. Needs enableInspector. |
enableMultiTurnOrchestrator | boolean | true | Scripted multi-turn sequences (Siren, Echo Chamber, TombRaider) |
orchestratorPattern | "auto" | "siren" | "echo_chamber" | "tombRaider" | none | Sequence to start with when the orchestrator is on |
enableAdaptiveTemperature | boolean | false | AutoAdv-style temperature scheduling. Also creates the orchestrator. |
temperatureConfig | Partial<TemperatureConfig> | DEFAULT_TEMPERATURE_CONFIG | Temperature schedule for the orchestrator |
scanMode | "extraction" | "injection" | "extraction" | Mode for a single-mode scan |
enableDualMode | boolean | false | Run extraction and injection in parallel. Overrides scanMode. |
injectionCategories | string[] | all | Injection probe categories to run |
injectionSeverities | string[] | all | Injection probe severities to run |
maxInjectionProbes | number | 20 | Maximum injection probes, in severity order. 0 runs all. |
enableMultiTurnInjection | boolean | true | Include multi-turn grooming injection probes |
ScanConfig also declares enableCrescendo, enableManyShot, maxTokensPerTurn, maxTotalTokens, enableVectorMemory, enableParallelEvaluation, enableFailureAnalysis, and injectionTestTypes. The 1.5.0 engine accepts them but does not read them.
runScan options
engine.runScan(systemPrompt, {
onProgress?: (progress: ScanProgress) => Promise<void>,
onFinding?: (finding: Finding) => Promise<void>,
onDefenseDetected?: (fingerprint: DefenseFingerprint) => Promise<void>,
onInjectionResult?: (result: InjectionTestResult) => Promise<void>,
maxDurationMs?: number, // default 0, no limit
});With a time limit set, the extraction loop stops when less than 30 seconds of the budget remain.
onProgress is called on each turn:
interface ScanProgress {
turn: number;
maxTurns: number;
phase: AttackPhase; // "reconnaissance" | "profiling" | "soft_probe" | "escalation" | "exploitation" | "persistence"
strategy: string;
leakStatus: LeakStatus; // "none" | "hint" | "fragment" | "substantial" | "complete"
findingsCount: number;
treeNodesExplored: number;
estimatedCompletion: number; // 0-1
}onFinding is called for each extraction finding:
interface Finding {
id: string;
turn: number;
timestamp: number;
technique: string;
category: AttackCategory;
severity: "critical" | "high" | "medium" | "low";
confidence: "high" | "medium" | "low";
extractedContent: string;
contentType: "system_prompt" | "rule" | "constraint" | "capability" | "persona" | "unknown";
evidence: string;
attackNodeId: string;
verified: boolean;
verificationMethod?: string;
}onInjectionResult is called for each injection probe. Useful fields on InjectionTestResult are success, compliance ("full", "partial", or "refused"), severity, technique, probeCategory, intent, and evidence.
Dual-mode scan
Set enableDualMode and call runScan. Extraction and injection run in parallel against two separate target sessions.
const engine = createScanEngine({ scan: { enableDualMode: true } });
const result = await engine.runScan(systemPrompt, {
maxDurationMs: 120000,
onProgress: async (p) => console.log(p.turn, p.maxTurns),
onFinding: async (f) => console.log(f.technique),
onInjectionResult: async (r) => console.log(r.technique, r.success, r.compliance),
});
console.log(result.overallScore, result.injectionScore);Custom engine example
import { createScanEngine } from "zeroleaks";
const engine = createScanEngine({
apiKey: process.env.OPENROUTER_API_KEY,
scan: {
maxTurns: 10,
maxTreeDepth: 3,
branchingFactor: 2,
enableBestOfN: true,
bestOfNCount: 5,
targetModel: "openai/gpt-5",
},
});
const result = await engine.runScan(systemPrompt, {
maxDurationMs: 60000,
onProgress: async (p) => {
process.stdout.write(`\rTurn ${p.turn}/${p.maxTurns} (${p.leakStatus})`);
},
onFinding: async (f) => {
console.log(`\n[!] ${f.severity}: ${f.technique}`);
},
});
console.log(`\nScore: ${result.overallScore}/100 (${result.overallVulnerability})`);ScanEngine also has getDefenseFingerprint() and getCurrentTemperature() for reading state after or during a scan.
CLI Usage
zeroleaks scan, probes, categories, and techniques. Flags for prompt input, scan mode, models, injection probe selection, and output.
Attack Agents
Strategist, Attacker, Evaluator, Mutator, Inspector, Orchestrator, and InjectionEvaluator. Each has an exported create function for custom integrations.