Skip to content

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
MiniMax-AI:mainfrom
flenordchere264-crypto:orcarouter/task-8402
Open

flenordchere264-crypto wants to merge 2 commits into
MiniMax-AI:mainfrom
flenordchere264-crypto:orcarouter/task-8402

Conversation

@flenordchere264-crypto

@flenordchere264-crypto flenordchere264-crypto commented Oct 8, 2026 •

Copy link
Copy Markdown

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.

  • Provider label: OrcaRouter (a named preset, not a free-form base URL).
  • API-key entrance: paste an sk-orca-… key; it is written to Core as the Harness's write-only provider key exactly as before.
  • PKCE entrance: Connect with OrcaRouter runs OAuth 2.0 with PKCE, S256, a fresh state, and one code per attempt.
  • Auth base: https://www.orcarouter.ai (authorize at the fixed /auth; exchange at the fixed /api/v1/auth/keys).
  • Inference base: https://api.orcarouter.ai/v1.
  • Shared self-hosted override: ORCA_BASE_URL; separate ORCA_AUTH_BASE_URL / ORCA_API_BASE_URL win 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.
  • Login lifecycle: createCredentialAttempts (console) and cancelLogin/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.
  • Live catalog limits and filters: GET /v1/models on the configured API origin, bounded server-side at 512 KiB and 2000 records, reduced to id, name, context_length, max_completion_tokens, supported_endpoint_types, input_modalities.
  • Verified fallback models and metadata: five chat entries plus embedding and image seeds, each with its context/output size and declared modalities, including openai/gpt-5.5 with reasoningEfforts: [low, medium, high, xhigh].

Covered AI input entrances

Surface Where Entrances wired
Console (apps/web + services/web) System -> Default model configuration -> Set/Replace for a Harness API Key and Connect with OrcaRouter, both feeding the same write-only provider key; model control is a searchable catalog dropdown
Example (example/parsar) Models -> add/edit a Provider API Key and Connect with OrcaRouter; discovery reads the workspace catalog

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, and AGENTS.md forbids 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 to services/web/orcarouter.go, the example to server/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/keys is not the exchange and is never used; the code never derives one origin from the other. /console/orcarouter/config reports 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

  • Console -> Flow B (out-of-band code). OAC_PUBLIC_URL differs 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 -> Flow A (loopback redirect). example/parsar is a local single-user server on 127.0.0.1 that can open a listener, so the code returns to the process holding the verifier and nothing is copied.
  • S256 is sent in both flows, and Flow B also verifies the returned state. Not implemented: Flow C device grant (optional; it cannot replace PKCE).

Credential persistence

  • Console: the issued or pasted key lives only in the dialog's form state and travels once in the Core write, exactly like any other provider key. The console never stores it and never returns it.
  • Example: the key is stored in the same local restricted SQLite file that already holds provider keys, chmod 0600, never returned to the browser; a 401 marks the exact generation as needs_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/models catalog for the operator's own workspace; free-text model entry is not offered. Each entrance filters independently and fails closed:

  • chat/agent: ?capability=chat, and supported_endpoint_types must contain at least one of openai, anthropic, gemini, openai-response; image-generation, openai-video, jina-rerank and embeddings-only records are excluded;
  • multimodal: chat first, then architecture.input_modalities must explicitly declare the uploaded modality (a model that declares none is excluded, never guessed in);
  • embedding: ?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

  • Primary endpoint documentation: https://www.orcarouter.ai/.well-known/openid-configuration documents authorization_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] and token_endpoint_auth_methods_supported = [none] (no client secret). Inference is OpenAI-compatible at POST https://api.orcarouter.ai/v1/chat/completions (401 without a credential); the model list is GET 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).
  • Terms / legal entity: not independently verifiable. https://www.orcarouter.ai/terms, /legal and /privacy return 404 and no operating legal entity is published on the public pages I could reach. Stated rather than asserted.
  • Aggregator routing or resale authorization: the public site documents routing, load balancing and failover across "200+ models" behind one OpenAI-compatible endpoint, but I found no public resale/redistribution authorization statement, so none is claimed.
  • Maintenance owner: the OrcaRouter team (team@orcarouter.ai). Evidence verification date: 2026-10-08.
  • Contributor affiliation disclosure: I'm an engineer on the OrcaRouter team.
  • Focused test result: apps/web OrcaRouter units 56 passed / 0 failed (4 files: orcarouter.test.ts 19, orcarouter-credential.test.ts 25, orcarouter-attempts.test.ts 6, orcarouter-catalog.test.ts 6); services/web Go OrcaRouter tests 11 passed / 0 failed / 0 skipped; example/parsar OrcaRouter units 38 passed / 0 failed (orcarouter.test.mjs 17, catalog.test.mjs 10, product.test.mjs 11).
  • Changed/full test result: apps/web full unit suite 438 passed / 0 failed (72 files) with a clean tsc --noEmit and a successful vite build; apps/web Playwright acceptance 83 passed / 0 failed including the 3 OrcaRouter specs; example/parsar typecheck clean, package suite 46 passed / 1 skipped (the pre-existing live-credential skip) and build ok; make check-names, make check-docs and make check-ci pass; website translation gate 3 passed / 0 failed.
  • Real login result: not run end to end. A real PKCE login needs a human to approve the consent screen, and that consent must not be fabricated. The automated tests drive the shipped adapters against a local fake auth server through authorize -> callback/OOB -> exchange -> persist for both flows.
  • Real inference result: live catalog read through the shipped provider path. The console's Go path answered GET /console/orcarouter/catalog?capability=chat with 16 records, 16 text (0 non-text models offered to a text selector) from the real gateway, and echoed no key; example/parsar's own orcaRouterAPI.catalog() returned 16 chat models and 2 with a declared image input. No live chat completion was issued.
  • Revocation / reauth result: covered by the local tests (a 401 marks the exact generation, a stale generation is ignored, no refresh is attempted); not exercised against a real revoked key.
  • GUI screenshot link: orca-evidence/auth-methods.png and orca-evidence/text-model-dropdown.png, rendered by apps/web/e2e/orcarouter-evidence.spec.ts through the shipped acceptance fixture, with orca-evidence/manifest.json recording the sha256 of each file and the api_key_visible / pkce_visible / secret_masked / controls_enabled and dropdown assertions.

Both OrcaRouter credential entrances
The real catalog model dropdown

  • Unresolved review threads: none yet (no PR exists at preparation time).
  • Remaining maintainer-only gate: any sponsorship or security label the repository requires for a new credential surface. Please review the exact head commit; I will not self-approve or add a sponsor label.

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 check was 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-catalog was not run; this change does not touch internal/harnessconfig/builtin/catalog.json or its generators.
  • The apps/web acceptance suite ran on the packaged Chromium via apps/web/playwright.config.ts; services/web was 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.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

flenordchere264-crypto and others added 2 commits October 8, 2026 06:28
… OAuth 2.0 + PKCE

Signed-off-by: flenordchere264-crypto <flenordchere264-crypto@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant