{"id":51068932,"url":"https://github.com/openclaw/clawrouter","last_synced_at":"2026-06-23T09:00:34.612Z","repository":{"id":366452637,"uuid":"1259924435","full_name":"openclaw/clawrouter","owner":"openclaw","description":"ClawRouter API gateway and provider router for OpenClaw services","archived":false,"fork":false,"pushed_at":"2026-06-21T22:26:52.000Z","size":907,"stargazers_count":0,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-21T23:17:51.412Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openclaw.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"AGENTS.md","dco":null,"cla":null},"funding":{"github":["moltbot"]}},"created_at":"2026-06-05T01:57:58.000Z","updated_at":"2026-06-21T22:26:55.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/openclaw/clawrouter","commit_stats":null,"previous_names":["openclaw/clawrouter"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/openclaw/clawrouter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Fclawrouter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Fclawrouter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Fclawrouter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Fclawrouter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openclaw","download_url":"https://codeload.github.com/openclaw/clawrouter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Fclawrouter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34682632,"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-23T02:00:07.161Z","response_time":65,"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":[],"created_at":"2026-06-23T09:00:26.589Z","updated_at":"2026-06-23T09:00:34.593Z","avatar_url":"https://github.com/openclaw.png","language":"Rust","funding_links":["https://github.com/sponsors/moltbot"],"categories":[],"sub_categories":[],"readme":"# ClawRouter\n\nClawRouter is a high-throughput API gateway and provider router for OpenClaw services.\n\nIt brokers proxy keys, service identities, versioned upstream grants, budgets, and metered usage across model providers, search APIs, tool APIs, and future service providers.\n\nCurrent implementation target:\n\n- TypeScript data plane on Cloudflare Workers\n- Durable Object budget ledgers\n- serialized Durable Object access-control authority\n- tenant/policy-sharded Durable Object usage ledgers\n- TypeScript admin/control UI\n- declarative service provider manifests\n- OpenClaw-native `clawrouter-` key routing\n- Cloudflare KV-backed one-time migration seeds, version 1\n  API-key/OAuth/subscription grants, assignment rules, and provider health\n\n## Provider Registry\n\nProvider support is data-driven. Most integrations are added by creating one file:\n\n```text\nproviders/\u003cservice\u003e.provider.yaml\n```\n\nThat file declares the service id, auth scheme, OAuth platform mapping when needed,\nbase URLs, route templates, adapter family, model/capability mapping, and billing\nmeters. The TypeScript compiler turns those files into a provider snapshot consumed by\nthe edge runtime and admin UI.\n\nBuilt-in starter coverage:\n\n- model APIs: OpenAI, Anthropic, Google Gemini, Azure OpenAI, AWS Bedrock,\n  MiniMax, Mistral, Cohere, xAI, Groq, Perplexity, DeepSeek, Together, Fireworks,\n  Hugging Face, Replicate\n- gateway APIs: OpenRouter, Cloudflare AI Gateway\n- tool/API platforms: Tavily, Firecrawl (keyless starter access; API key\n  optional for higher limits)\n\nValidate the catalog with:\n\n```sh\npnpm provider:compile\n```\n\nBefore a real Cloudflare deploy, run:\n\n```sh\npnpm cf:doctor\n```\n\nIt checks Wrangler auth, required GitHub Actions secret names, local deploy env,\nprovider binding coverage, and the all-provider smoke plan without printing\nsecret values. Real deploys require at least one live golden-provider smoke;\nthe result is persisted in `POLICY_KV` so readiness distinguishes verified,\nfailed, stale, unverified, and disabled providers.\n\nThe browser console is meant to sit behind Cloudflare Access. Provision that\nedge gate with:\n\n```sh\nCLOUDFLARE_ACCOUNT_ID=... \\\nCLOUDFLARE_API_TOKEN=... \\\nCLAWROUTER_ACCESS_GITHUB_ORGS=openclaw \\\nCLAWROUTER_ACCESS_ADMIN_EMAILS=you@example.com \\\npnpm cf:access\n```\n\nThen redeploy with the printed `CLAWROUTER_ACCESS_TEAM_DOMAIN` and\n`CLAWROUTER_ACCESS_AUD` values. `/` redirects to the Access-protected\n`/dashboard` path, `/dashboard` redirects to the role-aware `/dashboard/home`, and canonical console views live under `/dashboard/*`, while\npublic `/v1` catalog and proxy routes stay normal. The Access app must also\nprotect `/v1/session*`, `/v1/playground/*`, `/v1/admin/*`, and\n`/v1/oauth/callback` so the browser console can bootstrap identity,\nentitlements, session quota usage, playground calls, admin mutations, and OAuth callbacks from a\nverified Access session. A ClawRouter `access_session_required` JSON body on\n`/dashboard/*`, `/v1/session`, `/v1/session/usage`, `/v1/playground/*`, or `/v1/oauth/callback` means the Access app is not\nin front of that console path yet, and `pnpm cf:smoke` treats that as a failed\ndeployment smoke.\n\nThe `Deploy Cloudflare` workflow can do the Access step too: dispatch it with\n`provision_access=true` after adding a `CLOUDFLARE_API_TOKEN` that can manage\nZero Trust Access apps and policies. The production default admits verified\nmembers of the `openclaw` GitHub organization through the configured GitHub\nidentity provider. Real deploys also require KV write\npermission because Wrangler validates the Worker `POLICY_KV` binding while\npublishing. Keep `access_domain` set to the console hostname; `worker_url` is\nonly the post-deploy smoke-test target.\n\n## Edge Proxy\n\nThe Worker currently exposes:\n\n- `GET /v1/health`\n- `GET /v1/providers`\n- `GET /v1/routes`\n- `GET /v1/session`\n- `GET /v1/entitlements`\n- `GET /v1/me`\n- `GET /v1/usage`\n- `GET /v1/models`\n- `GET /v1/catalog`\n- `GET /v1/oauth/callback`\n- `GET /v1/key/inspect`\n- `POST /v1/chat/completions`\n- `POST /v1/responses`\n- `POST /v1/embeddings`\n- `POST /v1/messages`\n- `POST /v1/messages/count_tokens`\n- `POST /v1/proxy/\u003cprovider\u003e/\u003cendpoint\u003e`\n- `\u003cMETHOD\u003e /v1/native/\u003cprovider\u003e/\u003cprovider-native-path\u003e`\n- `GET /v1/admin/overview`\n- `GET /v1/admin/bootstrap`\n- `GET /v1/admin/tenants`\n- `GET /v1/admin/usage`\n- `GET /v1/admin/policies`\n- `GET /v1/admin/credentials`\n- `GET /v1/admin/connections`\n- `GET /v1/admin/access-users`\n- `GET /v1/admin/policy-bindings`\n- `GET /v1/admin/provider-status`\n- `GET /v1/admin/provider-health`\n- `GET /v1/admin/upstream-grants`\n- `GET /v1/admin/assignment-rules`\n- `PUT /v1/admin/access-users/\u003cemail\u003e`\n- `PUT /v1/admin/access-user-grants/\u003cemail\u003e`\n- `PUT /v1/admin/policy-bindings`\n- `PUT /v1/admin/policies/\u003cpolicy-id\u003e`\n- `PUT /v1/admin/credentials/\u003ccredential-id\u003e`\n- `PUT /v1/admin/connections/\u003cprovider-id\u003e`\n- `PUT /v1/admin/upstream-grants/\u003cpolicies|tenants\u003e/\u003cscope-id\u003e/\u003ctoken-ref\u003e`\n- `PUT /v1/admin/assignment-rules/\u003crule-id\u003e`\n- `POST /v1/admin/policies/\u003cpolicy-id\u003e/revoke`\n- `POST /v1/admin/credentials/\u003ccredential-id\u003e/revoke`\n- `POST /v1/admin/upstream-grants/\u003cpolicies|tenants\u003e/\u003cscope-id\u003e/\u003ctoken-ref\u003e/revoke`\n- `POST /v1/admin/upstream-grants/\u003cpolicies|tenants\u003e/\u003cscope-id\u003e/\u003ctoken-ref\u003e/refresh`\n- `POST /v1/admin/upstream-grants/\u003cpolicies|tenants\u003e/\u003cscope-id\u003e/\u003ctoken-ref\u003e/authorize`\n- `POST /v1/admin/assignment-rules/reconcile`\n\nLegacy `GET|PUT /v1/admin/keys...`, `POST /v1/admin/keys/\u003ckid\u003e/revoke`, and\n`GET /v1/admin/users` remain compatibility aliases during migration. New\ncontrol-plane clients should use policies, credentials, and tenants directly.\nThe legacy revoke alias treats `\u003ckid\u003e` as a credential id and never disables a\nshared policy.\n\nOpenAI-compatible proxy requests route by the request body `model` field, for\nexample `openai/gpt-4.1-mini`. Before an upstream provider secret is used, the\nWorker verifies the issued credential and its policy from serialized\n`ACCESS_CONTROL` Durable Object authority. `credentials/\u003ccredential-id\u003e` and\n`policies/\u003cpolicy-id\u003e` in `POLICY_KV` are one-time migration seeds:\n\n```json\n{\"enabled\":true,\"secretSha256\":\"\u003csha256 of key secret\u003e\",\"policyId\":\"team_docs\",\"policyGeneration\":\"policy_...\"}\n```\n\n```json\n{\n  \"enabled\": true,\n  \"generation\": \"policy_...\",\n  \"providers\": [\"openai\"],\n  \"allProviders\": false,\n  \"tenantId\": \"team_docs\",\n  \"tokenRole\": \"service\",\n  \"monthlyBudgetMicros\": 100000000\n}\n```\n\nPolicy and credential generations must match. Strongly consistent authority\nwrites make revocation and scope reductions immediate; generation mismatches\nstill fail closed during migration or incomplete rotations. Canonical policy\nedits preserve their generation. The legacy key mutation alias rejects changing\npolicy scope and secret together.\n\n`GET /v1/catalog` is the credential-scoped client integration contract. It\nreturns only allowed providers and executable models. Each provider row reports\nwhether the unified OpenAI-compatible `/v1` route is available, its native proxy\nbase URL, and the request/response formats for its executable native routes.\nClients can use those fields to choose the real provider transport without\nguessing from provider ids.\n\nDisable a credential to revoke one issued key, disable a policy to revoke every\nuser and credential bound to it, or disable a provider connection to stop that\nprovider globally. Legacy `keys/\u003ckid\u003e` records remain readable during\nmigration only when they are genuine pre-migration records. Generation-bearing\ncompatibility records stay disabled and are never authorization fallback. See\n`docs/deploy-cloudflare.md` for Cloudflare provisioning, deployment, key\nregistration, and smoke commands.\n\nThe legacy-named `pnpm cf:oauth:put` and `pnpm cf:oauth:revoke` helpers write\ncanonical version 1 upstream-grant records for `api_key`, `oauth`, and\n`subscription` connections. They accept access tokens, refresh tokens, and\nsingle- or multi-field credentials only through stdin, environment variables,\nor files, never argv.\nRevocation retains grant metadata in a disabled tombstone while removing\nsecrets. See `docs/deploy-cloudflare.md` for the complete operator flow.\n\nAdmin endpoints accept a verified Cloudflare Access admin session or\n`Authorization: Bearer \u003cadmin-token\u003e` against `CLAWROUTER_ADMIN_TOKEN_SHA256`.\nProvider-approved browser OAuth starts only from a verified Access admin\nsession, uses a one-time PKCE state, and stores the resulting grant without\nreturning provider tokens to the browser.\nThe browser console hashes generated proxy key secrets in-browser before\nissuing a credential, manages policies separately from credentials and provider\nconnections, assigns explicit user/group policy bindings, shows provider\nreadiness and request audit, and includes a Cloudflare Access-backed playground\nfor model and manifest-proxy service routes. Admin rights come from the Access\nadmin allowlist, not editable user rows, and do not imply provider access.\n\nGeneric REST/tool proxy requests are manifest-driven:\n\n```sh\ncurl \"$CLAWROUTER_BASE_URL/v1/proxy/tavily/search\" \\\n  -H \"authorization: Bearer $CLAWROUTER_KEY\" \\\n  -H \"content-type: application/json\" \\\n  --data '{\"body\":{\"query\":\"openclaw\"},\"query\":{\"topic\":\"news\"}}'\n```\n\nThe Worker resolves `provider` and `endpoint` from the compiled provider\nsnapshot, applies manifest path/query/header/auth mapping, forwards the request,\nand emits a usage event to `USAGE_QUEUE`. The same Worker consumes that queue\ninto tenant/policy-sharded, bounded `USAGE_LEDGER` reporting Durable Objects and\nreplays unsettled budget updates. Audit events retain\nidentity, policy, credential, provider, route capability, model, timing,\noutcome, tokens when safely available, and cost for 30 days. Prompt and\ncompletion bodies are never stored in the usage ledger. LLM request bodies are stored separately for\n30 days when the access policy enables request-content retention (the default)\nand the credential owner is not exempt; see [Content retention](docs/content-retention.md).\n`/v1/usage` returns the caller policy's\nbudget plus usage summary; `/v1/admin/usage` returns budget rows plus the\nall-tenant usage summary and recent request audit.\n\nBudgeted requests reserve an upper-bound token cost before the upstream call\nwhen the selected model has versioned pricing in its provider manifest. An\nexplicit policy `requestCostMicros` remains a fixed-cost override; unpriced\nroutes retain the one-micro fallback only outside monthly budgets. Budgeted\ncalls without versioned pricing fail closed. Successful responses settle\ntheir actual token cost, including cached input when reported. SSE responses are metered\ninline without buffering the client stream. Missing or interrupted usage stays\ncharged at the conservative reservation; non-2xx and transport failures refund\nthe reservation. Failed settlement calls are persisted to `USAGE_QUEUE` for\ndurable retry while the reservation remains charged fail-closed. Settlement and\naudit delivery are independent: failure of either cannot suppress the provider\nresponse or the other accounting operation. Messages that exhaust automatic retries move to the\nseparate usage DLQ for operator inspection and replay before Cloudflare's\nfour-day unconsumed-DLQ retention expires. Internal reservation ids are\ngenerated independently from caller-supplied request ids.\n\nCodex and Claude Code integration, pricing fields, reservation semantics, and\nagent attribution headers are documented in\n[`docs/agent-spend-control.md`](docs/agent-spend-control.md).\nOAuth, SigV4, and deployment-templated providers execute through the same\npolicy-enforced proxy when their manifest-declared grant and remaining runtime\nconfiguration are present. Provider secrets can come from scoped upstream\ngrants instead of Worker-global bindings.\n\nControl-plane boundaries, migration rules, refresh behavior, and failure\nsemantics are summarized in [Architecture](docs/architecture.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenclaw%2Fclawrouter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenclaw%2Fclawrouter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenclaw%2Fclawrouter/lists"}