Promptfoo agreed in March 2026 to be acquired by OpenAI, but the open-source project is still MIT and shipped 0.124.1 on 8 October, so ownership is the wrong first question for a regulated team. The right one is default egress. Without an OpenAI key, Promptfoo red teaming sends attack generation and grading to Promptfoo's API; with a key, most of both go to OpenAI, but remote-only plugins and harm categories still call Promptfoo's API. Pointing redteam.provider at a local model moves grading and some multi-turn attacker calls in-network, but single-turn plugin generation and entity extraction from your prompts stay remote until PROMPTFOO_DISABLE_REMOTE_GENERATION or PROMPTFOO_DISABLE_REDTEAM_REMOTE_GENERATION is set. Setting that flag removes coverage, not just quality: 28 medical, financial, pharmacy and insurance plugins, 22 unaligned harm categories and 10 strategies generate nothing. Garak 0.17.1 has no telemetry code but downloads Hugging Face models, Microsoft PyRIT 1.1.0 sends traffic only where you point it, and DeepTeam 1.0.10 carries two telemetry systems with two different opt-out variables. This week, run your current red-team config once in staging behind a default-deny egress rule and list every blocked destination.
The usual "promptfoo alternatives" page answers a question about ownership: Promptfoo agreed in March to be acquired by OpenAI, so here is a list of other tools. For a bank, insurer or hospital red-teaming an internal model, that is the wrong question. The one your security team will actually ask is what each harness sends off the network by default, and what you give up to stop it.
For Promptfoo the answer is uncomfortable. Reading the 0.124.1 source, pointing redteam.provider at a local model moves grading in-network, but with no OpenAI key, single-turn attack generation and entity extraction from your prompts still go to Promptfoo's API, and the remote-only plugins go there even with one. Setting the flag that stops that does not just lower attack quality: 28 regulated-industry plugins, 22 harm categories and 10 strategies stop producing tests at all.
This post compares four harnesses on that axis, from source and docs at their release tags on or before 22 October 2026: Promptfoo 0.124.1, NVIDIA garak 0.17.1, Microsoft PyRIT 1.1.0, and DeepEval 4.3.1 with its red-team sibling DeepTeam 1.0.10. Nothing here was run; every claim points to code or documentation at a pinned version. A red-team harness is one input to an AI security assessment, not the assessment itself; for the methodology around it, see how penetration testing AI systems differs from traditional pen testing.
What changed with Promptfoo, and what did not
Promptfoo's blog post announcing that it is joining OpenAI is dated 9 March 2026, and its excerpt says "The open-source project will continue." The record since then supports that. The GitHub repository is not archived, the license in package.json is still MIT, and the npm package kept shipping: 0.124.1 was published on 8 October 2026.
So Promptfoo has not exited, gone closed-source or changed license, whatever some alternatives listicles imply. The open-source CLI you installed last year is the same kind of tool today. The docs also describe Promptfoo Enterprise On-Prem as "our on-premises solution that includes a dedicated runner for deployments behind your firewall", which matters later.
What did change is who runs the hosted service the CLI talks to. That service is where the regulated-industry question lives, and it is not a question about the license at all.
What Promptfoo sends off your network by default
The red-team configuration docs at 0.124.1 state the default plainly: "By default, Promptfoo uses your local OpenAI key for redteam attack generation and grading. If you do not have a key, Promptfoo will automatically proxy requests to our API for generation and grading." Add a local redteam.provider and the disable flags, and four setups result:
Telemetry is a separate stream on top of that: a PostHog client sends events to a.promptfoo.app, and every event is also posted to r.promptfoo.app. A version check calls api.promptfoo.dev unless PROMPTFOO_DISABLE_UPDATE is set.
Read the second row twice. Setting an OpenAI key does not fully take Promptfoo's own API out of the loop: the remote-only plugins and unaligned harm categories still call it, and everything else defaults to OpenAI, which agreed in March to acquire Promptfoo. That is still external egress.
What the hosted service receives
The same page says "The eval of your target model is always performed locally." That sentence is easy to misread. It means your target model is called from your machine. It does not mean the target's responses stay there. Promptfoo's own SECURITY.md at the tag lists what the hosted side may receive: In a hospital deployment, the application purpose and the target's responses are exactly the material the data protection officer cares about. A response from a model grounded on patient records can carry patient records. Promptfoo's general FAQ answers "Does Promptfoo forward calls to an intermediate server?" with "By default, no", and frames hosted generation as something you opt into. For red teaming without an OpenAI key, the red-team docs and the source say the opposite. Use the red-team page and the code for your egress review, not the FAQ line.
- Hosted test generation: the application purpose, plugin and strategy configuration, generated attack context, and your user email for usage tracking.
- Hosted multi-turn or agentic attack generation: conversation history or target responses.
- Hosted grading: the prompt sent to the target, the target's response, the grading criteria and related assertion context.
| Setup | Attack generation goes to | Grading goes to |
|---|---|---|
| No OpenAI key, defaults | Promptfoo's API (api.promptfoo.app) | Promptfoo's API |
| OPENAI_API_KEY set | OpenAI, with your key, except remote-only plugins and harm categories, which still use Promptfoo's API | OpenAI, with your key |
| Local redteam.provider, no disable flag | Promptfoo's API for remote-only plugins and harm categories, and for all single-turn plugins when no OpenAI key is set | In-network (rubric grading) |
| Local redteam.provider plus a disable flag | In-network | In-network |
Why a local redteam.provider is not enough
This is the finding most worth handing to your engineer. The red-team docs at 0.124.1 say: "If you need to use a custom provider for generation, you can still benefit from our remote service by leaving PROMPTFOO_DISABLE_REDTEAM_REMOTE_GENERATION set to false (the default)." The troubleshooting page on grading results describes configuring redteam.provider for local grading while leaving remote generation at its default. A custom provider and remote generation coexist by design.
The source confirms it. shouldGenerateRemote() in src/redteam/remoteGeneration.ts returns false when a disable flag is set. Otherwise it returns true when PROMPTFOO_REMOTE_GENERATION_URL is set, when you are logged into Promptfoo Cloud, when OPENAI_API_KEY is absent, or when --remote is passed. It never reads redteam.provider. The paths that branch on it include:
What a local provider does move: LLM-rubric grading skips the remote path when redteam.provider is set, and the crescendo, iterative and iterativeTree multi-turn providers treat "remote generation enabled and no red-team provider" as their remote condition, so their attacker calls go to your model. Grading in-network is a real gain. It is not the same as a local run.
The two disable flags and the FAQ's offline list
There are two disable variables, and they nest. PROMPTFOO_DISABLE_REMOTE_GENERATION turns off all remote generation. PROMPTFOO_DISABLE_REDTEAM_REMOTE_GENERATION turns off red-team remote generation only. Either one stops the paths above for red teaming; the broader one is the safer default for a regulated runner. The FAQ's "completely offline" list has five variables: PROMPTFOO_DISABLE_TELEMETRY=1, PROMPTFOO_DISABLE_UPDATE=1, PROMPTFOO_DISABLE_REMOTE_GENERATION=true, PROMPTFOO_DISABLE_SHARING=1 and PROMPTFOO_SELF_HOSTED=1, plus "Only configure local or self-hosted LLM providers (e.g., Ollama)". It then adds: "These settings are not a network firewall. For strict offline or air-gapped use, combine local/self-hosted providers with network egress controls or use Promptfoo Enterprise On-Prem." SECURITY.md says the same of each disable flag. One more detail matters for an audit trail. With PROMPTFOO_DISABLE_TELEMETRY set, PostHog is skipped, but the CLI sends one "telemetry disabled" acknowledgment event to r.promptfoo.app. The FAQ and SECURITY.md both say that acknowledgment may include the local user identifier and, when set, the email. If your rule is "zero packets to the vendor", the opt-out variable alone does not meet it. This is the same exercise as auditing which commands of a coding tool call home, covered in our guide to OpenCode offline mode on an air-gapped network: read the source for every outbound call, then enforce the answer at the network, not in an env file.
What you lose when Promptfoo runs fully local
Promptfoo's docs warn that "Disabling remote generation may result in lower quality adversarial inputs", and that local generation quality "is generally low for most models." Those are vendor statements, and none of the sources behind this post measures the gap independently. The bigger cost is visible in source, and it is coverage.
REMOTE_ONLY_PLUGIN_IDS in src/redteam/constants/plugins.ts carries the comment "These have no local implementation and always call the remote API". When remote generation is disabled, the function that builds those plugins logs an error and returns an empty list. The unaligned harm categories behave the same way.
The first four rows add up to 28 regulated-domain plugins. For a bank or a hospital, these are the plugins written for your domain, and they exist only as hosted generation. Promptfoo's default plugin set includes all the harm plugins too, so a default red-team config run fully local is quietly a smaller test than the same config run online.
The docs themselves illustrate the trap. The "Using Local Providers for Grading" example on the grading-results troubleshooting page sets redteam.provider to an Ollama model, lists two plugins, harmful:hate and excessive-agency, and says it runs entirely locally when combined with PROMPTFOO_DISABLE_REMOTE_GENERATION=true. Read against the 0.124.1 source, harmful:hate is an unaligned harm category that returns zero tests in that mode, so only excessive-agency generates. The documented local example drops one of its two plugins.
The practical rule: after any local run, compare the plugin list in the generated report with the list in your config. A plugin missing from the report was never tested. And once the harness is running, the metric that matters is attack success rate per category; see what prompt injection benchmarks show about attack success for how to read it.
| Group (Promptfoo 0.124.1) | Examples | Count | With remote generation disabled |
|---|---|---|---|
| Medical plugins | medical:* | 9 | Zero test cases, error logged |
| Financial plugins | financial:sox-compliance, financial:data-leakage | 12 | Zero test cases, error logged |
| Pharmacy plugins | pharmacy:* | 3 | Zero test cases, error logged |
| Insurance plugins | insurance:phi-disclosure | 4 | Zero test cases, error logged |
| Unaligned harm categories | harmful:hate, harmful:self-harm, harmful:cybercrime | 22 | Zero test cases, error logged |
| Other remote-only plugins | indirect-prompt-injection, rag-document-exfiltration, rag-poisoning, ssrf, bola, bfla, hijacking, data-exfil, system-prompt-override | Not counted here | Zero test cases, error logged |
| Strategies requiring remote | audio, citation, gcg, goat, indirect-web-pwn, jailbreak:composite, jailbreak:goblin, jailbreak:hydra, jailbreak:likert, jailbreak:meta | 10 | Listed as requiring remote generation |
Garak, Microsoft PyRIT and DeepEval/DeepTeam on the same axes
The three Python options are not three versions of Promptfoo. Compare what each is for before comparing egress.
NVIDIA garak 0.17.1
Garak is a vulnerability scanner: it runs probes against a model and scores the outputs with detectors. Apache-2.0, released 16 October 2026. There is no telemetry or analytics code in the source tree. Network traffic comes from components. Hugging Face based detectors and generators download models from the Hugging Face Hub, and the atkgen probe, which is active by default, uses a GPT-2 based attack model from the Hub (garak-llm/attackgeneration-toxicity_gpt2) on CPU. The TAP and PAIR probes are inactive by default; enable them and the default attacker is mistralai/Ministral-8B-Instruct-2410 from the Hub, while the default evaluator is OpenAI gpt-5-nano, a hosted judge. Other opt-in probes default to hosted models too: AutoDAN, FITD and AgentBreaker point their attacker at NVIDIA's hosted NIM endpoint, and the judge detectors used by GOAT and FITD default to meta/llama3-70b-instruct on NIM. Fully local takes a mirrored Hugging Face cache for every detector and generator you use, plus an in-network evaluator before you enable TAP or PAIR, and in-network attacker and judge models before you enable any NIM-backed probe. The fileformats probe already handles Hugging Face offline mode by skipping. The same mirroring discipline applies to serving the attacker model; our guide to running vLLM air-gapped without Hugging Face calls covers the cache layout. What you lose: garak's probes are general. There is no equivalent of Promptfoo's finance, medical, pharmacy or insurance packs, so domain cases are yours to write.
Microsoft PyRIT 1.1.0
PyRIT is an orchestration framework: you assemble targets, attack strategies and scorers into your own campaigns. MIT, released 4 September 2026. The canonical repository is microsoft/PyRIT; the older Azure/PyRIT repository is archived. Initialization requires an explicit memory type: InMemory (SQLite in memory), SQLite (a local pyrit.db file) or AzureSQL. Azure is an option, not a requirement, and loading secrets from Azure Key Vault is optional as well. There is no telemetry module. Attacker and scorer models are whatever targets you construct; the OpenAI chat target reads OPENAI_CHAT_ENDPOINT, OPENAI_CHAT_KEY and OPENAI_CHAT_MODEL and accepts an explicit endpoint, so it can point at an in-network OpenAI-compatible server. The default egress is therefore whatever you configure. The one inbound dependency to plan for: more than 50 remote seed-dataset loaders fetch public datasets such as HarmBench, XSTest, StrongREJECT and MedSafetyBench from the internet, and an air-gapped site must mirror them. What you lose: breadth out of the box. PyRIT gives you the parts, not a finished scan.
DeepEval 4.3.1 and DeepTeam 1.0.10
DeepEval is an eval harness; DeepTeam is the red-team runner built on it. Both are Apache-2.0. Pin DeepTeam from PyPI (1.0.10, 16 October 2026): GitHub has no 1.0.10 tag, and its release dates do not match PyPI uploads. For DeepEval's RAG metrics, see our DeepEval vs RAGAS vs TruLens comparison; this post covers only its egress. DeepEval sends PostHog telemetry to us.i.posthog.com unless DEEPEVAL_TELEMETRY_OPT_OUT=1 is set. Its data-privacy page at the tag lists event names, metric names, a Jupyter flag, the storage backend type, an anonymous UUID and "Public IP address (used only for coarse regional analytics)". Upload to its hosted platform, Confident AI, happens only when CONFIDENT_API_KEY is configured. The default judge model is gpt-5.4 on hosted OpenAI, and the CLI has set-ollama and set-local-model commands for an in-network judge. DeepTeam has its own telemetry module, separate from DeepEval's, with its own variable: DEEPTEAM_TELEMETRY_OPT_OUT, which opts out only when the value is exactly YES. When the OpenTelemetry OTLP exporter is importable and the variable is not set, DeepTeam requests your public IP from api.ipify.org, initializes Sentry, exports spans to New Relic and starts a PostHog client; red-team runs record vulnerability names, attack names and the public IP. DeepEval's docs line that it "does not report crashes or exceptions anywhere" does not cover DeepTeam. Set the variable regardless of whether the exporter is installed. The RedTeamer defaults are gpt-3.5-turbo-0125 as simulator and gpt-4o as evaluator, both hosted OpenAI unless you pass a model wrapper around an in-network endpoint. DeepTeam depends on DeepEval, so one install carries both telemetry systems.
Egress comparison table
Every cell below comes from the source or docs at the pinned version.
None of these variables is a firewall, and Promptfoo's own FAQ and SECURITY.md say so about theirs. The guarantee an auditor accepts is a default-deny egress policy on the runner host, with only your in-network model endpoints allow-listed, and a staging run with the egress log open to show nothing else was attempted. Env vars reduce what tries to leave. The network rule proves what did.
| Tool and version | Attack generation default | Judge or grader default | Telemetry and opt-out | Cloud upload | Mirror for air-gap | License |
|---|---|---|---|---|---|---|
| Promptfoo 0.124.1 | Promptfoo API (no key) or OpenAI (key set); local only with a disable flag | Promptfoo API or OpenAI; local when redteam.provider is set | PostHog plus r.promptfoo.app; PROMPTFOO_DISABLE_TELEMETRY=1 still sends one acknowledgment | Sharing feature; PROMPTFOO_DISABLE_SHARING=1 turns it off | Local attacker and judge models; accept loss of remote-only plugins | MIT |
| garak 0.17.1 | Built-in probes; atkgen uses a Hub GPT-2 model | Detectors (HF-based ones download from the Hub); TAP/PAIR evaluator gpt-5-nano on OpenAI; judge detectors on NVIDIA NIM | None in source | None | Hugging Face models for detectors, generators, atkgen | Apache-2.0 |
| Microsoft PyRIT 1.1.0 | Whatever target you configure | Whatever scorer you configure | None in source | AzureSQL memory only if chosen | Remote seed datasets (50+ loaders) | MIT |
| DeepEval 4.3.1 | Not a red-team tool | gpt-5.4 on OpenAI; set-ollama or set-local-model | PostHog incl. public IP; DEEPEVAL_TELEMETRY_OPT_OUT=1 | Confident AI only with CONFIDENT_API_KEY | Local judge model | Apache-2.0 |
| DeepTeam 1.0.10 | Simulator gpt-3.5-turbo-0125 on OpenAI | Evaluator gpt-4o on OpenAI | ipify, Sentry, New Relic, PostHog when active; DEEPTEAM_TELEMETRY_OPT_OUT=YES exactly, plus DeepEval's | Inherits DeepEval's | Local simulator and evaluator models | Apache-2.0 |
Red teaming in a regulated perimeter: the decision
In a bank, insurer or hospital, the confidential part of a red-team run is not the attack strings. It is what they carry and what comes back: the application purpose, the system prompt, the tool schemas, and target responses that can contain customer or patient data the model was grounded on. That changes the tool choice.
If you keep Promptfoo's hosted generation, ask three questions in writing: what the hosted service retains and for how long, whether its data-handling terms changed after March 2026, and whether the generation endpoint can run inside your network. The docs say Enterprise On-Prem includes a runner behind your firewall and that PROMPTFOO_REMOTE_GENERATION_URL can point the CLI at a different generation endpoint; what the on-prem product ships beyond that is a sales conversation, not a docs fact.
Every fully local path has the same hidden cost: the attacker and the judge are now models you host. A weaker local judge produces scores that look as confident as a strong one's, so calibrate it against a human-labelled sample before trusting its pass rate; our post on regression testing with LLM-as-judge covers that. Red teaming also finds gaps without closing them; the runtime control is a separate layer, compared in NeMo Guardrails vs Llama Guard vs Guardrails AI. And red-team runs belong in the same loop as your functional evals, as described in our evals-driven development workflow. More tooling comparisons sit in the AI development tools guide.
Our recommendation for a regulated team that cannot send prompts or outputs out: use garak as the baseline scanner and PyRIT for the multi-turn campaigns specific to your application, not Promptfoo in local mode, unless you have checked that your test plan survives the plugin loss. If your plan is built around the finance or healthcare packs, the honest choice is between hosted generation under contract and the on-prem product, not a local flag that silently removes them.
| Your situation | Pick |
|---|---|
| You need the financial, medical, pharmacy or insurance packs and can accept hosted generation under a written data agreement | Promptfoo with defaults, or ask about Enterprise On-Prem |
| Your test plan does not depend on remote-only plugins | Stay on Promptfoo, fully local, with the five FAQ variables and a local redteam.provider |
| Prompts and outputs may not leave the network and you want breadth of known probes | Garak with Hugging Face models mirrored and the TAP evaluator repointed |
| You need custom multi-turn campaigns against an internal agent | Microsoft PyRIT on SQLite memory with in-network targets |
| Your CI already gates on DeepEval | DeepTeam with both opt-outs and local simulator and evaluator models |
Run it once behind a default-deny rule this week
Take the red-team config you run today, whichever harness it uses. Put the runner in staging behind a default-deny egress rule that allows only your in-network model endpoints, run it once, and write down every blocked destination from the egress log. Then open the generated report and check its plugin or probe list against what your test plan says you are testing. Anything blocked is traffic your current setup sends out; anything missing from the report was never tested.
FAQ
Quick answers to the questions this post tends to raise.



