{"id":50960423,"url":"https://github.com/avikalpg/byok-relay","last_synced_at":"2026-06-18T13:01:53.655Z","repository":{"id":351329257,"uuid":"1210534143","full_name":"avikalpg/byok-relay","owner":"avikalpg","description":"Bring your own key (BYOK) relay to enable lightweight frontend-only apps to use LLMs using the user's API keys without hitting CORS.","archived":false,"fork":false,"pushed_at":"2026-05-25T06:27:24.000Z","size":148,"stargazers_count":42,"open_issues_count":15,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-25T08:19:06.376Z","etag":null,"topics":["ai","ai-gateway","anthropic","api-proxy","bring-your-own-key","browser-safe","byok","cors","gemini","llm","llm-gateway","llmops","no-backend","nodejs","openai","relay","self-hosted"],"latest_commit_sha":null,"homepage":"https://byokrelay.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/avikalpg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-14T14:06:56.000Z","updated_at":"2026-05-25T07:03:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/avikalpg/byok-relay","commit_stats":null,"previous_names":["avikalpg/byok-relay"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/avikalpg/byok-relay","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avikalpg%2Fbyok-relay","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avikalpg%2Fbyok-relay/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avikalpg%2Fbyok-relay/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avikalpg%2Fbyok-relay/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/avikalpg","download_url":"https://codeload.github.com/avikalpg/byok-relay/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avikalpg%2Fbyok-relay/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34491239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-18T02:00:06.871Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-gateway","anthropic","api-proxy","bring-your-own-key","browser-safe","byok","cors","gemini","llm","llm-gateway","llmops","no-backend","nodejs","openai","relay","self-hosted"],"created_at":"2026-06-18T13:01:49.662Z","updated_at":"2026-06-18T13:01:53.467Z","avatar_url":"https://github.com/avikalpg.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# byok-relay\n\n**Website:** [byokrelay.com](https://byokrelay.com) | **Hosted relay:** [relay.byokrelay.com](https://relay.byokrelay.com)\n\n[![skills.sh](https://skills.sh/b/avikalpg/byok-relay)](https://skills.sh/avikalpg/byok-relay)\n[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Favikalpg%2Fbyok-relay\u0026env=ENCRYPTION_SECRET,ALLOWED_ORIGINS,APP_SECRET\u0026envDescription=ENCRYPTION_SECRET%3A%20generate%20with%20%60openssl%20rand%20-hex%2032%60.%20ALLOWED_ORIGINS%3A%20your%20frontend%20domain%20(e.g.%20https%3A%2F%2Fmy-app.vercel.app)\u0026envLink=https%3A%2F%2Fgithub.com%2Favikalpg%2Fbyok-relay%23setup\u0026project-name=byok-relay\u0026repository-name=byok-relay)\n\n**Your users already have AI keys. byok-relay lets them use those keys — straight from your frontend, with no CORS issues and no keys in your code.**\n\nBuilt for developers building prosumer tools and B2B AI products. Whether you're running a frontend-only app or have a full backend, byok-relay handles the BYOK plumbing — encrypted key storage, secure relay, multi-provider support — in minutes, not days. Your users bring their own OpenAI, Anthropic, or Gemini keys; you build the product; they pay for their own AI usage.\n\n## Managed relay\n\n**Skip the setup — use ours:**\n\n```\nhttps://relay.byokrelay.com\n```\n\nFree to use. Open CORS (any origin). [Health check →](https://relay.byokrelay.com/health)\n\n## For AI coding agents\n\nIf you're using a coding agent (Cursor, Claude Code, Copilot, Codex, etc.), install the skill and let it handle the integration:\n\n```bash\nnpx skills add avikalpg/byok-relay\n```\n\nOr point your agent directly at the skill file:\n\n```\nhttps://byokrelay.com/skill\n```\n\n\u003e Prompt: *\"Read the byok-relay skill at https://byokrelay.com/skill and integrate byok-relay into this project using the hosted relay at https://relay.byokrelay.com\"*\n\n## The problem\n\nBrowser apps can't call AI APIs directly:\n- `api.anthropic.com`, `api.openai.com`, and most AI providers **block browser requests via CORS**\n- Putting API keys in frontend code exposes them to every user\n\nThe common workaround — a backend proxy — means the *app developer* holds the keys. That's a trust problem, and it puts inference costs on your bill permanently.\n\n**byok-relay solves this differently:** the relay sits between your frontend and the AI provider. Users register their own keys once; every request after that uses their key, billed to their account.\n\n## How it compares\n\n| | byok-relay | OpenRouter | LiteLLM |\n|---|---|---|---|\n| Who holds the API keys | Your users | OpenRouter | Your org |\n| Who pays for AI usage | Your users | You (the dev) | You (the org) |\n| BYOK for end users | ✅ | ❌ | ❌ |\n| Browser-safe (CORS handled) | ✅ | ✅ | ❌ (needs backend) |\n| Self-hosted | ✅ | ❌ | ✅ |\n| Open source | ✅ Apache 2.0 | ❌ | ✅ |\n| Model routing / fallbacks | ❌ | ✅ | ✅ |\n\nUse OpenRouter or LiteLLM when you're paying for your users' AI and want routing + analytics. Use byok-relay when you want users to bring their own keys.\n\n## How it works\n\n```\nBrowser                  byok-relay              AI Provider\n  │                           │                       │\n  ├─ POST /users ────────────►│                       │\n  │◄─ { token } ─────────────┤                       │\n  │                           │                       │\n  ├─ POST /keys/anthropic ───►│                       │\n  │  { key: \"sk-ant-...\" }    │ (stored encrypted)    │\n  │◄─ { ok: true } ──────────┤                       │\n  │                           │                       │\n  ├─ POST /relay/anthropic ──►│                       │\n  │  x-relay-token: \u003ctoken\u003e   ├─ (real key injected) ►│\n  │  { model, messages... }   │                       │\n  │◄─ streamed response ──────┤◄─ streamed response ──┤\n```\n\nThe `token` (not the API key) lives in the browser. The API key stays server-side, encrypted at rest with AES-256-GCM.\n\n## Quickstart (60 seconds)\n\n```bash\n# 1. Clone and install\ngit clone https://github.com/avikalpg/byok-relay.git \u0026\u0026 cd byok-relay \u0026\u0026 npm install\n\n# 2. Configure\necho \"ENCRYPTION_SECRET=$(openssl rand -hex 32)\" \u003e .env\necho \"ALLOWED_ORIGINS=http://localhost:3000\" \u003e\u003e .env\n\n# 3. Start (add APP_SECRET for production to restrict who can register users)\n# echo \"APP_SECRET=$(openssl rand -hex 32)\" \u003e\u003e .env\nnpm start \u0026\n\n# 4. Register a user and get a token\nTOKEN=$(curl -s -X POST http://localhost:3000/users \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"app_id\":\"test\"}' | python3 -c \"import sys,json; print(json.load(sys.stdin)['token'])\")\n\n# 5. Store your Anthropic key\ncurl -X POST http://localhost:3000/keys/anthropic \\\n  -H \"Content-Type: application/json\" \\\n  -H \"x-relay-token: $TOKEN\" \\\n  -d '{\"key\":\"sk-ant-YOUR-KEY-HERE\"}'\n\n# 6. Relay a request (streaming)\ncurl -X POST http://localhost:3000/relay/anthropic/v1/messages \\\n  -H \"Content-Type: application/json\" \\\n  -H \"anthropic-version: 2023-06-01\" \\\n  -H \"x-relay-token: $TOKEN\" \\\n  -d '{\"model\":\"claude-3-5-haiku-20241022\",\"max_tokens\":256,\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"Hello!\"}]}'\n```\n\n## Supported providers\n\n| Provider | Name | Notes |\n|---|---|---|\n| Anthropic | `anthropic` | Claude models, SSE streaming |\n| OpenAI | `openai` | GPT models, SSE streaming |\n| Google | `google` | Gemini API (key in query param) |\n| Groq | `groq` | Fast inference, OpenAI-compatible |\n| OpenRouter | `openrouter` | 200+ models via one API |\n| Mistral | `mistral` | Mistral models |\n| Any OpenAI-compatible | `openai-compatible` | Pass `x-relay-base-url` header — covers LiteLLM, Ollama, Perplexity, Together AI, and any other OpenAI-compatible endpoint |\n\nAdding a new built-in provider is ~5 lines in `src/providers.js`.\n\n## API\n\n### Register a user\n```http\nPOST /users\nContent-Type: application/json\n\n{ \"app_id\": \"my-app\" }\n```\n→ `{ \"token\": \"\u003crelay-token\u003e\" }` — store in browser localStorage\n\n\u003e **If `APP_SECRET` is set**, the request must include `Authorization: Bearer \u003csecret\u003e`:\n\u003e ```http\n\u003e POST /users\n\u003e Content-Type: application/json\n\u003e Authorization: Bearer \u003cAPP_SECRET\u003e\n\u003e \n\u003e { \"app_id\": \"my-app\" }\n\u003e ```\n\u003e Without a valid `Authorization` header, the server returns `401 Unauthorized`.\n\n### Store an API key\n```http\nPOST /keys/anthropic\nx-relay-token: \u003ctoken\u003e\nContent-Type: application/json\n\n{ \"key\": \"sk-ant-...\" }\n```\n\n### List stored providers (key values never returned)\n```http\nGET /keys\nx-relay-token: \u003ctoken\u003e\n```\n\n### Delete a key\n```http\nDELETE /keys/anthropic\nx-relay-token: \u003ctoken\u003e\n```\n\n### Relay a request\n```http\nPOST /relay/anthropic/v1/messages\nx-relay-token: \u003ctoken\u003e\nContent-Type: application/json\nanthropic-version: 2023-06-01\n\n{ \"model\": \"claude-3-5-haiku-20241022\", \"max_tokens\": 1024, \"messages\": [...], \"stream\": true }\n```\nFull streaming (SSE) is supported — the response is piped directly from the provider to the browser.\n\n### Generic OpenAI-compatible relay\n```http\nPOST /relay/openai-compatible/v1/chat/completions\nx-relay-token: \u003ctoken\u003e\nx-relay-base-url: https://openrouter.ai\nContent-Type: application/json\n\n{ \"model\": \"...\", \"messages\": [...] }\n```\n\n## Deploy in one click\n\nThe fastest way to get byok-relay running is via Vercel:\n\n[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Favikalpg%2Fbyok-relay\u0026env=ENCRYPTION_SECRET,ALLOWED_ORIGINS,APP_SECRET\u0026envDescription=ENCRYPTION_SECRET%3A%20generate%20with%20%60openssl%20rand%20-hex%2032%60.%20ALLOWED_ORIGINS%3A%20your%20frontend%20domain%20(e.g.%20https%3A%2F%2Fmy-app.vercel.app)\u0026envLink=https%3A%2F%2Fgithub.com%2Favikalpg%2Fbyok-relay%23setup\u0026project-name=byok-relay\u0026repository-name=byok-relay)\n\n1. Click the button above\n2. Set `ENCRYPTION_SECRET` (generate: `openssl rand -hex 32`) and `ALLOWED_ORIGINS` (your frontend domain)\n3. Deploy — your relay is live at `https://byok-relay-\u003chash\u003e.vercel.app`\n\n\u003e **Note:** Vercel's serverless environment has an ephemeral filesystem, so SQLite state resets between cold starts. This is fine for demos and prototyping. For production with persistent key storage, deploy to a long-running server (see [Production setup](#production-ubuntu--systemd) below, or use Railway/Render).\n\n## Setup\n\n### 1. Install\n```bash\ngit clone https://github.com/avikalpg/byok-relay.git\ncd byok-relay\nnpm install\n```\n\n### 2. Configure\n```bash\ncp .env.example .env\n# Set ENCRYPTION_SECRET (generate: openssl rand -hex 32)\n# Set ALLOWED_ORIGINS to your app's domain(s)\n```\n\n### 3. Run\n```bash\nnpm start\n```\n\n### Production (Ubuntu + systemd)\n```bash\n# Copy service file\nsudo cp deploy/byok-relay.service /etc/systemd/system/\nsudo systemctl enable --now byok-relay\n\n# HTTPS with nginx + Let's Encrypt\nsudo apt install nginx\nsudo snap install --classic certbot\nsudo certbot --nginx -d relay.yourdomain.com\n```\n\n## Security\n\n- **AES-256-GCM encryption** — keys are encrypted at rest; the `ENCRYPTION_SECRET` lives only in your server environment\n- **Keys never returned** — after the initial POST, the key value is never sent over the wire again\n- **Registration gate** — set `APP_SECRET` to require `Authorization: Bearer \u003csecret\u003e` on `POST /users`; without it anyone who reaches your relay can register. Generate with `openssl rand -hex 32`.\n- **Rate limiting** — 100 req/min global, 20 AI req/min per token, 10 registrations/hour per IP\n- **Startup validation** — server refuses to start without a valid `ENCRYPTION_SECRET`\n- **CORS** — restrict `ALLOWED_ORIGINS` to your app's domain in production\n- **HTTPS required** in production (mixed-content browsers block HTTP endpoints called from HTTPS pages)\n\n## BYOK — your users pay for what they use\n\nTwo patterns, one integration:\n\n**Prosumer / individual** — each user registers their own API key once. They use their own credits; you spend $0 on inference. Great for developer tools, research UIs, or any product where users already have API accounts.\n\n**Team / B2B** — a company admin registers the org's shared API key once. The relay token lives in your app's backend; all team members access AI through your app, which routes requests automatically. Billing, usage, and key rotation are managed inside the customer's organisation — not by you.\n\nbyok-relay handles both patterns today.\n\n## Trade-offs\n\n- **You hold the encrypted keys** — users trust your server. Mitigate with a cloud KMS-backed store for higher assurance.\n- **No built-in user accounts** — the relay token is the only credential. Scope tokens to IP or add your own auth layer for production.\n- **Self-hosted** — you're responsible for uptime, security updates, and backups. Or use [relay.byokrelay.com](https://relay.byokrelay.com) and skip all of that.\n\n## Find us on\n\n- [There's An AI For That](https://theresanaiforthat.com) — *submission in review*\n- [skills.sh](https://skills.sh/avikalpg/byok-relay) — AI coding agent skill registry\n- [Awesome LLMOps](https://github.com/tensorchord/Awesome-LLMOps) — *PR in review*\n- [Awesome ChatGPT API](https://github.com/reorx/awesome-chatgpt-api) — *PR in review*\n\n## License\n\nApache 2.0\n\n---\n\n**Ready to integrate?** → Use `npx skills add avikalpg/byok-relay` or point your coding agent at [byokrelay.com/skill](https://byokrelay.com/skill)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favikalpg%2Fbyok-relay","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Favikalpg%2Fbyok-relay","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favikalpg%2Fbyok-relay/lists"}