ThrottleSecurity
What Throttle runs, what it sends, and what it keeps.
Throttle is open source, so you do not have to take this page on trust. Each line links to the code that does it.
Throttle is software you run, not a service you send data to. This page says what each part of it does with your data, and links every line to the code that does it. It was checked against the code on main (commit 64cb03e) on Nov 9, 2026.
The short version
- It runs in your infrastructure. A proxy you run in your cloud, a GitHub Action in your CI, and a command line tool. Nothing runs on our servers.
- Throttle the company receives none of your prompts, answers or keys. The code has no Throttle server address, no telemetry and no license check.
- Your data is stored on your machine, in
~/.throttle/own, with emails, phone numbers, card numbers and secrets masked by default. - Keys are read from environment variables and never stored.
- If a model you moved errors or sends a broken answer, the request goes to the API you already use.
What runs where
| Part | Where it runs | What it talks to | Code |
|---|---|---|---|
The proxy (throttle capture) | A machine you run, in your cloud. It listens on 127.0.0.1 (localhost) by default. | The OpenAI or Anthropic API your app already calls. With --route, also the model you approved for a job. | capture.py route.py |
| The GitHub Action | Your CI runner, on pull requests. | GitHub, to post one comment with the token you give it. With first looks turned on, also the API, your model and your judge. | action.yml github.py scan.py |
| The command line tool | Your machine or server. | Only the endpoints you pass it: the models you test and your judge. | shadow.py evaluate.py judge.py |
You point your app’s API base URL at the proxy. It sends every request on to the API, unchanged and with your app’s own headers, and sends the answer back unchanged. It caches nothing. If you bind it to an address other than localhost, it warns you that anyone who can reach that port can send traffic through it, because the proxy has no login of its own. Keep it on a private network.
What leaves your infrastructure
- To the API you already use: the same requests your app sends today, with your app’s own key. The proxy only passes them through (
capture.py). - To the models you test: when you run
throttle shadow, captured requests, after redaction, go to each model you name. If one of them is a hosted API model, that provider sees the requests sent to it. A model you serve yourself keeps them in your cloud (shadow.py). - To the judge: grading uses a judge model you pick, OpenAI or Anthropic or any compatible endpoint you host, with your own key. The judge sees the redacted conversation and the two answers it compares.
--judge-max-usdcaps what it spends (judge.py,evaluate.py). - To a moved job’s model: after you approve a move, that job’s live requests go to the model you approved (
route.py). - To GitHub: the Action posts or edits one comment on the pull request, using the token you give it. The workflow token works with
pull-requests: write(github.py). - First looks, if you turn them on: for a brand-new AI job, the Action sends the prompt template found in your code to your judge to write a few made-up inputs, then sends those to the API, to your model and to the judge. It is capped per pull request (
firstlook.py). - To Throttle: nothing. There is no Throttle server in the loop.
What is stored, and where
Everything is plain files in one folder on the machine running Throttle: ~/.throttle/own by default, or the folder you set with $THROTTLE_OWN_DIR or --dir (records.py).
| File | What it holds |
|---|---|
capture/2026-11.jsonl | Each API call the proxy saw, after redaction, one file per month. |
shadow/<model>.jsonl | Each tested model’s answers. These are never sent to your users. |
evals/ | Test results and the judge’s verdicts. |
routes.json | The moves you approved. It stores the name of the environment variable that holds a key, never the key. |
jobs/registry.json | Each AI job in your code, its state, and a log of every decision. |
ledger/ | The savings record, month by month. |
Auth headers are never written to disk. To delete everything, delete the folder.
Redaction modes
Pick one with throttle capture --redact (redact.py):
| Mode | What is stored |
|---|---|
mask (default) | Request and answer bodies, with emails, phone numbers, US social security numbers, card numbers (Luhn-checked), IP addresses and secrets (API keys, tokens, JWTs, passwords) replaced by placeholders like <EMAIL>. Inline images and audio become <IMAGE> or <AUDIO>. |
hash | Like mask, plus a salted hash so repeats of one value can be matched, like <EMAIL:3f2a9c>. Set the salt with THROTTLE_OWN_SALT. |
metadata | No bodies at all. Only the model, token counts and timing. You still see spend per job, but nothing can be tested. |
off | Bodies exactly as sent. |
Redaction is pattern matching. It does not catch names, street addresses, dates of birth, or free-text medical or financial details. If that content cannot sit on your disk, use metadata.
Keys and telemetry
- Keys for the models you test, for the judge and for a moved job’s model are read from environment variables you name, like
--judge-key-env. They are never printed or stored (judge.py). - The Action takes the name of a secret’s environment variable, never the key itself (
action.yml). - No telemetry. Throttle does not report usage, errors or anything else to us.
Fallback to the API
A job moves to another model only after it passes its tests and you approve it. You can override a failed test with throttle route approve --force; that move is marked FORCED everywhere and its savings are not counted. After that, any doubtful answer goes back to the API: the original request is sent on unchanged, so your users still get an answer. It falls back when the model cannot be reached, times out, returns an HTTP error or a bad response, cuts the answer off, sends an empty answer, sends broken JSON or JSON that misses the schema you asked for, or calls a tool that does not exist or with bad arguments. Every fallback is logged with its reason (route.py).
You can pin any job to the API or undo a move at any time (jobs.py).
Nothing to shut down
Your data, your models, your CI. Throttle is a tool you run, so there is no account to close and no data of yours on our side to get back. To stop, point your API base URL back at the API and remove the Action. The code is MIT licensed, so it keeps working whether or not you pay us.
Audits
No SOC 2 yet. Throttle is a young company and has not been audited. The MIT code is the proof: your team can read every line that touches your data, run it in your own cloud, and pin the exact commit it reviewed, in the Action and in pip install.
This website
The signup form is the only place Throttle the company gets data from you. Privacy says what it sends and where.
Report a security issue
Email kush@throttle-pro.com with “Security” in the subject. Please do not open a public GitHub issue for a vulnerability. The policy is in SECURITY.md.