Skip to content

feat: add OrcaRouter as a first-class provider (API key + OAuth 2.0 PKCE) - #266

Open
hodeswildsmith455-boop wants to merge 1 commit into
caigee-cmd:mainfrom
hodeswildsmith455-boop:orcarouter/task-8071
Open

hodeswildsmith455-boop wants to merge 1 commit into
caigee-cmd:mainfrom
hodeswildsmith455-boop:orcarouter/task-8071

Conversation

@hodeswildsmith455-boop

Copy link
Copy Markdown

Summary

Adds OrcaRouter as a first-class provider in internal/providers/orcarouter (registered in internal/providers/registry.go) together with a real console surface, so an operator can either paste an existing sk-orca-… key or connect an OrcaRouter account through OAuth 2.0 + PKCE. The model selector is driven by the live OrcaRouter model catalog with per-entry capability filtering, replacing free-text model entry for this provider.

  • Inference/catalog base: https://api.orcarouter.ai/v1 (OpenAI-compatible)
  • Auth base: https://www.orcarouter.ai — consent at /auth, code exchange at /api/v1/auth/keys
  • The two public origins are separate constants and are never derived from one another (https://api.orcarouter.ai/v1/auth/keys is a 404 and is asserted against in tests).

Two authentication entries, one credential seam

Provider id Label Credential source
orcarouter OrcaRouter – API Operator pastes an sk-orca-… key
orcarouter-oauth OrcaRouter – Auth Browser consent issues the same kind of key

Both entries run through a single credential interface (internal/providers/orcarouter/credential.go); the format discriminator records which entry minted the key, and nothing downstream — provider requests or model discovery — branches on it. The key is stored with the project's existing account-secret mechanism and is masked/clearable from the console.

Connect flow: OAuth 2.0 + PKCE, Flow A (loopback redirect)

Flow A is used because this host is self-hosted software that can bind a loopback listener and therefore needs no pre-registered redirect URI; the consent screen redirects straight back to http://127.0.0.1:<port>/cb. Flow B (out-of-band code) remains reachable as the fallback path (CompleteLogin accepts a pasted callback URL or a bare code) for hosts that cannot receive a redirect.

  • Verifier and state are generated fresh per attempt from crypto/rand; only the unpadded base64url(sha256(verifier)) challenge travels on the authorize URL, with code_challenge_method=S256.
  • The state echoed on the redirect is compared in constant time before the code is used.
  • Denial, state mismatch, expiry/reuse, 403, 400 and 429 all terminate the attempt safely with an actionable message; the response body is passed through the redactor so no credential or verifier can reach a log or an error string.
  • The exchange response's granted scope is read back and recorded as-is; a narrower grant than requested is surfaced rather than assumed away.
  • The issued key is durable: it is reused until OrcaRouter revokes it. There is no refresh grant and none is invented. An upstream 401 marks only the exact account and credential generation that issued the rejected request as needing reauthentication, so a late failure cannot contaminate a freshly reauthorized credential.
  • Login cancellation is generation-guarded across success, denial, exchange error, cancel, provider switch and pagehide.

Model catalog and capability filtering

The selector is populated from the configured origin's GET /v1/models, fetched through the provider's own client (the console backend holds the key; the browser only ever receives minimal model metadata). The vendor/model namespace is preserved verbatim.

Per-entry filtering is applied on declared metadata, never on the model name:

  • text chat/agent: ?capability=chat, requiring a speakable endpoint type and excluding image-generation/video/rerank-only models;
  • multimodal understanding: chat plus a declared non-text input modality — models that do not declare it fail closed;
  • embedding, image generation, video and rerank each match their own capability/endpoint type.

A small verified cold-start seed remains for catalog outages (live success is authoritative and no seed is mixed in); a persisted model id is re-validated against the live compatible list before it is restored.

Test plan

Run on this commit (b92caa2 → 6b5f310), orchestrator binary built from a bootstrapped Go 1.27 toolchain:

  • make test (Go + worker) — all packages ok
  • make vet — clean
  • make test-architecture — ok
  • make changelog — bilingual fragment validates
  • npm --prefix frontend run build — ok
  • npm --prefix frontend run lint — 0 errors (18 pre-existing warnings)
  • make test-live — TestLiveCatalogThroughProvider and TestLiveChatThroughProvider PASS against the real gateway
  • make orca-evidence — Playwright captures produced, passed: true

Focused counts: internal/providers/orcarouter 41/41 pass (0 skip with a key present), plus TestFilterModelsByCapability / TestProviderModelEntry* in internal/control 12/12. The dual-auth seam is asserted directly: both adapters must yield the same credential result, and auth requests must only reach the auth origin while inference/catalog only reach the API origin.

Verification (independent, from the base commit)

The delivery verifier re-applied the patch to b92caa2 in a clean checkout and re-ran every check; each command below is a real test-runner invocation and covers provider, dual auth, catalog, capabilities, errors, regression and the live path. All checks exited 0, including the live provider run and the GUI evidence run. The GUI checks regenerate orca-evidence/ from the tree under test (gitignored): auth-methods.png shows the API-key field and the Connect-with-OrcaRouter entry side by side with the pasted secret masked, and text-model-dropdown.png / multimodal-model-dropdown.png show the catalog-backed dropdown before and after the image attachment re-queries the vision capability (16 chat models, 2 vision).

Personal verification of the round trip

A real login still requires human consent, so it is not automated here. The automated PKCE tests drive the project's own connect adapter against a local fake auth server — authorize → callback/OOB → exchange → persist → reuse — covering denial, state mismatch, expired/reused code, scope downgrade, 403/400/429 and network failure. Nothing was bypassed.


OrcaRouter is the gateway this provider targets. OrcaRouter is an OpenAI-compatible AI gateway that routes many providers behind one endpoint. I'm an engineer on the OrcaRouter team.

Primary sources for this integration, verified 2026-10-03: inference and catalog at https://api.orcarouter.ai/v1; OAuth 2.0 + PKCE authorization at https://www.orcarouter.ai/auth with the exchange documented at POST https://www.orcarouter.ai/api/v1/auth/keys; discovery at https://www.orcarouter.ai/.well-known/openid-configuration; credential revocation at https://www.orcarouter.ai/console/authorized-apps; terms of service and the operating legal entity published at https://www.orcarouter.ai. Maintenance owner for this provider: the OrcaRouter contributor who opened this PR.

…KCE)

Signed-off-by: hodeswildsmith455-boop <hodeswildsmith455-boop@users.noreply.github.com>
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