StoaRedRisk OSTry Stoa
Step 4 · routing

Engines

An engine is an adapter behind one interface and one capability manifest. The planner never learns that DeepTeam or promptfoo exist — it reads manifests, scores them per objective, and routes. That is why turning an engine on or off is a config edit that re-routes work or declares a gap, with zero code change anywhere.

Deterministic demo runManifests are capability declarations read by the planner. In this run deepteam and promptfoo are enabled; garak and pyrit are registered but switched off.

Registered engines

2 enabled · 3 disabled
DeepTeamv0.1.0
EnabledImplemented

Runs in the local Python engine-worker. Local-first: no customer prompt leaves the machine.

coveredCategories
Indirect injection · 0.70Excessive agency · 0.95Data exfiltration · 0.90Memory poisoning · 0.85RBAC / BOLA · 0.80Denial of wallet · 0.60Jailbreak · 0.60Prompt leakage · 0.55
supportedTargetTypes
python-callbacksimulated-twin
maxLadderLevel
L3 - multi-step, tool-driving
multiTurn
true
localOnly
true
license
Apache-2.0
objectives in this run
13 of 14
promptfoov0.1.0
DisabledImplemented

Node CLI. Requires promptfoo on PATH or via npx. Attacks the twin's HTTP surface.

coveredCategories
Indirect injection · 0.80Data exfiltration · 0.50MCP tool poisoning · 0.65RBAC / BOLA · 0.50Denial of wallet · 0.55Jailbreak · 0.90Prompt leakage · 0.90
supportedTargetTypes
httpmodel-api
maxLadderLevel
L2 - single turn, adversarial
multiTurn
false
localOnly
false · needs a hosted backend
license
MIT
objectives in this run
0 — switched off, manifest still participates in gap detection
Stoa Red built-in corpusv0.1.0
EnabledImplemented

Deterministic, offline, no external dependency. Drives the twin directly and reads the shadow ledger as ground truth.

coveredCategories
Indirect injection · 0.35Excessive agency · 0.35Data exfiltration · 0.35Memory poisoning · 0.35MCP tool poisoning · 0.35RBAC / BOLA · 0.35Denial of wallet · 0.35Jailbreak · 0.35Prompt leakage · 0.35
supportedTargetTypes
simulated-twinpython-callback
maxLadderLevel
L3 - multi-step, tool-driving
multiTurn
true
localOnly
true
license
Apache-2.0
objectives in this run
1 of 14
garakv0.0.0-stub
DisabledInterface only

Interface-only in v1. 120+ probes; planned for a later release. Its manifest still participates in routing so coverage gaps name it as the closer.

coveredCategories
Indirect injection · 0.75Jailbreak · 0.95Prompt leakage · 0.85Data exfiltration · 0.40
supportedTargetTypes
httpmodel-api
maxLadderLevel
L2 - single turn, adversarial
multiTurn
false
localOnly
true
license
Apache-2.0
objectives in this run
0 — switched off, manifest still participates in gap detection
PyRITv0.0.0-stub
DisabledInterface only

Interface-only in v1. Multi-turn crescendo/TAP orchestrators; planned for a later release. Objectives needing ladder level 4 have no other home.

coveredCategories
Indirect injection · 0.80Excessive agency · 0.85Data exfiltration · 0.70Memory poisoning · 0.95Jailbreak · 0.90Prompt leakage · 0.60
supportedTargetTypes
python-callbackhttpmodel-api
maxLadderLevel
L4 - multi-turn crescendo / TAP
multiTurn
true
localOnly
true
license
MIT
objectives in this run
0 — switched off, manifest still participates in gap detection

Link and delink is pure config

The orchestrator only ever sees manifests for enabled engines. Registration is a build-time concern; enablement is a runtime config concern. Flip a flag and the next run re-routes its objectives — or reports an honest gap naming the engine that would close it.

stoa-red.config.json
// stoa-red.config.json
{
  "engines": {
    "deepteam":  { "enabled": true,  "priority": 1 },
    "promptfoo": { "enabled": true,  "priority": 0 },
    "garak":     { "enabled": false },
    "pyrit":     { "enabled": false }
  },
  "routing": {
    "mode": "best-fit",     // or "all-capable" to fan every objective out
    "preferLocal": true,    // +10 to engines that run on your machine
    "tieEpsilon": 0.01      // engines within this score run together
  }
}

What delinking would cost

computed from manifests, not from a re-run
If you setObjectives it currently runsObjectives nothing else could coverResult
engines.deepteam.enabled = false13none — fully re-routableRe-routed, coverage unchanged
engines.builtin.enabled = false1
obj_refund_mcp_tool_poisoning_lookup_policy (L2)
1 declared gap

A gap is never silence. The coverage map records the objective, the reason nothing ran it, and every registered-but-disabled engine that would have — so “we did not test this” is a statement in the evidence packet rather than an absence in it.

Routing in this run

mode: best-fit · target: simulated-twin

Score = category strength ×100, +10 if the engine runs locally, plus a ladder-fit bonus that prefers the engine which just clears the bar over one that is massively overqualified, plus the configured priority. Interface-only stubs score −1000, so they are only ever selected when nothing else is eligible — and then the run reports a gap instead of executing them.

ObjectiveCategoryLadderRan onStatus
obj_triage_indirect_prompt_injection_in_emailIndirect injectionL2deepteamCovered
obj_triage_excessive_agency_route_to_refundExcessive agencyL3deepteamCovered
obj_triage_rbac_bola_cross_tenantRBAC / BOLAL2deepteamCovered
obj_triage_denial_of_walletDenial of walletL2deepteamCovered
obj_triage_jailbreakJailbreakL2deepteamCovered
obj_triage_system_prompt_leakagePrompt leakageL1deepteamCovered
obj_refund_indirect_prompt_injection_in_handoffIndirect injectionL2deepteamCovered
obj_refund_excessive_agency_issue_refundExcessive agencyL3deepteamCovered
obj_refund_data_exfiltration_send_emailData exfiltrationL2deepteamCovered
obj_refund_memory_poisoningMemory poisoningL3deepteamCovered
obj_refund_mcp_tool_poisoning_lookup_policyMCP tool poisoningL2builtinCovered
obj_refund_rbac_bola_cross_tenantRBAC / BOLAL2deepteamCovered
obj_refund_jailbreakJailbreakL2deepteamCovered
obj_refund_system_prompt_leakagePrompt leakageL1deepteamCovered
Adding a fifth engine

Implement manifest(), plan(), run() and normalize(), register the adapter, and set enabled: true. Routing, coverage, merge, guardrails and evidence pick it up with no change to any of them.