Model or dataset
zeroc00I/DontFeedTheAI avatar
zeroc00I/DontFeedTheAI

DontFeedTheAI: the regex floor you can measure and the model floor you cannot

Transparent anonymization proxy for AI-assisted pentesting. Strips IPs, credentials, hostnames and PII before they reach any LLM (Claude, OpenAI, OpenRouter). Local Ollama + regex detection. Per-engagement vault.

661 stars78 forksPythonMIT

At a glance

What is it?
DontFeedTheAI is a MIT-licensed proxy that swaps your real IPs, credentials and hostnames for surrogates before a request leaves the machine, then restores them on the reply, using a local Ollama model for prose and regex for patterns. The design is sound and the configuration is where it gets interesting: the compose file and the image disagree about the port, and the layer that catches credentials written as words is one small model you can switch off.
Who is it for?
DontFeedTheAI earns its place if your work involves pasting scan output, internal hostnames or credentials into a hosted model, because it keeps detection on your own machine and makes every substitution reversible, which beats both a cloud anonymization service and trusting the provider with the raw text. The caveats are about coverage and configuration rather than about the idea.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 96 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two passes, and only one of them is cheap to test

The proxy makes two passes over every request and they are not interchangeable. A regex layer catches the shapes that have a fixed form: IP addresses, hashes, tokens, API keys. A model on your own machine catches what has no shape at all, namely hostnames, organisation names and credentials that appear as prose inside a sentence rather than as a pattern. Both run locally, and the model is reached over the address configured for it with a thirty second timeout ceiling. The split decides how you evaluate coverage, because only one side can be measured quickly. Running the improvement loop with three cycles pushes all 53 fixtures through the regex layer, reports what leaked and what was flagged by mistake, and names the exact strings that got through.

bash
python3 wizard.py improve --cycles 3

That is a fast floor to raise and it is also that floor's ceiling, because it never exercises the model. The separate full-pipeline check is the command that needs Ollama up, and it asserts zero leaks across the same fixtures.

bash
python3 wizard.py test --integration

The number worth holding onto is 53. That is the width of the evidence, and a leak in a category nobody wrote a fixture for is not something this suite would have caught.

The prose layer is one small model and a floating image tag

Everything the prose pass knows about your data comes from a single model on your own hardware. The default is qwen3:1.7b, and the configuration comment offers qwen3:4b as a better but slower alternative, which is the only quality against latency dial the project exposes. Thirty seconds is the ceiling on that call, so a larger model on a busy machine turns into a timeout rather than a better answer. The runtime underneath it is pulled as ollama/ollama:latest, a floating tag, so the detector's execution environment can change without anything in this repository changing. Startup ordering is handled properly at least: the compose file declares the proxy's dependency on the model and waits for its health endpoint to answer before starting, polling every ten seconds with a thirty second start period. There is also a switch that removes the layer outright. LLM_ENABLED defaults to true, and setting it to false leaves the regex pass running on its own. Nothing in the configuration says what that costs, and from the detection table it is the whole of prose detection: hostnames, organisation names and credentials written as words. If you turn it off for speed you are running a different product with the same name.

Every surrogate lands in a per-engagement SQLite vault

A surrogate is only useful if it survives the round trip, so the mapping has to outlive a single request. Each one is written to a SQLite vault on disk, and the reply coming back is rewritten to put your real values in place of the placeholders the model echoed. Isolation between clients rests on one identifier: ENGAGEMENT_ID is set per engagement so two engagements do not share mappings. The storage path is configurable, and in the container the data directory is a declared volume whose stated purpose is to survive restarts, so the vault is not rebuilt empty every time the proxy comes back. The dependency list gives a clue about what the placeholders look like, since a fake data generator is a direct requirement: a replaced hostname or person is a plausible invented string rather than a marker like REDACTED. Invented rather than opaque is the right trade for something a model has to read, because an opaque marker invites the model to reason about the redaction instead of the finding. What a mapping does not carry is an age. No expiry is documented, so a surrogate created in one session can be used to restore text in the next, and the audit surface described further down is where that accumulated history becomes visible.

The audit page reverses every surrogate, so it lives behind the tunnel

This project ships a browser page that can turn every surrogate back into the original value, and it is deliberate about why that page is fenced. Starting it takes the tunnel command with an audit flag, so the page only exists while an SSH tunnel is up. What it shows is the complete lookup table for the session, every original-to-surrogate pair logged during it, filterable by entity type such as DOMAIN, CREDENTIAL, TOKEN and HASH, with a per-request timing breakdown beside it. The purpose is to let you spot a leak by looking instead of grepping logs, which is the right tool for the job. The cost is stated too: the page performs a reverse lookup over HTTP, and making it write-only sits on the project's roadmap. Until that lands, the tunnel is the entire security boundary for this feature. That boundary is worth checking rather than assuming, because the compose file also publishes the proxy port on the host, and a published port is not the same thing as a tunnel-only listener.

The compose file publishes 8080 while the image defaults to 5555

Getting the proxy to answer on the port your client expects is where the configuration files stop agreeing. The compose service publishes host 8080 to container 8080 and sets no port variable of its own, so the process falls back to the value baked into the image, which is 5555, and 5555 is also the port the image declares as exposed. The environment example lists 8080 as the proxy port for a local run and 5555 as the port the tunnel binds on the VPS loopback, so both numbers are right for different journeys. The documented local path points a client at 8080, which lines up with what compose publishes and not with what the image defaults to.

bash
python3 wizard.py setup       # create venv + install dependencies
python3 wizard.py docker up   # start proxy + Ollama in Docker
export ANTHROPIC_BASE_URL=http://localhost:8080
export ENGAGEMENT_ID=my-engagement
claude                        # or any OpenAI-compatible client

If you drive the stack with the compose file directly rather than through the wizard, that gap is the first thing to check, and the health endpoint is the quickest way to see which port the process actually bound. There is a second layer of ambiguity on top of it: three compose files sit in the repository, one for a native local Ollama, one for a VPS and one general file, and the local instructions go through the wizard rather than naming which of them it uses.

Scan output is cut into 1500-character pieces with 150 of overlap

Tool output is the reason a chunking configuration exists at all. A scan produces far more text than one request should carry, so the text is cut into pieces of 1500 with an overlap of 150 between neighbours, both values configurable. The overlap is the part that matters for detection, because it means a hostname or a credential sitting across a cut still appears whole in at least one piece, and a boundary does not become a place where a value stops being recognisable. The configuration names nmap output as the case this was sized for, and the worked example in the project description is an nmap service scan, so the numbers were chosen with reconnaissance output in mind rather than with prose in mind. What is not documented is what happens when a surrogate is created in one piece and shows up again in the overlapping neighbour, and the audit page is where you would go looking for the answer. Installation is from source, with no published package and no repository releases: clone it, then run the wizard, which asks for an engagement name, a VPS address and a model, deploys, opens the tunnel and launches the client with the proxy active. The image is Python 3.12 on a slim base with curl as the only added system package.

Your provider key is forwarded, the project's own secret rides in the URL

The proxy sits between your client and the model provider, so it handles two secrets and treats them in opposite ways. The provider key is never configured in the project's files, because it is forwarded transparently from the client: Claude Code or an OpenAI-compatible client holds it and hands it over at request time rather than the proxy storing one. The project's own secret is optional and has an unusual shape. Setting PROXY_SECRET changes the base URL your client uses so the secret becomes a path element, and a request arriving without that token in the path is rejected with a 403. The comment recommends generating it with openssl rand -hex 24. A token carried in a URL lands in shell history, in process arguments and in anything that logs the client's configuration, so treat it as a local convenience rather than a hardened control. The intended topology points the same way: the wizard drives a VPS over SSH and then binds the port on that machine's loopback for a tunnel, so the design assumes the proxy is reachable from you and not from the internet. The last push recorded for the project is 8 July 2026.

Editorial conclusion

DontFeedTheAI earns its place if your work involves pasting scan output, internal hostnames or credentials into a hosted model, because it keeps detection on your own machine and makes every substitution reversible, which beats both a cloud anonymization service and trusting the provider with the raw text. The caveats are about coverage and configuration rather than about the idea. Prose detection rests on a 1.7 billion parameter model that a single flag removes, the evidence base is 53 fixtures, the audit page can reverse every mapping and exists only behind a tunnel, and the port the compose file publishes is not the port the image defaults to. Check those four things on your own deployment, and use the visual audit rather than log grepping before you point it at a live engagement.

Frequently asked questions

What does DontFeedTheAI replace before a request leaves the machine?

IP addresses, hashes, tokens and API keys are matched by regex, while hostnames, organisation names and credentials written as prose are caught by a local Ollama model. Both layers run on your own machine, and the reply is rewritten to put the real values back.

Does DontFeedTheAI need a cloud anonymization service or a paid API?

No. Detection happens locally through Ollama and regex, and the only outbound call is to the model provider you already use. The provider key is forwarded from your client at request time rather than stored in the project configuration.

What happens to coverage if I set LLM_ENABLED to false?

The regex pass keeps running and the model pass disappears, which removes all prose detection: hostnames, organisation names and credentials that appear inside sentences. The flag defaults to true.

How do I check that nothing leaked through the proxy?

Run python3 wizard.py test --integration with Ollama up to push all 53 fixtures through both layers and assert zero leaks, run python3 wizard.py improve --cycles 3 to tighten the regex floor quickly, and open python3 wizard.py tunnel --audit to see every mapping visually behind the tunnel.

Which port does the DontFeedTheAI proxy listen on?

The files disagree. The compose service publishes 8080, the image sets its port to 5555 and exposes it, the environment example lists PORT as 8080 for local runs and PROXY_PORT as 5555 for the VPS loopback, and the local quick start points the client at 8080.

How does DontFeedTheAI keep surrogate mappings apart between clients?

By engagement identifier. ENGAGEMENT_ID is set per engagement to isolate surrogate mappings, and the mappings themselves live in a SQLite vault at the configured database path, stored in a volume so they survive container restarts.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. zeroc00I/DontFeedTheAI on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zeroc00i-dontfeedtheai.svg)](https://hysenlabs.com/projects/zeroc00i-dontfeedtheai)