Split-key reverse proxy that makes leaked API keys worthless
Project description
Worthless
Make leaked API keys worthless.
Your API key is split in two. Neither half works alone. The proxy enforces a hard spend cap before the key reconstructs — blow the budget, the key never forms, the request never leaves your machine.
Quickstart
curl -sSL https://worthless.sh | sh # fresh machine — no Python needed
# or, if you already have Python 3.10+:
pipx install worthless
Then, in your project:
cd your-project
worthless
Detects keys in your .env, splits them, starts a local proxy. No code changes.
Integrity check (transit, not origin)
Piping a script from the internet into sh is a supply-chain risk. The
Worker emits an X-Worthless-Script-Sha256 header so you can confirm
the saved file matches what the Worker advertised at fetch time:
# 1. Download.
curl -sSL https://worthless.sh -o install.sh
# 2. Body sha matches the header advertised by the Worker.
echo "$(curl -sSI https://worthless.sh | grep -i 'x-worthless-script-sha256' | awk '{print $2}' | tr -d '\r') install.sh" \
| sha256sum -c -
# 3. (Optional) read it.
less install.sh
# 4. Run.
sh install.sh
What this catches: post-download local-file tampering (between
curl -o install.sh and sh install.sh), CDN cache poisoning, and
Worker regressions that detach the served body from the advertised
header.
What this does NOT catch: transit MITM (TLS + HSTS already prevent that — the header/body match check would be redundant if that were the only threat), or a compromised origin. The header and the body come from the same Worker; an attacker who controls the response controls both, so the sha check is not origin attestation. Real cryptographic origin attestation lands with cosign-signed release manifests — tracked in WOR-303.
See docs/install-security.md for trust roots (what the installer talks to and what it verifies) and the kill-switch runbook.
$ worthless
Found 2 API keys:
OPENAI_API_KEY openai
ANTHROPIC_API_KEY anthropic
Lock these keys? [y/N] y
OPENAI_API_KEY locked
ANTHROPIC_API_KEY locked
Proxy ready on 127.0.0.1:8787
How it works
worthless locksplits each API key into two shards using XOR- Shard A stays on your machine (encrypted). Shard B goes to the proxy database.
- Your
.envis rewritten with shard-A (format-preserving — looks like a real key but is cryptographically useless alone) - The proxy reconstructs the key only when the rules engine approves the request
- Spend cap blown? The key never forms. The request never reaches the provider.
Commands
worthless # Auto-detect, lock, start proxy (the magic)
worthless lock # Lock keys in .env
worthless unlock # Restore original keys
worthless scan # Detect exposed keys without locking
worthless status # Show proxy and key status
worthless up # Start proxy (foreground)
worthless up -d # Start proxy (background daemon)
worthless down # Stop the proxy
worthless wrap <cmd> # Run a command through the proxy
worthless revoke # Revoke enrolled keys
Docker
docker run ghcr.io/shacharm2/worthless-proxy:<version> — multi-arch, vulnerability-scanned, cosign-signed. See docs/install-docker.md.
Platforms
Worthless runs on POSIX hosts. The proxy relies on setsid, os.killpg,
fd-based key transport, and signal-group shutdown — primitives that have no
reliable native-Windows equivalent. Rather than degrade silently, the CLI
refuses to start on native Windows and tells you how to run it under WSL or
Docker.
| Platform | Status |
|---|---|
| macOS | Supported |
| Linux | Supported |
| Windows + WSL | Supported |
| Windows + Docker | Supported |
| Native Windows | Not supported — up, wrap, and the default command exit with WRTLS-110. down is allowed so existing state can be cleaned up. |
WORTHLESS_WINDOWS_ACK=1 suppresses the soft warning on down; it does not
bypass the hard gate on the other entry points.
Planned: native-Windows support (stdin Fernet key transport, Windows Job
Objects, DETACHED_PROCESS) is tracked in
WOR-237 —
target v1.2. If you need it sooner or want to help, comment on the issue
rather than patching around the guard locally.
install.sh / worthless.sh support matrix
curl worthless.sh | sh bootstraps uv and worthless with no Python required
on the target host. Coverage (run via pytest -m docker):
| Host | Status | Notes |
|---|---|---|
| Ubuntu 24.04 (bare) | Supported | no python, no uv |
| Ubuntu 22.04 (bare) | Supported | still the LTS most prod/CI boxes run |
Ubuntu 24.04 + pre-installed uv |
Supported | asserts uv is reused, not reinstalled (sha256 check) |
| Debian 12 (bare) | Supported | second glibc distro |
| Alpine / musl | Supported | uv fetches musl-compatible Python via PBS; zstd required for modern tarballs |
| macOS (Intel / ARM) | Supported | manual test on dev boxes |
| Fedora / RHEL | Untested | — |
| Windows + WSL | Untested (expected to work) | — |
| Native Windows | Not supported | see Platforms section |
All distros are pinned to linux/amd64 so arm64 hosts still exercise amd64
coverage. Per-distro verification runs verify_install.sh — checks resolved
binary path, --version, --help (exercises lazy imports), and scans stderr
for Traceback / ModuleNotFoundError.
Undo everything
$ worthless unlock
1 key(s) restored.
Original key is back. No trace.
Pre-commit hook
repos:
- repo: https://github.com/shacharm2/worthless
rev: main
hooks:
- id: worthless-scan
For AI coding agents
Worthless ships a SKILL.md that tells Claude Code, Cursor, and Windsurf what commands are available. Agents use --json for structured output:
worthless status --json
Development
git clone https://github.com/shacharm2/worthless && cd worthless
uv sync --extra dev --extra test
uv run pytest
Learn more
- Security model -- threat model, invariants, known limitations
- Engineering docs -- internal developer documentation for the live codebase
- Engineering architecture -- current internal architecture overview
- Contributor security rules -- invariants all contributions must preserve
- SKILL.md -- agent discovery file
Versioning
Three identifiers, kept loosely aligned:
- PyPI version (
pip install worthless==0.3.2) — canonical Python package. Source of truth:pyproject.toml. - Git tag (
v0.3.2) — same value, signed by the maintainer, drives both the PyPI publish and the worthless.sh Worker deploy. CI fails fast if the tag andpyproject.tomldisagree (see.github/workflows/publish.yml). X-Worthless-Script-Tagheader onhttps://worthless.sh/— labels the Worker deploy (which install.sh bytes you fetched). Usually matches the latest PyPI version; can drift when the Worker is redeployed without a new package release. Not a promise of PyPI alignment — for that, runpip installand checkworthless --version.
install.sh resolves the latest worthless from PyPI at install time;
override with WORTHLESS_VERSION=x.y.z curl … | sh to pin.
Contributing
PRs welcome. Read CONTRIBUTING-security.md first.
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file worthless-0.3.2.tar.gz.
File metadata
- Download URL: worthless-0.3.2.tar.gz
- Upload date:
- Size: 320.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7bc162b8a324d05f52bf6f3f5ed18dba8b5e1ea25da5c927ebe24ea721b78daf
|
|
| MD5 |
3c3e4f661d4a2c02e0ff1b7f2f3b9f4a
|
|
| BLAKE2b-256 |
1fe3caa35df02f0767c2d2ed330490031cb1ea7cfc5332ccc9060f18c7d6ca6e
|
Provenance
The following attestation bundles were made for worthless-0.3.2.tar.gz:
Publisher:
publish.yml on shacharm2/worthless
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
worthless-0.3.2.tar.gz -
Subject digest:
7bc162b8a324d05f52bf6f3f5ed18dba8b5e1ea25da5c927ebe24ea721b78daf - Sigstore transparency entry: 1408941681
- Sigstore integration time:
-
Permalink:
shacharm2/worthless@582a64f09218065f24a2471868869241f82fd370 -
Branch / Tag:
refs/tags/v0.3.2 - Owner: https://github.com/shacharm2
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@582a64f09218065f24a2471868869241f82fd370 -
Trigger Event:
push
-
Statement type:
File details
Details for the file worthless-0.3.2-py3-none-any.whl.
File metadata
- Download URL: worthless-0.3.2-py3-none-any.whl
- Upload date:
- Size: 130.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3be9a9d23ce0d9fb35905f756370454aa416fd0e512d078e707fbd744f5fe935
|
|
| MD5 |
9a4d0ce740939594f9a5f08a5dc4d3eb
|
|
| BLAKE2b-256 |
22b95ab943897d41e9e41ca2a320dc12ae11b1dd37708455297c3714cb4cd713
|
Provenance
The following attestation bundles were made for worthless-0.3.2-py3-none-any.whl:
Publisher:
publish.yml on shacharm2/worthless
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
worthless-0.3.2-py3-none-any.whl -
Subject digest:
3be9a9d23ce0d9fb35905f756370454aa416fd0e512d078e707fbd744f5fe935 - Sigstore transparency entry: 1408941762
- Sigstore integration time:
-
Permalink:
shacharm2/worthless@582a64f09218065f24a2471868869241f82fd370 -
Branch / Tag:
refs/tags/v0.3.2 - Owner: https://github.com/shacharm2
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@582a64f09218065f24a2471868869241f82fd370 -
Trigger Event:
push
-
Statement type: