claude-relay
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 8 GitHub stars
Code Warn
- process.env — Environment variable access in clients/ts/claudeRelay.ts
- network request — Outbound network request in clients/ts/claudeRelay.ts
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Self-hosted, secured HTTP API around headless Claude Code — per-app keys, all tools locked off, zero deps. Use Claude from your own apps.
claude-relay
Turn the Claude Code you already run into a tiny, secured HTTP API for your own apps.
Self-hosted. One file of stdlib Python. Per-app keys, rate limits, and every tool locked off
so a prompt can never touch your machine.
You run Claude Code on a machine where you're logged in. claude-relay wraps claude -p
behind a small HTTP endpoint so any of your apps, scripts, or cron jobs can call Claude over
the network — instead of wiring the Anthropic SDK into each one.
curl -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"system":"You are terse.","prompt":"Name 3 primary colors as a JSON array."}' \
https://your-relay.example/v1/complete
# {"ok":true,"model":"opus","duration_ms":1900,"text":"[\"red\",\"green\",\"blue\"]"}
- 🔒 Can't be turned against your machine. Every call runs with all tools and MCP disabled —
no bash, no file access, no network. Pure text in → text out. (There's a test that proves it.) - 🔑 Per-app keys. Each app gets its own key (sha256-hashed at rest), revocable in one command,
applied instantly. Plus rate limits and a concurrency cap so nothing runs away. - 🪶 Tiny + boring. One ~250-line Python file, zero dependencies, installs as a
systemd --user
service with no root. Easy to read, audit, and trust.
[!IMPORTANT]
Know your plan's terms. Claude consumer subscriptions and Claude Code are intended for
individual, interactive use. Pointing a production app — or other people's traffic — at a
personal subscription may violate Anthropic's terms and can get your account suspended. Use this
for your own projects, internal tools, and automation, and keep volume sane. For products that
serve customers or need guaranteed throughput, use the Anthropic API.
You are responsible for complying with the terms of whatever account you point it at.
Quickstart
On a Linux host where claude is installed and logged in:
git clone https://github.com/dimabru/claude-relay ~/claude-relay
cd ~/claude-relay
./install.sh # starts the service (systemd --user, no root)
./bin/genkey my-app # mint a key — copy the secret it prints
That's it — the relay is live on 127.0.0.1:8799. No systemd? Just ./run.sh to run it in the
foreground.
Expose it (permanent URL, no domain to buy)
The service binds to localhost; a tunnel is the only way in. Easiest free option — an
ngrok dev domain (one permanent *.ngrok-free.dev, no domain needed):
ngrok config add-authtoken <YOUR_TOKEN> # free account
# claim your free domain at https://dashboard.ngrok.com/domains, then put it in config.env:
# NGROK_DOMAIN=your-name.ngrok-free.dev
cp systemd/claude-relay-ngrok.service ~/.config/systemd/user/
systemctl --user daemon-reload && systemctl --user enable --now claude-relay-ngrok
Already on Tailscale? tailscale funnel 8799 gives a permanent https://<host>.<tailnet>.ts.net
with no extra account.
API
POST /v1/complete — Authorization: Bearer <key>
| field | required | notes |
|---|---|---|
prompt |
✅ | the user prompt |
system |
system prompt | |
model |
opus (default), sonnet, or haiku |
|
output_format |
text (default) → {text}; json → {result, usage} |
POST /v1/fetch — Authorization: Bearer <key> — proxy a page fetch through the relay host's
IP (for apps on datacenter IPs that shops/search engines bot-wall). SSRF-guarded (private/loopback
targets rejected on every redirect hop), size-capped, and rate-limited in its own bucket so page
fetches never spend the LLM budget.
| field | required | notes |
|---|---|---|
url |
✅ | http/https, must resolve to a public address |
max_bytes |
HTML size cap in the response (default 500 KB, max 2 MB) | |
strip |
default true: drop scripts/styles/svg/comments, keep JSON-LD |
→ {ok, status, final_url, truncated, html}.
Also GET /v1/models (auth) and GET /health (no auth). Errors: 401 unauthorized, 400 bad
request, 413 too large, 429 rate limited (+Retry-After), 502/504 upstream error/timeout.
Use it from an app
Drop-in TypeScript and Python
clients read CLAUDE_RELAY_URL + CLAUDE_RELAY_KEY from the environment. Server-side only —
never ship the key to a browser.
import { complete } from "./claudeRelay";
const colors = await complete({ system: "Output only JSON.", prompt: "3 primary colors", outputFormat: "json" });
Security model
Three independent layers — see SECURITY.md for the full threat model.
| Goal | How |
|---|---|
| A prompt can't touch the host | every claude -p runs --disallowed-tools <all> + --strict-mcp-config --mcp-config '{}', default permission mode, throwaway cwd. Never --dangerously-skip-permissions. |
| Your account can't be drained | global concurrency cap + per-key requests/minute & per-day + a global daily cap → 429 Retry-After. |
| Only your apps get in | per-client bearer keys, sha256-hashed in keys.json, labeled, revocable, hot-reloaded. Binds 127.0.0.1 only. Audit log records label/model/status/duration — never prompt content. |
No secrets in the repo: config.env, keys.json, and your tunnel token are gitignored; your Claude
login stays in ~/.claude, never here.
Manage keys
./bin/genkey <label> [daily_limit] # mint (prints the secret once)
./bin/revoke <label> # revoke — effective immediately (hot-reloaded)
Configure (config.env)
PORT, DEFAULT_MODEL, ALLOWED_MODELS, MAX_INPUT_BYTES, REQ_TIMEOUT, GLOBAL_CONCURRENCY,PER_KEY_RPM, PER_KEY_DAILY, GLOBAL_DAILY, NGROK_DOMAIN.
Who it's for
✅ Your side projects, internal tools, scripts, cron jobs, and homelab automation — one place to
call Claude from anything you own.
🚫 Not a way to resell Claude, run a public service on a personal plan, or dodge the API for a
product with real users. That risks your account and isn't what subscriptions are for.
FAQ
Does this need an Anthropic API key? No — it shells out to your logged-in Claude Code. (See the
terms note above.)
Can a caller make Claude run shell commands on my box? No. Tools are denied at the CLI level and
proven blocked by a test; the service also binds to localhost only.
Why not just use the API? If you can, use the API — it's the supported path for apps. This exists
for personal use of a subscription you already pay for.
Windows / macOS? The service is pure Python and runs anywhere via ./run.sh; the install.sh
helper targets Linux + systemd --user.
Contributing
Issues and PRs welcome — see CONTRIBUTING.md. Be kind: Code of Conduct.
License
MIT. Not affiliated with Anthropic. "Claude" is a trademark of Anthropic.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found