What you'll learn
- the two ways to give the SDK a setting, and which one wins;
- every
FOXY_*setting, in one table; - the settings you can only give in code;
- how to keep your settings when you close the terminal;
- recipes for common setups.
Two ways to give a setting
You can give most settings in two ways:
- In code, as an argument:
FoxyClient(mode="block"). - In the environment, as an environment variable:
FOXY_.MODE= block
When both are given, the code wins. When neither is given, the SDK uses the default. So the order is: argument, then environment variable, then default.
A setting the SDK cannot read, such as a misspelled value in the environment, falls back to its default, and your program starts as usual. Two things do stop a client from being made, with an error at once:
- a mistake in code, such as a plain
http://address to another machine passed asendpoint; - with an API key set, a spool file in a folder your program cannot write to.
Every setting
| Setting | What it does | Default | Example | When to change it |
|---|---|---|---|---|
FOXY_ |
Your API key. With it, records are sent to Foxy Audit. | none: records are not sent | YOUR_ |
Always, to send records. |
FOXY_ |
Where records are sent. Give the base address, with no path after it. | http://127.0.0.1:8000, a backend on this machine |
https:// |
Always, to use Foxy Audit's service. |
FOXY_ |
Lets the key go over plain http:// to another machine. |
off | 1 |
Almost never: only on a private test network. |
FOXY_ |
What happens when a prompt check fires: observe, block or redact. |
observe |
block |
When you are ready to stop risky prompts. See Modes. |
FOXY_ |
What happens when the answer check fires: observe, block or off. |
observe |
block |
For regulated data. See Response scanning. |
FOXY_ |
Makes a call fail when its record cannot be confirmed. It needs an API key: with none, nothing is sent, so an error is logged and it stays off. | false |
true |
When a call must never go unconfirmed. |
FOXY_ |
The key used to make your hashes. | the key in the SDK's key file | YOUR_ |
In a container or a serverless function, whose disk does not last. Keep the key in a secret store. See below. |
FOXY_ |
Where the SDK keeps its own commitment key. | commitment., next to the spool |
/ |
When the spool's folder is not where you keep a secret. |
FOXY_ |
Turns on salts, and names the file where they are kept. | not set: no salts | / |
For extra protection of short prompts. See The evidence model. |
FOXY_ |
Where the spool file is. | ~/. |
/ |
In a container, or on a shared server. |
FOXY_ |
How many records may wait, for each key and address. 0 means no limit. |
100000 |
250000 |
Rarely. See The spool and delivery. |
FOXY_ |
How many seconds to keep delivery receipts in the spool. 0 keeps them for ever. |
604800, which is 7 days |
86400 |
Rarely, to keep the spool smaller. |
FOXY_ |
A fixed name for this installation in the ledger. Letters, digits and _ . : -, up to 128 characters. Any other name is logged as an error and not used. |
made up once for each key and address, and kept in the spool | billing- |
When you want to recognise a machine in your records. |
FOXY_ |
Which of your AI systems these records belong to. | not set: no system | an id that GET / returns |
When your workspace lists more than one AI system. |
FOXY_ |
Whether to read your workspace's enforcement setting. | on | off |
On a machine with no internet, or when only your code should decide. |
FOXY_ |
Seconds between reads of the workspace setting. | 300 |
60 |
Rarely. |
FOXY_ |
Turns the injection classifier on or off. The pattern rules always run. | on | off |
When very long prompts make it too slow. See below. |
FOXY_ |
Your old key, for foxy spool rekey only. |
not set: the command asks for it | YOUR_ |
When you move waiting records to a new key. |
Case and spaces around the value do not matter, but the words do:
FOXY_is on forAUDIT_ REQUIRED 1,true,yesoron, and off for0,false,no,offor nothing. Any other value leaves it off, and says so in the log.FOXY_is off unless the value isALLOW_ INSECURE 1,trueoryes. Any other value, evenon, leaves it off.FOXY_andORG_ POLICY FOXY_are on unless the value isINJECTION_ CLASSIFIER 0,false,nooroff. Any other value leaves them on.
Your commitment key. Your hashes are made with a key of your own, which Foxy Audit never sees. If you set none, the SDK makes one when a client first starts with an API key. It keeps the key in a file called commitment., next to the spool, and only you can read it. Every program that shares the spool uses the same key.
Back that file up. foxy explain needs the key to prove a record later. Without it, you cannot prove which text a record's hashes came from. The chain still checks out without it.
In a container or a serverless function, the disk does not last, so a new key would be made at each start. There, set FOXY_ from your secret store, or mount the key file from a volume that lasts.
If the key file can be neither read nor made, the SDK uses your API key, and logs a warning. It also warns when FOXY_ is your API key, because Foxy Audit receives that key with every request.
With audit_ in your code, the SDK is strict about this. If the key file can be neither read nor made, the client does not start: it raises an error that says why and how to fix it. With FOXY_ instead, the SDK logs an error and goes on with your API key.
Settings you give in code only
A few settings have no environment variable. You pass them to FoxyClient(...):
| Argument | What it does | Default |
|---|---|---|
on_ |
a function the SDK calls after each call, with a receipt | none |
timeout |
how many seconds a call waits for its record to be confirmed when audit_ is on (at least 1), and how long a read of your workspace's setting may take |
5.0 |
desktop_ |
whether to send signals to the optional desktop app | True |
udp_, udp_ |
where the desktop app listens | 127.0.0.1, 9999 |
The environment settings above can also be given in code, with the same names in lower case: api_, endpoint (for the backend URL), mode, response_, audit_, commitment_, commitment_ (for the key file), salt_, spool_, client_, system_ and injection_.
Five environment settings have no argument on FoxyClient: FOXY_, FOXY_, FOXY_, FOXY_ and FOXY_. They belong to whoever runs the machine, not to the code, so you set them in the environment. FOXY_ is read only by foxy spool rekey.
Step 1: Keep your settings
A setting you type in a terminal lasts until you close it. To keep it, put it where your program starts:
- On a server, set it in the service's own settings, for example the environment section of a service file or a container's environment.
- On your own computer, add the
exportlines to your shell's start-up file. On Windows, open Start and search for "Edit environment variables for your account".
Keep your API key out of your code and out of version control. Anyone who has it can write records into your ledger.
Step 2: See what the SDK is using
from foxy_audit import FoxyClient
foxy = FoxyClient()
print(foxy.enabled, foxy.cfg.endpoint, foxy.cfg.mode)
What you should see, with nothing set:
False http://127.0.0.1:8000 observe
foxy. is True when an API key is set, which means records will be sent. foxy. holds every resolved setting. It is read-only. To change a setting, make a new client.
The backend address, in detail
The SDK sends your API key with every request, so it is careful about where the key goes:
- An
https://address is fine, to any machine. - A plain
http://address is allowed only to this machine:127.0.0.1,::1, orlocalhost. A name that happens to point at this machine is not enough; write127.0.0.1. - A plain
http://address to any other machine is refused, unless you setFOXY_.ALLOW_ INSECURE=1 - An address that two parts of Python would read in two different ways is always refused, even with
FOXY_.ALLOW_ INSECURE
Where the address came from changes what a refusal looks like:
| Where you set it | What happens when it is refused |
|---|---|
FoxyClient(endpoint=...) in code |
an InsecureEndpointError straight away |
-- on a foxy command |
the command prints why, and exits 2 |
FOXY_ |
one error in the log. Your program keeps running, records are kept in the spool for that address, and nothing is sent there. foxy doctor exits 2. |
The SDK never follows a redirect when it sends your key. If your backend answers with a redirect, set the address to the place it redirects to.
Records are kept for the address they were made for. After you change the address, records made for the old one stay in the spool, and a notice in foxy doctor counts them. Move them to the new address with foxy spool rekey --, with FOXY_ set to your key. See If your API key changes.
The response scan and regulated data
FOXY_ controls the check on the model's answers. It is observe by default, so it records findings and never stops an answer. For PHI and other regulated data, set block: then an answer the scan flags never reaches your code. The scan is a set of patterns, so an answer that does harm in a way no rule matches still gets through. What block costs is explained under Which to choose in Response scanning.
The injection classifier's cost
The classifier runs on your machine, before the model call, and its cost grows with the length of the prompt. Here is how long the whole prompt check took, measured on one development laptop on 2026-09-30, with Python 3.13, best of three:
| Prompt length | With the classifier | With the classifier off |
|---|---|---|
| 2 KB | 7.8 ms | 4.4 ms |
| 8 KB | 31.8 ms | 16.3 ms |
| 64 KB | 259 ms | 139 ms |
| 256 KB | 1,080 ms | 592 ms |
These numbers are from one machine on one day, so measure your own. There is no length limit, on purpose: a limit would give an attacker a place to hide. If your prompts are very long and speed matters more than this check, set FOXY_. The six pattern rules keep running.
The setting belongs to each client. check() and the foxy command read the environment variable.
Recipes
Trying it out, with no account: set nothing. Checks and hashes run on your machine, and nothing is sent.
Connected, for a first test:
export FOXY_API_KEY="YOUR_API_KEY"
export FOXY_BACKEND_URL="https://app.foxyaudit.tech"
Regulated work, where every call must be recorded:
export FOXY_API_KEY="YOUR_API_KEY"
export FOXY_BACKEND_URL="https://app.foxyaudit.tech"
export FOXY_MODE=block
export FOXY_RESPONSE_SCAN=block
export FOXY_AUDIT_REQUIRED=true
In a container: add a lasting volume, and point the spool at it.
export FOXY_SPOOL_PATH=/data/foxy/spool.sqlite3
No internet, or only your code decides:
export FOXY_ORG_POLICY=off
If it goes wrong
| What you see | Why | What to do |
|---|---|---|
| Records are not sent, and no error | FOXY_ is not set where the program runs |
Print foxy.. Set the key in the program's own environment. |
InsecureEndpointError |
A plain http:// address to another machine, in code |
Use https://. |
An error in the log about FOXY_, and records waiting |
The same, set in the environment | Fix the variable, and new records go out. The waiting ones were made for the old address: move them with foxy spool rekey --. |
| A setting seems to be ignored | An argument in code wins over the environment variable | Remove the argument, or change it. |
FOXY_, in the log |
A word other than 1, true, yes or on |
Use true. |
ValueError when the client is made, saying audit_ needs an API key |
audit_ is on in code, and no key is set |
Set FOXY_, or leave audit_ off. |
An error in the log: FOXY_ is on, but there is no API key |
The same, set in the environment | Set FOXY_. Until then nothing is recorded, and no call fails. |
ValueError about client_, or an error in the log about FOXY_ |
The name has a character the backend refuses, or is over 128 characters | Use letters, digits and _ . : -. |
ValueError about system_ |
The id is not written exactly as GET / returns it |
Copy the id from that answer. The decorator and the client shows how. |
| An error when the client is made | With a key set, the spool file's folder cannot be written to | Point FOXY_ at a folder your program can write. |
Foxy Audit