npm.io
0.4.0 • Published 2d ago

@lore-hex/trusted-router

Licence
Apache-2.0
Version
0.4.0
Deps
0
Size
105 kB
Vulns
0
Weekly
0

TrustedRouter JavaScript SDK

npm version npm downloads CI TypeScript types License: Apache-2.0 Verifiable trust

OpenAI-compatible JS/TS client for TrustedRouter — the hosted, attested LLM router that lets you point one OpenAI-shaped client at every provider (Anthropic, OpenAI, Google Vertex, Gemini, DeepSeek, Mistral, Cerebras) and prove the prompt path doesn't log.

  • Inference API: https://api.trustedrouter.com/v1
  • Control API: https://trustedrouter.com/v1
  • Trust release: https://trust.trustedrouter.com
  • Source: https://github.com/Lore-Hex/trusted-router-js
  • License: Apache-2.0
npm install @lore-hex/trusted-router

Runs on Node 20+, Deno, Bun, and modern browsers — no native deps. The attestation verifier uses the WebCrypto SubtleCrypto API.

Quick start

import { TrustedRouter, AUTO_MODEL } from "@lore-hex/trusted-router";

const client = new TrustedRouter({ apiKey: "sk-tr-v1-..." });

const resp = await client.chatCompletions({
  model: AUTO_MODEL,                  // "trustedrouter/auto" — multi-provider failover
  messages: [{ role: "user", content: "hello" }],
});

console.log(resp.choices[0].message.content);

chatCompletions(...) defaults to AUTO_MODEL when model is omitted.

Node TLS session verification

G6 TLS session pinning uses Node TLS sockets, so import it from the Node-only session subpath rather than the browser-safe package root:

import { verifyGatewaySession } from "@lore-hex/trusted-router/session";

Fusion

Fan a request across a panel of models and let a judge model pick or synthesize one answer. fusion(...) returns the same OpenAI-shape chat.completion as chatCompletions. FUSION_FREEDOM_PANEL / FUSION_FREEDOM_FALLBACK_JUDGES are the recommended most-permissive configuration.

import {
  TrustedRouter,
  FUSION_FREEDOM_PANEL,
  FUSION_FREEDOM_FALLBACK_JUDGES,
} from "@lore-hex/trusted-router";

const client = new TrustedRouter({ apiKey: "sk-tr-v1-..." });

const resp = await client.fusion({
  messages: [{ role: "user", content: "explain how mRNA vaccines work" }],
  analysisModels: FUSION_FREEDOM_PANEL,   // the panel
  // omit selectionStrategy to use synthesize_non_refusals
  fallbackJudges: FUSION_FREEDOM_FALLBACK_JUDGES, // tried in order if a judge refuses/fails
});

console.log(resp.choices[0].message.content);

Or attach fusionTool(...) to any chat call yourself. preset: "quality" or "budget" picks a built-in panel.

Browser sign-in / delegated keys

Browser apps should not ask users to paste a full TrustedRouter key. Use the OAuth/PKCE delegation flow to mint a limited inference key for your app, then store that delegated key in browser storage.

import { TrustedRouter } from "@lore-hex/trusted-router";

const tr = new TrustedRouter();

// Sign-in button handler.
const auth = await tr.createOAuthAuthorization({
  callbackUrl: `${location.origin}/auth/callback`,
  keyLabel: "Lore Web",
  limit: "5",
  usageLimitType: "monthly",
});

sessionStorage.setItem("tr_oauth", JSON.stringify({
  state: auth.state,
  codeVerifier: auth.codeVerifier,
}));
location.assign(auth.url);

On the callback page:

const params = new URLSearchParams(location.search);
const saved = JSON.parse(sessionStorage.getItem("tr_oauth") || "{}");
if (params.get("state") !== saved.state) throw new Error("OAuth state mismatch");

const { key } = await new TrustedRouter().exchangeOAuthKey({
  code: params.get("code"),
  codeVerifier: saved.codeVerifier,
});

localStorage.setItem("tr_delegated_key", key);

createOAuthAuthorization(...) generates an RFC7636 S256 PKCE verifier and challenge locally. exchangeOAuthKey(...) posts only the one-time code and verifier, and deliberately omits any existing bearer key.

Sign in with TrustedRouter

For browser SPAs, BrowserOAuthFlow (from @lore-hex/trusted-router/oauth) wraps the lower-level helpers above: initiate(...) builds the authorize URL and stashes { state, codeVerifier } in sessionStorage, and handleCallback(...) validates state, exchanges the code, and returns the delegated key + verified identity. Then client.userInfo() reads the signed-in user.

import { TrustedRouter } from "@lore-hex/trusted-router";
import { BrowserOAuthFlow } from "@lore-hex/trusted-router/oauth";

const flow = new BrowserOAuthFlow(`${location.origin}/auth/callback`, {
  client: new TrustedRouter(),
});

// sign-in button:
const { url } = await flow.initiate({ keyLabel: "My App", limit: "5" });
location.assign(url);

// on /auth/callback (reads location.search; throws on state mismatch):
const { key, identity } = await flow.handleCallback();
localStorage.setItem("tr_delegated_key", key);

// later:
const { data } = await new TrustedRouter({ apiKey: key }).userInfo();

Full flow, endpoints, and security notes: Sign in with TrustedRouter.

Streaming

for await (const token of client.chatCompletionsText({
  messages: [{ role: "user", content: "Write a haiku" }],
})) {
  process.stdout.write(token);
}

chatCompletionsChunks(...) yields the raw OpenAI chat.completion.chunk objects (with finish_reason, model, id) when you need more than just the text delta. chatCompletionsRawStream(...) yields the underlying SSE bytes — useful if you're writing an HTTP relay that doesn't want to parse.

Inference endpoint and failover

Inference calls default to DEFAULT_API_BASE_URL, the global api.trustedrouter.com load balancer. Regional failover re-requests that apex; the load balancer handles healthy-region selection server-side. Pass baseUrl only for a custom inference endpoint (e.g. a self-hosted gateway). Metadata, OAuth, billing, credits, activity, and broadcast helpers use the control plane at DEFAULT_CONTROL_BASE_URL; override it with controlBaseUrl only when you need a custom control endpoint.

Typed errors

Every HTTP failure throws a typed subclass of TrustedRouterError so callers can discriminate without inspecting status codes:

import {
  TrustedRouter, RateLimitError, AuthenticationError,
  BadRequestError, EndpointNotSupportedError, InternalError,
} from "@lore-hex/trusted-router";

try {
  await client.chatCompletions({ messages: [...] });
} catch (err) {
  if (err instanceof RateLimitError) {
    await sleep((err.retryAfter ?? 5) * 1000);
  } else if (err instanceof AuthenticationError) {
    refreshKey();
  } else if (err instanceof BadRequestError) {
    console.warn("bad request:", err.message);
  } else if (err instanceof EndpointNotSupportedError) {
    disableOptionalFeature();
  } else if (err instanceof InternalError) {
    // auto-retried; still failing
  } else {
    throw err;
  }
}

All subclasses inherit TrustedRouterError.

Automatic retries

By default the client retries 429 and 5xx responses up to 2 times with exponential backoff + jitter (capped at 30s, honors Retry-After). Disable with maxRetries: 0:

const client = new TrustedRouter({ apiKey: "...", maxRetries: 0 });

Regional failover applies only to inference routes and re-requests the apex. Control-plane calls retry on the configured control host.

Per-call extras

Every chat method (and request() for ad-hoc paths) accepts:

Option Purpose
apiKey Override the instance bearer for this call only (threadsafe)
extraHeaders Object of headers to merge in (trace IDs, custom routing)
workspaceId Sets X-TrustedRouter-Workspace for workspace-scoped management calls
idempotencyKey Adds Idempotency-Key: so the gateway dedupes retries — strongly recommended for billing
timeout Per-call timeout in milliseconds (uses AbortController)
await client.billingCheckout({
  amount: 25,
  paymentMethod: "stablecoin",
  idempotencyKey: `checkout-${userId}-${orderId}`, // never double-charge
});

Attestation verification (the differentiator)

Every TrustedRouter response is generated inside a Google Confidential Space workload. The gateway's /attestation endpoint mints a Google-signed JWT that commits to the workload image digest, image reference, your nonce, and the TLS leaf cert SHA-256. Verifying it proves the prompt path you're about to use is the exact build the trust page advertises:

import { TrustedRouter } from "@lore-hex/trusted-router";
import {
  verifyGatewayAttestation, policyFromTrustRelease,
} from "@lore-hex/trusted-router/attestation";

const client = new TrustedRouter({ apiKey: "sk-tr-v1-..." });
const policy = await policyFromTrustRelease();   // pulls live trust release

const nonce = crypto.randomUUID().replace(/-/g, "");
const jwt = await client.attestation();           // raw JWT bytes (Uint8Array)

const attestation = await verifyGatewayAttestation(jwt, {
  policy,
  nonceHex: nonce,
  // Optionally pass the live TLS cert DER bytes for extra binding.
});

console.log("verified gateway:", attestation.imageDigest);

verifyGatewayAttestation() throws AttestationVerificationError on any failure — bad signature, expired JWT, wrong issuer, audience mismatch, image_digest mismatch, image_reference mismatch, missing nonce echo, or TLS cert mismatch. Never returns falsey for a failed verification.

Bring your own fetch

Pass fetchImpl for custom transports (proxies, retries you manage, observability hooks):

const client = new TrustedRouter({
  apiKey: "...",
  fetchImpl: async (url, init) => myInstrumentedFetch(url, init),
});

Other endpoints

client.models();             // OpenAI-shape catalog via the control plane
client.providers();          // provider list via the control plane
client.regions();            // deployed regions via the control plane
client.credits({ workspaceId: "ws_..." }); // prepaid balance via the control plane
client.activity({ since: "2026-01-01", limit: 50 }); // control-plane activity
client.messages({            // Anthropic shape, preserves system + content blocks
  model: "anthropic/claude-3-5-sonnet",
  messages: [{ role: "user", content: "hi" }],
  maxTokens: 512,
});
client.billingCheckout({ amount: 25, paymentMethod: "stablecoin", idempotencyKey: "..." });

client.embeddings(...) uses the attested inference plane. Embedding model catalog routes such as /embeddings/models are control-plane metadata.

For routes the SDK doesn't wrap, drop down to client.request(...):

await client.request("GET", "/some/new/route", {
  extraHeaders: { "x-trace": "abc" },
});

Contributing

npm install
npm run check        # syntax check
npm test             # node --test

CI runs lint + tests on every push to main and PR.

Keywords