feat(web): add OrcaRouter as a first-class provider with API key and PKCE auth - #346
Open
martinzudergaming-a11y wants to merge 1 commit into
Open
martinzudergaming-a11y wants to merge 1 commit into
martinzudergaming-a11y wants to merge 1 commit into
Conversation
…PKCE auth Signed-off-by: martinzudergaming-a11y <martinzudergaming-a11y@users.noreply.github.com>
Contributor
|
@martinzudergaming-a11y is attempting to deploy a commit to the Vercel Labs Team on Vercel. A member of the Team first needs to authorize it. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
| }; | ||
| } | ||
| try { | ||
| const catalog = await fetchOrcaCatalog({ |
Contributor
There was a problem hiding this comment.
discoverOrcaModels() hardcodes capability: "chat" when fetching the upstream catalog, so the /api/orcarouter/models?capability=… route requests a chat-filtered catalog for every capability, making non-chat requests (embedding/image/video/rerank) return empty results if upstream honors the server-side ?capability= filter.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds OrcaRouter as a first-class provider in the json-render web app, with two explicit authentication choices and a model control built from the live catalog.
OrcaRouter is an OpenAI-compatible AI gateway that routes many providers behind one endpoint.
I'm an engineer on the OrcaRouter team. This PR is made on behalf of OrcaRouter, in coordination with the project. I am not affiliated with vercel-labs.
packages/core/src/orcarouter/(exported from@json-render/core) — inference base URLhttps://api.orcarouter.ai/v1,Authorization: Bearer <key>.orcarouter, labelOrcaRouter - API. The key is read fromORCAROUTER_API_KEYor pasted into the connect panel; it is stored in the Next.js env file this app already uses for provider secrets (apps/web/.env.local, already gitignored). No second credential store is introduced.packages/coreis a Node library and the primary consumer is a Node/Next server process that can bind127.0.0.1:<random port>, so the code returns automatically and the user clicks once; the hosted deployment (json-render.dev) cannot receive a callback on the user's machine, so the same S256 verifier, exchange, and seam are also reachable through the out-of-band path. Flow C (device grant) is not implemented.orcarouter-oauth, labelOrcaRouter - Auth— distinct labels everywhere both appear, as the spec asks.The two choices are adapters over one
OrcaCredentialSourceinterface and resolve to the sameOrcaCredentialResult, so/api/generate,/api/docs-chat, and model discovery never learn which entry point produced the key.How the credential works
The key belongs to the user, not to this project: it is billed to their OrcaRouter account, listed in their console, and revocable by them at any time from
https://www.orcarouter.ai/console/authorized-apps. No client secret is involved — PKCE binds the auth code to this process, so an intercepted code cannot be redeemed by anyone else.packages/core/src/orcarouter/origins.ts): authorization ishttps://www.orcarouter.aiwith the fixed paths/authand/api/v1/auth/keys; inference and the model catalog arehttps://api.orcarouter.ai/v1.https://api.orcarouter.ai/v1/auth/keysis a 404 and is never derived by swapping a hostname or appending/v1. Explicit per-role overrides (ORCA_AUTH_BASE_URL,ORCA_API_BASE_URL) win over the sharedORCA_BASE_URL; remote origins must be HTTPS and plain HTTP is accepted only for loopback.statefrom a CSPRNG on every attempt;code_challenge = base64url(sha256(verifier))with no padding; the verifier stays in the process until exchange and never appears in a URL, log, error, or telemetry; the Flow A callback comparesstatein constant time. Denial, state mismatch, timeout, expired/reused code, 403, 429, and network failure all end the attempt with an actionable message instead of hanging or retrying in a loop.scopeis read and checked against what this client can use (api). The requested scope is never treated as the granted scope; a response that does not satisfy the requirement is rejected.sk-orca-…key, not a refresh token. The stored key is reused across restarts until it is revoked; there is no proactive refresh and no invented refresh grant. Reconnecting is never done on startup, which keeps a user well under the 10-key/24h issuance cap.CredentialStore), so a late async 401 cannot invalidate the credential a user just reconnected. The old secret is not silently deleted before a new login succeeds./api/orcarouter/models,/api/orcarouter/connect/*, and/api/orcarouter/credentialkeep the key server-side; the client receives only masked state and minimal model metadata.secret_maskedis asserted in the UI evidence below.Model catalog and capability filtering
GET https://api.orcarouter.ai/v1/modelsis the only source of truth, requested with the user's key so the list reflects that workspace's actually callable models. Model IDs keep theirvendor/modelnamespace verbatim. When OrcaRouter is selected the model control is a searchable dropdown built from that response — there is no free-text model field.selectOrcaModelsfilters per entry point:?capability=chat, andsupported_endpoint_typesmust include one ofopenai/anthropic/gemini/openai-response; image-generation, openai-video, and jina-rerank models are excluded;architecture.input_modalitiesmust explicitly contain the modality the entry point actually uploads. A record with noarchitectureblock fails closed and never enters the multimodal list;?capability=embeddingor a strictembeddingsendpoint match;?capability=imageor a strictimage-generationmatch;openai-video; rerank: strictjina-rerank.Options are recomputed when the provider changes, when an attachment/modality/task changes, and when the catalog is refreshed. An already-selected model that is no longer compatible is cleared with a prompt to reselect rather than silently kept — the playground's image attachment is the concrete case, and the selector's options (not just a send-time guard) are what is filtered. Loading, empty, auth-error, network-error, refresh, and caching states are implemented; a live success is authoritative and the five-model seed (
openai/gpt-5.5,anthropic/claude-opus-4.8,google/gemini-3.5-flash,deepseek/deepseek-v4-pro,orcarouter/auto) is only an outage fallback that is labelled degraded and never merged into a live result. A persisted model ID is re-validated against the compatible list before it is restored.AI entry points covered
apps/web/app/api/generate/route.ts: OrcaRouter selectable; model from the live chat catalog; server-side credential seam; Bearer againstapi.orcarouter.ai/v1.apps/web/app/api/docs-chat/route.ts: same provider/model resolution.packages/core/src/experimental-evaluator.ts(the Jev path) speaks the Vercel AI Gateway v4 evaluation protocol, which OrcaRouter does not implement, so it keeps its existing behavior.examples/*are standalone demos outside the published packages and outside CI; the reusable provider lives inpackages/coreso they can adopt it.examples/imagerenders locally withsatori/resvgandexamples/remotionrenders locally, neither calls a model. The capability filters above are implemented and tested for the day an entry point exists.Testing
pnpm install --frozen-lockfile, then:npx vitest run— 92 files, 1409 tests, all passing on this head (live tests skip without a key).npx vitest run packages/core/src/orcarouter apps/web/lib/orcarouter— 10 files, 194 tests, all passing.npx vitest run apps/web/lib/orcarouter/live.test.ts— 4 tests, all passing with a realORCAROUTER_API_KEY; it lists the live catalog through the project's own discovery path and completes a real chat completion through the project's own transport (not a standalone curl).node scripts/orca-verify.mjs check-types|lint|version— clean, and covered byscripts/repo-gates.test.mjsso the same CI gates run inside the test suite.Both adapters are tested independently and converge: API-key save/read/clear/redaction; verifier/challenge/state generation, authorize URL, the exact exchange path and body, successful persistence, denial, Flow A state mismatch, code reuse and expiry, scope downgrade, corrupt-key terminal classification, exact-account 401 reconnect, 429, and network failure. Fixtures use fake keys and codes only, and the tests assert that no verifier or key appears in logs or errors. GUI lifecycle is covered too: success, denial, exchange error, timeout, explicit cancel, switching auth method, closing the panel, unmount, reload, and
pagehideall release the login lock, with a monotonic attempt/generation guard so a stale response cannot overwrite a newer login; a test proves a second login can start afterpagehidewithout a remount.git grepaudit on this head: no client secret, no fixed verifier, no realsk-orca-key outside fake test fixtures, and no implementation call to/v1/auth/keys.UI
Real screenshots from the running app (
apps/webdev server, Chromium via Playwright), produced byscripts/orca-ui-evidence.py(run throughnode scripts/orca-verify.mjs ui-evidence). That script is committed; theorca-evidence/directory it writes is generated output and gitignored.manifest.jsonrecords automationplaywright,passed: true, catalog sourcehttps://api.orcarouter.ai/v1/models?capability=chat, 16 chat models, 2 image-input chat models.Both auth choices, side by side, secret masked
Text model dropdown, populated from the live chat catalog (16 items)
Multimodal dropdown after attaching an image — only the 2 chat models that declare image input remain, and the previously selected text-only model was cleared
Provider evidence
https://api.orcarouter.ai/v1—POST /v1/chat/completionswithAuthorization: Bearer sk-orca-…; documented athttps://docs.orcarouter.ai/getting-started/get-api-keyandhttps://docs.orcarouter.ai/compatibility/anthropic-sdk.GET https://api.orcarouter.ai/v1/models(requires the Bearer key) —https://docs.orcarouter.ai/getting-started/models.https://www.orcarouter.ai/authandPOST https://www.orcarouter.ai/api/v1/auth/keys—https://docs.orcarouter.ai/getting-started/sign-in-with-orcarouter. The discovery documenthttps://www.orcarouter.ai/.well-known/openid-configurationadvertisescode_challenge_methods_supported: ["S256","plain"]andtoken_endpoint_auth_methods_supported: ["none"](no client secret), and itstoken_endpointis on thewwworigin, which confirms the auth/inference origin split used here.https://www.orcarouter.ai/console/authorized-apps(revoking the app deletes every key issued to it).https://www.orcarouter.ai/terms.htmlandhttps://www.orcarouter.ai/privacy.html— the operating entity is CONTINUUM AI PTE. LTD.https://docs.orcarouter.ai/routing/routing-dslandhttps://docs.orcarouter.ai/routing/model-fallbacks, andhttps://docs.orcarouter.ai/integrations/overviewstates the drop-in OpenAI-compatible endpoint used here.https://docs.orcarouter.ai,https://www.orcarouter.ai); this integration is maintained alongside the upstream repository.Notes for reviewers
MAINTAINERS/CODEOWNERSfile and no sponsorship label, and no readiness checklist is present in the repo. The maintainer with the most commits onmain(Chris Tate <chris@ctate.dev>, who also owns the release process described inAGENTS.md) is the right reviewer for the credential boundary.examples/*.Affiliation
Made on behalf of OrcaRouter by an engineer on the OrcaRouter team, in coordination with the json-render maintainers. No unrelated behavior was changed.