Skip to main content

luxvigil 🕯️

tests

Abuse forensics for AI APIs. Your free tier is part of your attack surface.

Nihil occultum quod non manifestetur. Nothing stays hidden forever (Luke 8:17).

luxvigil reads the request logs of your AI API (or any app that gives away tokens or credits) and shows you the abuse hiding in them: account farms, throwaway emails, shared or leaked keys, bots sending the same prompts over and over… and roughly how much it cost you.

⚠️ Early days. All the checks and the terminal report work. I'm building it in the open step by step, so star or watch if you want to follow along.

Try it

pipx run luxvigil demo        # no install: makes fake logs with abuse in them and scans them
pip install luxvigil          # then: luxvigil scan your-logs.csv

Python 3.11+, no dependencies, nothing leaves your machine.

Why

If you run an AI product with a free tier, you're probably feeding someone who signed up 300 times with fake emails. Each account looks normal on its own, so per-user rate limits don't catch it. All you see is the inference bill going up and conversion going down.

And it's not just a billing problem. Free credits and leaked keys are how a lot of people get anonymous, untraceable access to LLMs: to resell it, to run spam or scam campaigns, or to hammer models with jailbreak attempts without risking their own account. On the attacker side, "free inference" is a resource like any other, and your free tier is part of the attack surface.

luxvigil looks across accounts to find the patterns that only show up when you put them side by side.

What it does

You give it a CSV export of your request logs, it gives you a report. Want to see it before digging out your logs? luxvigil demo makes a month of fake logs with two farms, a leaked key and a spend spike planted in them, and scans them:

$ luxvigil demo

🕯️ luxvigil · 2026-10-01 to 2026-10-30

  11,405 requests · 285 accounts · $324.60 spent
  💰 ~$195.25 is likely abuse (60% of your spend), 86 accounts
  🔎 ~$22.76 more is worth a look, 1 account (signals that can have an innocent explanation)

  🚨 2 account farms, 85 accounts
     - 60 accounts on 3 shared devices + 1 shared network (185.220.101.0/24, with throwaway emails or template prompts)  ·  3,368 requests  ·  ~$144.89
     - 25 accounts on 1 shared device  ·  968 requests  ·  ~$32.39
  📧 60 of 285 accounts (21%) use throwaway emails: mailinator.com (16), sharklasers.com (12), 10minutemail.com (12)
     3,368 requests  ·  ~$144.89
  🔑 1 key probably shared or leaked
     - key key_eb500b7a7780b4c2: 13 countries in a single day, 401 IPs, home is GB  ·  400 requests from abroad  ·  ~$17.97
  🤖 1 copy-paste bot, 60 accounts
     - 60 accounts sending the same 5 prompts over and over (3,368 times)  ·  3,368 requests  ·  ~$144.89
  ✅ no shared Gmail inboxes
  💸 2 spend spikes (could be a launch or a batch job, check first)
     - acc_0100 went from ~$0.02/day to $7.99/day (368x) on 2026-10-28 to 2026-10-30  ·  302 requests  ·  ~$22.76 extra
     - acc_0007 went from ~$0.02/day to $6.26/day (266x) on 2026-10-23 to 2026-10-25  ·  402 requests  ·  ~$17.93 extra

  The same account can show up in more than one check. The totals at the top count it once.

The checks:

  • Account farms: lots of accounts on the same device, or on the same network when they also use throwaway emails or template prompts (a shared network alone proves nothing: think mobile carriers and universities)
  • Disposable emails: signups from mailinator & friends
  • Shared / leaked keys: one key used from many countries in a short window (often a sign it's been stolen and resold)
  • Copy-paste bots: different accounts sending the same prompts or templates
  • Gmail aliases: three or more accounts on the same Gmail inbox (you+1@gmail.com, y.o.u@gmail.com…), the classic way to get lots of free trials with one inbox
  • Spend spikes: a user suddenly burning 10x their usual

Every flag tells you why it fired. No black box.

The report keeps two totals apart. Likely abuse is what comes from strong signals: farms, copy-paste bots, shared Gmail inboxes and keys used from all over the world. Worth a look is for weak signals that often have an innocent explanation, like a throwaway email on its own (someone testing) or a spend spike (a launch, a batch job). An account that trips both just counts as likely abuse, and nothing is counted twice.

What it won't do

  • It doesn't block anything. It only looks at what already happened.
  • It doesn't run in real time or sit in your request path.
  • It never sends your logs anywhere. Everything runs on your machine, which matters since prompt logs are usually some of the most sensitive data you have.

Log format

One row per API request, as a CSV. Only when, who and how many tokens are a must, everything else is optional. Give it what you have: if a column is missing, the checks that need it are skipped and the report says so (⏭️ disposable emails: skipped, no emails in the logs) instead of telling you everything's fine.

column what it is needed for
timestamp when it happened, ISO 8601 (2026-10-01T14:03:22Z) or a unix timestamp required
account_id your id for the account required
input_tokens / output_tokens tokens in and out required
cost_usd what the request cost you (or pass prices, see below) spend, spikes
email email the account signed up with throwaway emails, Gmail aliases
api_key_id id or hash of the key used, never the key itself shared / leaked keys
ip / country client IP and its 2-letter country farms (IP), shared keys (country)
device_id device / browser fingerprint farms
prompt_hash hash of the prompt, so repeated prompts show up without storing them copy-paste bots
prompt_chars length of the prompt in characters lets the bot check ignore "more", "continue"...

Your columns are called something else?

luxvigil already understands the usual suspects (user_id, user_email, created_at, prompt_tokens, completion_tokens, client_ip…) and tells you when it uses one. For anything else, point it at your column with --map:

$ luxvigil scan logs.csv --map account_id=customer --map email=signup_email

No cost column? Give it your prices per 1M tokens and it works it out:

$ luxvigil scan logs.csv --price-in 3 --price-out 15

It will never pick up a column called api_key, since in some exports that's the raw key. Hash it first and map the hash with --map api_key_id=....

JSON

Want to feed it into something else? --json prints the same report as data. Every finding has a stable id (same logs, same ids), how strong it is, the evidence behind it, what could explain it innocently and what to check next:

{
  "id": "shared_key-c09f5a151c",
  "check": "shared_key",
  "severity": "likely",
  "summary": "key key_eb500b7a7780b4c2: 13 countries in a single day, 401 IPs, home is GB",
  "accounts": ["acc_0007"],
  "requests": 400,
  "cost_usd": 17.9693,
  "evidence": {"key": "key_eb500b7a7780b4c2", "home_country": "GB", "peak_countries_in_a_day": 13, "...": "..."},
  "innocent_explanation": "a customer running the same key from servers in several regions",
  "next_step": "ask the key owner if they use it from these countries, if not rotate it"
}

No raw emails or prompts end up in it, only the ids and hashes already in your logs.

Bad rows

luxvigil stops at the first row it can't trust (a cost of nan or -5, negative tokens, an empty account id…) and tells you which line, rather than quietly getting the totals wrong. If your export has a few broken rows and you just want the rest, add --skip-bad-rows and it'll tell you how many it skipped. It also warns you about duplicated rows and rows without a valid IP.

Telling it what's normal for you

Your office, your VPN or a big customer behind one network will look like a farm, since lots of accounts share an IP range. Drop a luxvigil.toml next to your logs (or pass --config):

[trusted]
networks = ["203.0.113.0/24"]  # office, VPN exit, a customer's office...
devices = ["kiosk-01"]

[ignore]
accounts = ["qa_*"]
emails = ["*@yourcompany.com"]
keys = ["key_ci_*"]

Trusted networks and devices are still checked, they just never link accounts into a farm. Ignored accounts are skipped completely, but still count towards your total spend.

No logs handy? luxvigil demo scans fake ones for you, or make your own to play with:

$ luxvigil generate --out sample-logs.csv

How fast is it?

On fake logs, on a small 2-core cloud box with Python 3.13 (python scripts/bench.py 10000 100000 1000000):

requests time peak memory
10k 0.1s ~30 MB
100k 1.2s ~90 MB
1M 18s ~740 MB

About half of that is just reading the CSV. Everything is loaded into memory, so tens of millions of rows will need a big machine or splitting the file by month. Run the script on your own machine to get your numbers.

Does it work on real fraud?

There's no public dataset of AI API abuse with labels, so we tested the farm check on the closest thing: Amazon's "Fraud ecommerce" benchmark (~150k signups with a fraud label). The fraud there comes in a burst of farms in one month, and luxvigil catches it: 9 out of 10 accounts it flags are real fraud, with no ML and no tuning. On Amazon's own test set, which has hardly any farms, it flags almost nothing and its device signal scores about the same as their best model.

Only the farm check is tested there, and it's e-commerce, not AI API logs. Full numbers, caveats and how to run it in benchmarks/.

We also ran it on WildChat, ~2M real messages to a free GPT chatbot (27s to scan). It found people using the free chat as an API (a print-on-demand shop, agent loops, data generation scripts), and it showed the copy-paste bot check over-flags follow-ups like "more" or "continue" in chat logs. With the optional prompt_chars column that mostly goes away (34% of the spend flagged went down to 5% on WildChat-1M), checked on 3.4M newer chats we never tuned on. Details in benchmarks/.

Status

  • Name & scope
  • Log format + fake log generator (for demos and tests)
  • Account farms
  • Disposable emails
  • Shared / leaked keys
  • Copy-paste bots
  • Gmail aliases
  • Spend spikes
  • Terminal report (with a total that counts each account once)
  • JSON output
  • Farm check tested on real labeled fraud
  • HTML report (v0.2)
  • Read LiteLLM / Helicone / gateway exports directly (v0.3)

Why "luxvigil"?

Lux = light, vigil = keeping watch through the night. It keeps an eye on what happened while you weren't looking.

License

Apache-2.0. Use it, change it, ship it, just keep the notice.

The list of throwaway email domains bundled with luxvigil comes from disposable-email-domains, which is public domain (CC0).


Built by @polfananas

Metadata

Release files for luxvigil 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for luxvigil 0.1.0
File Size Uploaded Provenance Actions
luxvigil-0.1.0.tar.gz 118.1 kB With provenance Details for luxvigil-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for luxvigil 0.1.0
File Interpreter ABI Platform Actions
luxvigil-0.1.0-py3-none-any.whl Python 3 none any Details for luxvigil-0.1.0-py3-none-any.whl

Total release size: 218.2 kB

Release files / luxvigil-0.1.0.tar.gz

Download URL luxvigil-0.1.0.tar.gz
Size 118.1 kB
Tags Source
SHA-256 checksum
How to use checksums
51be4c74e1a0163f4ad4a1dd4b7f68e04a65d9541bd065d408cc7958bfb61ae3
BLAKE2b-256 checksum
How to use checksums
a440e83cea655652b077ca376f02131c9e5d33817a0f633e2ce9149934dd53fa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / luxvigil-0.1.0-py3-none-any.whl

Download URL luxvigil-0.1.0-py3-none-any.whl
Size 100.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a381233cfa81eee253361216e2bb9f4f3d3ddcfade1b58600352368a323093d4
BLAKE2b-256 checksum
How to use checksums
f1c05d9b752d250023322767154bbe239f90065ff4abbc80a399470b6a003913
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Security for luxvigil 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for luxvigil-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page