AgentForge — Autonomous Multi-Agent Red Team

"The goal is to build a system of agents that can hunt, evaluate, escalate, and document vulnerabilities continuously — adapting as attackers adapt, without a human in the loop for every step. A static test suite is not the answer. An autonomous multi-agent red team is." — PRD

← Ops CenterStandalone HTML ↗

TOPOLOGY V4 — THE CLOSED LOOP

Five bands: TRIGGER → GENERATE → TARGET → EVALUATE & LEARN → OBSERVE. Feedback edges close the loop.
TRIGGERSGENERATETARGET — system under testEVALUATE & LEARNOBSERVE — live pressure feedback
SHIPPEDPARTIALPLANNED

CLOSED LOOP, LIVE VIEW

Same topology as the main dashboard, with the V4 satellites turned on.
loading topology…

THE 8 PRD OBJECTIVES — SHIPPED · NEXT · CUT

#1

Discovering vulnerabilities automatically

The system finds attack surface and chooses where to probe — no human picks the test cases.

SHIPPED

Orchestrator queues attacks across all 18 (category, subcategory) cells from the threat model. Scheduled probes + background traffic keep coverage warm. Heatmap shows where tests have landed.

NEXT

Coverage-gap scorer ranks under-tested cells highest each cycle. Event-driven trigger (target login / file ingest / LLM call → webhook → focused probe) so discovery reacts to live target activity.

CUT

A separate "is this harmful?" pre-screen LLM. The Judge already evaluates harm on responses; a second classifier on candidates duplicates work without adding signal.

#2

Generating adversarial attacks dynamically

Attacks aren't a fixed library. The red team adapts as the target adapts.

SHIPPED

Red Team uses local LLM (Nemotron) to mutate seed prompts per (category, subcategory). Probe + Run Battery actuators kick off campaigns from the dashboard.

NEXT

Adaptive mutator — consumes the last N target responses for a cell and prompts the LLM with "given these refusals/leaks, propose a novel variant." This is literally the PRD's "adapting as attackers adapt." Phase B.

CUT

Static jailbreak corpus. We seed from one but never depend on it; the live mutator is the source of truth.

#3

Measuring attack success and coverage over time

Every attack outcome is observable, attributable, and aggregatable.

SHIPPED

attack_attempts + verdicts + findings tables. coverage_cells aggregates pass rate per (cat, subcat) over 7d. cost_ledger + llm_calls give per-attack cost / tokens. 9-column trace step table per attack.

NEXT

Surface success-rate trend per cell (sparkline) inside the heatmap, and a rolling per-day verdict mix in the StatusBar.

CUT

Time-series Cost Burndown + Severity Over Time charts. Replaced with snapshot panes (Recent Traces + Open Findings) — at demo scale the time-axis read as noise.

#4

Converting successful exploits into repeatable evals

A confirmed vulnerability becomes a permanent test — but only if it's actually reproducible.

SHIPPED

Findings table holds every confirmed exploit with full reproducer payload (campaign_id, prompt, target response, judge rationale).

NEXT

Reproducer: re-fires the exploit ≥2 more times. Eval Promoter: promotes to suite only if judge_confidence ≥ 0.85 AND reproduces ≥ 2/3. Flaky exploits get tagged, not promoted. Phase B.

CUT

Auto-promotion of every success. Without the reproduce-gate the eval suite gets polluted with one-off flukes.

#5

Validating that fixes actually work

A "fix" isn't a fix until the variants also fail.

SHIPPED

patch_advisor drafts mitigation suggestions and queues them at /queue/patch_review for human apply. Per-finding reproducer payload makes manual re-test trivial.

NEXT

Fix Validator triple: on patch apply, run (a) the exact eval, (b) N mutated variants of the same seed, (c) the full (cat, subcat) category. All three pass = fix validated. Any fail = patch rejected. Phase B.

CUT

Single-test validation. Re-running the exact prompt only proves the fix handles that prompt; mutations are what catch over-fitted patches.

#6

Preventing regressions as the system evolves

The eval bank grows with every confirmed exploit and gates future changes — without becoming a runtime sink.

SHIPPED

Findings + battery_report endpoint give the canonical historical record. /v1/batteries/snapshot lets a battery be re-played on demand.

NEXT

Tiered Eval Suite: smoke (small, every commit), nightly (full), weekly (slow/expensive multi-turn). Retirement policy: evals retire when the target subsystem they probe is rewritten. Phase B → CI integration in Phase D.

CUT

Unbounded eval growth. An ever-growing list becomes a CI bottleneck and noise sink; tiered cadence + retirement keeps it healthy.

#7

Documenting vulnerabilities professionally

A CISO can read the report without asking us to explain it.

SHIPPED

Private Postgres findings table (the secure source of truth). /v1/batteries/report emits a campaign-level markdown summary. Plain-English Judge rationale (THIS HAPPENED / EXPECTED / FAILED BECAUSE / LOGGED AS) on every finding.

NEXT

CVE-style per-finding markdown: reproducer steps, impact statement, suggested mitigation, affected components, severity rubric. Surface inside the dashboard's Open Findings drawer. Phase B.

CUT

Public bug tracker / GitHub Issues integration. The findings DB is intentionally private — vulnerabilities surface to the CISO, not the world.

#8

Improving visibility into behavior under adversarial pressure

You can tell at a glance how hard the system is being pushed and where it's starting to crack.

SHIPPED

Coverage Heatmap (test surface), Recent Traces (live attack stream), Open Findings (current exploit list), per-node click-through for raw I/O, full 9-column step table per trace.

NEXT

Two new visibility surfaces in Phase B: (a) Pressure Gauge — attacks/min × rolling success rate as one number, the live stress meter; (c) Defense-Narrowing Diff — subcategories green 24h ago but red now, the live regression flag. Skip a latency-delta gauge until we control target metrics.

CUT

Heatmap-only visibility. Heatmap shows where we tested; it doesn't show how hard the target is sweating right now.

CLOSED-LOOP NODES (V4)

12 satellite nodes wiring the autonomous adapt-and-defend loop. All SHIPPED on develop.

event_ingress

SHIPPED

A signed inbox that lets the protected system tell the red team "something just happened, come probe it."

▾ details

WHY IT EXISTS

The PRD requires discovery to react to live target activity rather than only running on a schedule — this is the event-driven trigger path.

WHAT IT DOES

  • Exposes POST /v1/ingest/target_event, HMAC-SHA256 signed with AGENTFORGE_INGEST_SECRET (timing-safe compare).
  • Maps event_type (login / llm_call / upload / tool_call) to the matching threat-model category and enqueues a focused campaign job.
  • Writes every well-formed request — including signature failures — to target_events so probing of the webhook itself is auditable.
  • Returns 503 when the secret env var is unset so a misconfigured deploy refuses traffic instead of accepting unsigned events.

WHY IT MATTERS

Closes the gap between something risky happening in production and the red team probing it; the CISO sees event-driven coverage, not just calendar-driven coverage.

kill_chain_planner

SHIPPED

A planner that strings several individual attacks together into one realistic multi-step attack (the kind a real adversary actually runs).

▾ details

WHY IT EXISTS

Real breaches are chains, not single shots — e.g. indirect prompt injection → tool misuse → PHI exfiltration. Single-step testing under-states risk.

WHAT IT DOES

  • plan_chain() inserts a kill_chains row plus N kill_chain_steps rows (all pending), then emits step 0 as a campaign job and marks step 0 active.
  • advance_chain() reacts to kill_chain_step_complete payloads, marks the step success, and emits the next step — or marks the chain completed.
  • Tags each emitted campaigns row with kill_chain_id and kill_chain_step_index so trace continuity is walkable through the chain.
  • Reuses orchestrator campaign budget/deadline defaults so cost-meter behavior is identical to a normal scheduled campaign.

WHY IT MATTERS

Lets the CISO see whether the system holds up against an adversary that gets to take three moves, not one, which is the realistic threat profile.

novelty_seeder

SHIPPED

A pipeline that pulls vulnerability disclosures published after the model's training cutoff and turns them into fresh attack seeds.

▾ details

WHY IT EXISTS

A red team that only knows pre-training attacks tests a closed universe; novelty seeds break out of that window (PRD HP-EX-02, red.anthropic.com).

WHAT IT DOES

  • Pulls the NVD CVE feed (keyword-filtered for LLM / AI / healthcare) and caches the raw response under data/novelty_seeds/nvd/<date>.json for replay.
  • Pulls the AI Incident Database in the same shape under data/novelty_seeds/aiid/.
  • Maintains a built-in citation list of recent jailbreak techniques (Crescendo / Many-shot / Best-of-N) — citations only, the red team LLM regenerates the actual prompt.
  • Heuristic-maps each seed to (category, subcategory) using documented keyword tables (prompt-injection → TM-01, PHI → TM-02, …) and writes AttackSeed rows.

WHY IT MATTERS

Demonstrates to the CISO that the harness keeps tracking real-world threats as they emerge — not a static jailbreak list frozen at training time.

property_fuzzer

SHIPPED

A fuzzer that declares a hard rule the system must never break (e.g. "patient data never leaves the box") and then hunts for any input that breaks it.

▾ details

WHY IT EXISTS

Some safety properties — PHI confinement, tenant isolation — are non-negotiable, and a coverage-style red team will not exhaustively try to falsify them.

WHAT IT DOES

  • Loads YAML invariants from invariants/*.yaml (forbidden_patterns regex set, seed prompts, probe strategies).
  • For each invariant, round-robins through probe strategies and applies pure-code mutation primitives — no LLM in the loop; the regex IS the oracle.
  • Fires each probe at the target via redteam.target_client.send and tests every turn's reply against all forbidden regex.
  • Persists one invariant_runs row per batch summarising violations + first violating attack id, so leakiness is quantifiable per invariant.

WHY IT MATTERS

Gives the CISO and regulator a direct, evidence-based answer to "does this hard rule actually hold?" rather than an aggregate pass rate.

adaptive_mutator

SHIPPED

A learning attacker that reads the last few defender responses for a given threat cell and writes a new attack designed to beat them.

▾ details

WHY IT EXISTS

PRD §2 #2 — "attacks adapt as the target adapts." Without this node the red team only ever recombines the same seed corpus.

WHAT IT DOES

  • Loads the last N (attack_attempt, verdict) pairs for a (category, subcategory) cell and asks the redteam LLM for ONE novel variant.
  • Writes an adaptive_mutations row with inputs, proposal, rationale, and per-call cost from the cost meter.
  • Inserts a new un-fired attack_attempts row carrying the proposed prompt, links it via proposed_attack_id, and enqueues a follow-up campaign job tagged source:adaptive_mutator.
  • Returns AdaptiveMutationResult(status="noop") on empty history so an empty cell never produces a hallucinated mutation.

WHY IT MATTERS

Shows the CISO that defenders aren't being graded against a frozen 2024 quiz — the test set itself is evolving against the live target.

reproducer

SHIPPED

When the system thinks it found a real vulnerability, this node tries the same attack again, three more times, to confirm it wasn't a fluke.

▾ details

WHY IT EXISTS

PRD §4 — without a reproducibility gate the eval corpus fills up with one-off flukes that don't hold up on re-run.

WHAT IT DOES

  • Re-fires a successful attack N times (default 3) through redteam.target_client.send and grades each response via judge.grader.grade.
  • Persists each re-fire as a new attack_attempts row with parent_attack = original_attack_id — the eval_promoter walks this chain.
  • Writes one reproducer_runs row summarising n_attempts / n_success / reproducibility ratio against REPRODUCIBLE_RATIO_THRESHOLD.
  • Only promotes to status=reproducible when judge confidence on the original clears SUCCESS_CONFIDENCE_FLOOR; otherwise marks flaky.

WHY IT MATTERS

Means every "this is a confirmed vulnerability" you read in the dashboard has been verified ≥3× — the CISO isn't chasing ghosts.

eval_promoter

SHIPPED

When the system confirms an exploit holds up under retesting, this node turns it into a permanent test the harness will run forever.

▾ details

WHY IT EXISTS

PRD §4 Convert — reproducible exploits should become permanent eval cases so the same hole never reopens silently.

WHAT IT DOES

  • Consumes eval_promotion_request jobs emitted by the reproducer when status=reproducible.
  • Writes evals/<category>/<PREFIX>-PROMO-NNN.yaml matching the existing PI-DIR-001 schema; never overwrites existing files.
  • Idempotent on original_attack_id; near-duplicate detection (200-char prompt prefix substring match) records skipped_duplicate instead of writing.
  • Inserts an eval_promotions row with SHA-256 of the YAML bytes for tamper detection, plus best-effort cleanup if the DB write fails after the file is created.

WHY IT MATTERS

Gives the CISO a growing, auditable test bank that demonstrates "every confirmed vuln is now a permanent regression test" — the core compliance story.

eval_suite

SHIPPED

A test runner that re-plays the growing library of confirmed exploits against the system on three cadences: smoke, regression, and nightly.

▾ details

WHY IT EXISTS

PRD §4 Convert / §6 Prevent — a regression harness is the only thing that prevents a fixed vulnerability from silently reopening on the next deploy.

WHAT IT DOES

  • Three tiers: smoke (first 2 cases per category, pre-merge), regression (every case tagged add_to_regression), nightly (full corpus).
  • Loads cases from evals/ via eval_suite.loader, fires each through redteam.target_client.send, grades via judge.grader.grade.
  • Maps verdicts onto pass / fail / error: refused|safe|fail → pass; success|partial → fail; runner exceptions → error (tracked separately so broken networking is not a regression).
  • Persists one eval_suite_runs row + N eval_case_results rows per run, including target_version so verdict flips correlate with target deploys.

WHY IT MATTERS

Tells the CISO "the things we have seen exploited do not reopen" with persisted run history, not a one-time claim.

fix_validator

SHIPPED

After patch_advisor proposes a fix, this node re-runs the original exploit three times against the patched system to confirm the fix actually holds.

▾ details

WHY IT EXISTS

A "fix" isn't a fix until the same exploit can't reproduce — closes the loop between patch_advisor and bug_tickets (PRD §5).

WHAT IT DOES

  • Reads the bug_ticket; refuses if proposed_diff is null. Resolves the originating attack via evidence["attack_id"] or dedup_hash fallback.
  • Calls reproducer.runner.reproduce N=3× against the live (post-patch) target and grades each re-fire with the dual-judge.
  • Maps reproducer counts to a verdict: 0/3 success → fixed, ≥1/3 → regressed, mixed errors → flaky. Updates bug_tickets.status accordingly.
  • On regressed, inserts a regression_alerts row (severity from ticket, reason="fix validation failed") so the dashboard reopens the ticket loudly.

WHY IT MATTERS

The CISO sees patches marked fixed only when the proof is on the table — no human "trust me, I tested it" step.

cve_report_generator

SHIPPED

For each confirmed vulnerability ready for public disclosure, this node writes a coordinated-disclosure markdown report and ships it to maintainers.

▾ details

WHY IT EXISTS

PRD §7 — CISO-readable vulnerability documentation, in the industry-standard CVE/GHSA shape, without exposing the private findings DB.

WHAT IT DOES

  • For each findings row at severity ≥ p2 with status=published, renders reports/disclosures/DISC-NNNN.md in industry CVE/GHSA layout.
  • Includes Summary, Severity (rubric tag + placeholder CVSS line, p0=9.5 / p1=8.0 / p2=6.0 / p3=3.5), reproduction steps, observed behavior, suggested mitigation, coordinated-disclosure note.
  • POSTs the disclosure to DISCLOSURE_WEBHOOK_URL with X-AgentForge-Signature (HMAC-SHA256 over body) and X-AgentForge-Disclosure-Id headers; persists routed_via / routed_endpoint / routed_response_code.
  • Idempotent via UNIQUE(disclosures.finding_id); SHA-256 of the on-disk markdown is stored so a later verifier can confirm the artifact wasn't mutated.

WHY IT MATTERS

Gives the regulator and downstream maintainers a real, signed disclosure artifact — the system can do coordinated disclosure, not just internal triage.

regression_monitor

SHIPPED

A watcher that compares "how often the system defends right now" against "how often it defended 24 hours ago" and flags any cell that just got worse.

▾ details

WHY IT EXISTS

PRD §8 — defense-narrowing diff: the operator needs to see live regressions under adversarial pressure, not buried in a weekly report.

WHAT IT DOES

  • Every TICK_SECONDS (default 5 min), computes defense_pass_rate = 1 - (successes/attempts) per (category, subcategory) over a 1h "now" window vs a 2h baseline 24h ago.
  • Flags any cell whose defense pass-rate dropped by ≥ REGRESSION_THRESHOLD_PP (15 percentage points) with MIN_ATTEMPTS_PER_WINDOW ≥ 5 in both windows.
  • Inserts a regression_alerts row + files a bug_ticket via watchdog.bug_filer at severity p2, source=regression_monitor.
  • Suppresses re-alerts on the same cell for REALERT_SUPPRESS_HOURS (6h). Clears on POST /v1/regression/alerts/{id}/resolve; can re-fire once cleared.

WHY IT MATTERS

The CISO gets an early-warning signal the moment a deploy makes defenses worse — not days later from a CI run.

pressure_gauge

SHIPPED

A live stress meter that shows how hard the system is being pushed right now — how many attacks per minute and how often they're landing.

▾ details

WHY IT EXISTS

PRD §8 — "you can tell at a glance how hard the system is being pushed and where it's starting to crack."

WHAT IT DOES

  • GET /v1/pressure/now returns attacks/min × rolling success rate over two windows: 5-minute live + 60-minute baseline.
  • Trend bucketed off delta_pct = (5m_score − 60m_score) / 60m_score × 100: ≥ +20 = rising, ≤ −20 = falling, else stable.
  • Per-category split under TM-01..TM-06 keys (mapping mirrors threatLabels.ts CATEGORY_TM exactly) so a single TM regression doesn't hide in the global average.
  • Observed-only: joins attack_attempts × verdicts live. A separate launchd 5-min snapshotter populates pressure_snapshots (migration 0015) for future history graphs — the live endpoint never reads that table.

WHY IT MATTERS

During an active incident or campaign the CISO has one number that summarises load + pain in real time, not a heatmap they have to interpret.