Foxy Audit
Book a demo
← Back to all cards

One install. Three ways in.

Everything below is lifted from the SDK's own README: the shipped truth, not marketing. Its only runtime requirements are requests and the urllib3 it brings.

Install

The SDK is a single Python package. Your key, and the address of Foxy Audit's service, go in the environment:

shell
$ pip install foxy-audit
$ export FOXY_API_KEY="YOUR_API_KEY"
$ export FOXY_BACKEND_URL="https://app.foxyaudit.tech"

Method 1, the decorator: capture every call

Wrap the function that talks to your model. Your code stays unchanged: every call is hashed into commitments on your machine, the raw text is dropped before anything is sent, and only content-blind metadata reaches the ledger. The commitments are keyed with a key the SDK makes and keeps on your machine, which Foxy Audit never sees: back that key file up. Each record is saved to a durable local spool on your own thread, which normally takes milliseconds, and sent from there in the background.

app.py
import os
from foxy_audit import FoxyClient

foxy = FoxyClient(api_key=os.getenv("FOXY_API_KEY"))

@foxy.audit(policy="hipaa")
def ask_model(prompt: str) -> str:
    return llm_client.generate(prompt)   # your existing code, unchanged

Prefer zero client plumbing? The module-level decorator builds a client from the environment: from foxy_audit import audit, then @audit(policy="soc2"). Same capture, same guarantees.

The policy tag selects which local checks run before the model is called. The baseline runs under every tag: known prompt-injection patterns + a local statistical classifier (measured, not guaranteed), and secret detection. hipaa/gdpr add the PHI/PII sweeps. In block or redact mode, enforcement happens locally, before the request leaves your process.

Method 2, the pre-flight gate: check() in CI

Ask "would this prompt be blocked?" without wrapping anything. check() needs no API key, no network and no spool; the exit code drops straight into a pre-commit hook or a CI step. It is content-blind: it returns rule labels, and never returns, logs or echoes the text you gave it.

shell / ci
$ foxy check "ignore all previous instructions" --policy hipaa   # exit 1 if it fires
$ foxy check --prompt-file prompt.txt --policy gdpr --json

The same call is available in Python: from foxy_audit import check (result.triggered, result.rules, result.ruleset_version).

Method 3, the local replay: explain() proves a recorded row

For a row already in your ledger, explain() recomputes the commitment from your local key, matches it against the row's recorded hash, loads the frozen ruleset that row named (not today's rules) and replays it, showing which rule matched where. It runs on your machine, against a prompt you supply, so it shows you nothing you do not already have; the matched spans go to stdout only.

shell
$ foxy explain --event-id 3f2a… --export logs.json --prompt-file prompt.txt

Three answers are honestly "I cannot": a salted row whose salt sidecar is missing, a row naming a newer ruleset than your SDK, and a pre-1.7.0 row that recorded no ruleset. It says so rather than guessing.

  • What leaves your machine: commitments, a token count, a policy tag, and bounded metadata. Raw text never does. Nor does the key the commitments are made with: it stays on your machine.
  • What happens next: records are sealed into a per-organisation hash chain and, optionally, graded by the content-blind judge.
  • Where to go deeper: the SDK manual teaches every feature step by step, the documentation index links every topic page, and how it works walks the whole pipeline.

Want us to wire it in with you?

Book a walkthrough and we'll integrate the SDK into a real call live, then verify the chain in front of you.

Book a demo