Repository navigation
Add OrcaRouter as a first-class named model provider with API Key and OAuth 2.0 + PKCE - #548
Open
flenordchere264-crypto wants to merge 2 commits into
Open
flenordchere264-crypto wants to merge 2 commits into
flenordchere264-crypto wants to merge 2 commits into
Conversation
… OAuth 2.0 + PKCE Signed-off-by: flenordchere264-crypto <flenordchere264-crypto@users.noreply.github.com>
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
OpenAgentCore already lets an operator point a Harness at any OpenAI-compatible endpoint, but nothing names OrcaRouter and there is no in-product way to obtain its credential. This change adds OrcaRouter as a first-class named provider on both operator surfaces, with two independent credential entrances that produce the same ordinary OrcaRouter API key.
sk-orca-…key; it is written to Core as the Harness's write-only provider key exactly as before.state, and one code per attempt.https://www.orcarouter.ai(authorize at the fixed/auth; exchange at the fixed/api/v1/auth/keys).https://api.orcarouter.ai/v1.ORCA_BASE_URL; separateORCA_AUTH_BASE_URL/ORCA_API_BASE_URLwin over it (remote origins require HTTPS, HTTP only for loopback).401-> exact-account reauth: a rejected key is a terminal reauthentication requirement for the exact credential generation that made the request. There is no refresh grant and none is invented; a late failure from a superseded generation cannot mark a newly reauthorized credential.createCredentialAttempts(console) andcancelLogin/closeAll(example) release the busy state and any listener on success, denial, exchange error, timeout (10 min), explicit cancel,pagehide, unmount and reload, guarded by an increasing generation.GET /v1/modelson the configured API origin, bounded server-side at 512 KiB and 2000 records, reduced toid,name,context_length,max_completion_tokens,supported_endpoint_types,input_modalities.openai/gpt-5.5withreasoningEfforts: [low, medium, high, xhigh].Covered AI input entrances
apps/web+services/web)example/parsar)The console and the example are the only places where a person chooses a model provider. Core has no provider registry: the Harness<->model-provider boundary is
internal/modelprovider/config.go, andAGENTS.mdforbids a vendor-only Core setting, so a vendor must reach the system as a named entry in the surface that already collects a provider, not as a new Core field. Both surfaces send their catalog requests to their own server (the console toservices/web/orcarouter.go, the example toserver/orcarouter.mjs), so the operator's key never reaches the browser and never appears in a URL.Authentication and inference are different origins
The exchange is on the auth origin at
/api/v1/auth/keys; inference and the catalog are on the API origin under/v1.https://api.orcarouter.ai/v1/auth/keysis not the exchange and is never used; the code never derives one origin from the other./console/orcarouter/configreports both origins so the browser composes the authorize URL and the inference base instead of hardcoding a public origin.Credential seam
One small interface (
orcaRouterAPI/ the console's credential adapters) has two adapters: pasted API key, and PKCE. Provider requests, model discovery and every Model entrance read the key through that seam and cannot tell which adapter produced it. A PKCE-issued key is durable and is not a refresh token; there is no refresh endpoint.Flow choice
OAC_PUBLIC_URLdiffers per deployment, so no loopback redirect can be pre-registered and the console server cannot open a listener on the operator's machine. The operator pastes the one-time code.example/parsaris a local single-user server on127.0.0.1that can open a listener, so the code returns to the process holding the verifier and nothing is copied.state. Not implemented: Flow C device grant (optional; it cannot replace PKCE).Credential persistence
chmod 0600, never returned to the browser; a 401 marks the exactgenerationasneeds_reauth. Reloading reuses the stored key until it is revoked; the app never re-authorizes on every start.Model control and capability filtering
When OrcaRouter is selected the model control becomes a searchable dropdown built from the real
GET /v1/modelscatalog for the operator's own workspace; free-text model entry is not offered. Each entrance filters independently and fails closed:?capability=chat, andsupported_endpoint_typesmust contain at least one ofopenai,anthropic,gemini,openai-response; image-generation, openai-video, jina-rerank and embeddings-only records are excluded;architecture.input_modalitiesmust explicitly declare the uploaded modality (a model that declares none is excluded, never guessed in);?capability=embedding/embeddings; image:?capability=image/image-generation; video:openai-video; rerank:jina-rerank.Changing the provider, the modality requirement or the attachment/task type recomputes the options list, and a stored model that is no longer compatible is cleared with a prompt rather than kept. Live discovery is authoritative; on failure the selector shows the small verified seed catalog labelled as a fallback, never free text and never a hand-written list pretending to be the live catalog.
Evidence and verification
https://www.orcarouter.ai/.well-known/openid-configurationdocumentsauthorization_endpoint = https://www.orcarouter.ai/auth,token_endpoint = https://www.orcarouter.ai/api/v1/auth/keys,grant_types_supported = [authorization_code],code_challenge_methods_supported = [S256, plain]andtoken_endpoint_auth_methods_supported = [none](no client secret). Inference is OpenAI-compatible atPOST https://api.orcarouter.ai/v1/chat/completions(401 without a credential); the model list isGET https://api.orcarouter.ai/v1/models(200, public metadata) and?capability=narrows it with the operator's key. Key revocation and account management:https://www.orcarouter.ai/console/authorized-apps(200).https://www.orcarouter.ai/terms,/legaland/privacyreturn 404 and no operating legal entity is published on the public pages I could reach. Stated rather than asserted.team@orcarouter.ai). Evidence verification date: 2026-10-08.apps/webOrcaRouter units 56 passed / 0 failed (4 files:orcarouter.test.ts19,orcarouter-credential.test.ts25,orcarouter-attempts.test.ts6,orcarouter-catalog.test.ts6);services/webGo OrcaRouter tests 11 passed / 0 failed / 0 skipped;example/parsarOrcaRouter units 38 passed / 0 failed (orcarouter.test.mjs17,catalog.test.mjs10,product.test.mjs11).apps/webfull unit suite 438 passed / 0 failed (72 files) with a cleantsc --noEmitand a successfulvite build;apps/webPlaywright acceptance 83 passed / 0 failed including the 3 OrcaRouter specs;example/parsartypecheck clean, package suite 46 passed / 1 skipped (the pre-existing live-credential skip) and build ok;make check-names,make check-docsandmake check-cipass;websitetranslation gate 3 passed / 0 failed.GET /console/orcarouter/catalog?capability=chatwith 16 records, 16 text (0 non-text models offered to a text selector) from the real gateway, and echoed no key;example/parsar's ownorcaRouterAPI.catalog()returned 16 chat models and 2 with a declared image input. No live chat completion was issued.orca-evidence/auth-methods.pngandorca-evidence/text-model-dropdown.png, rendered byapps/web/e2e/orcarouter-evidence.spec.tsthrough the shipped acceptance fixture, withorca-evidence/manifest.jsonrecording the sha256 of each file and theapi_key_visible/pkce_visible/secret_masked/controls_enabledand dropdown assertions.Multimodal
Not applicable to a screenshot. The console's model control binds a text chat model for a Harness and there is no attachment/image/audio/video upload entrance that reaches a provider selection, so no multimodal dropdown can be shown. The fail-closed multimodal filter is still implemented and unit-tested.
Known limitations of this run
make checkwas not run as a whole: the full gate needs PostgreSQL, which this environment does not provide. The affected jobs were run individually (check-names,check-docs,check-ci,check-web-unit,check-web-acceptance,check-example).make check-harness-catalogwas not run; this change does not touchinternal/harnessconfig/builtin/catalog.jsonor its generators.apps/webacceptance suite ran on the packaged Chromium viaapps/web/playwright.config.ts;services/webwas tested with the Go toolchain available here.This integration targets OrcaRouter. OrcaRouter is an OpenAI-compatible AI gateway that routes many providers behind one endpoint. I'm an engineer on the OrcaRouter team.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.