Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ AUTH_COOKIE_SECURE=false
REQUIRE_API_KEY=false

# Allow revealing full API key values in the Dashboard UI.
# Used by: Dashboard providers page — controls show/hide of key values.
# Used by: src/shared/constants/featureFlagDefinitions.ts — controls show/hide of key values.
# Also configurable from Dashboard > Settings > Feature Flags.
# Default: false | Security risk if enabled on shared instances.
ALLOW_API_KEY_REVEAL=false

Expand Down
6 changes: 5 additions & 1 deletion docs/guides/USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ Models:

### 🆓 FREE Providers

No-auth free providers have a switch beside **No authentication required** on their provider page.
Turning it off disables that provider, removes it from Providers configured/compact views, and
removes its models from `/v1/models`.

#### Qoder (8 FREE models)

```bash
Expand Down Expand Up @@ -560,7 +564,7 @@ post_install() {
| `NEXT_PUBLIC_CLOUD_URL` | `https://omniroute.dev` | Cloud sync endpoint base URL (replaces legacy `CLOUD_URL`) |
| `API_KEY_SECRET` | `endpoint-proxy-api-key-secret` | HMAC secret for generated API keys |
| `REQUIRE_API_KEY` | `false` | Enforce Bearer API key on `/v1/*` |
| `ALLOW_API_KEY_REVEAL` | `false` | Allow Api Manager to copy full API keys on demand |
| `ALLOW_API_KEY_REVEAL` | `false` | Allow authenticated dashboard users to reveal full stored API key values on demand |
| `PROVIDER_LIMITS_SYNC_INTERVAL_MINUTES` | `70` | Server-side refresh cadence for cached Provider Limits data; UI refresh buttons still trigger manual sync |
| `DISABLE_SQLITE_AUTO_BACKUP` | `false` | Disable automatic SQLite snapshots before writes/import/restore; manual backups still work |
| `APP_LOG_TO_FILE` | `true` | Enables application and audit log output to disk |
Expand Down
26 changes: 13 additions & 13 deletions docs/reference/ENVIRONMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,19 +166,19 @@ OmniRoute uses **SQLite** (via `better-sqlite3`) for all persistence. These vari

## 4. Security & Authentication

| Variable | Default | Source File | Description |
| --------------------------------------- | ----------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MACHINE_ID_SALT` | `endpoint-proxy-salt` | `src/lib/auth` | Salt combined with hardware identifiers for machine fingerprinting. Change per-deployment for isolation. |
| `OMNIROUTE_CLI_SALT` | `omniroute-cli-auth-v1` | `src/lib/machineToken.ts` | HMAC salt for deriving the local CLI auth token. Changing this value rotates all CLI tokens on the machine. See `docs/security/CLI_TOKEN.md`. |
| `AUTH_COOKIE_SECURE` | `false` | `src/lib/auth` | Sets the `Secure` flag on session cookies. **Must be `true`** when running behind HTTPS. |
| `REQUIRE_API_KEY` | `false` | API middleware | When `true`, all `/v1/*` proxy requests must include a valid API key. |
| `ALLOW_API_KEY_REVEAL` | `false` | Dashboard providers page | Allows revealing full API key values in the Dashboard UI. Security risk on shared instances. |
| `NO_LOG_API_KEY_IDS` | _(empty)_ | `src/lib/compliance/index.ts` | Comma-separated API key IDs that bypass request logging (GDPR compliance). |
| `DEFAULT_RATE_LIMIT_PER_DAY` | `1000` | `src/shared/utils/apiKeyPolicy.ts` | Fallback per-day request budget applied to API keys whose `rate_limits` column is null. Default (unset/empty/malformed) keeps the legacy 1000/day, 5000/week, 20000/month windows. Set explicitly to `0` to opt out (unlimited). Any positive integer N enables N/day, 5N/week, 20N/month. Zod-validated; invalid values log a warning and use the legacy default. |
| `MAX_BODY_SIZE_BYTES` | `10485760` (10 MB) | `src/shared/middleware/bodySizeGuard.ts` | Maximum allowed request body size. Rejects payloads exceeding this limit. |
| `CORS_ORIGIN` | `*` | Next.js middleware | CORS `Access-Control-Allow-Origin` value. Restrict for production. |
| `OUTBOUND_SSRF_GUARD_ENABLED` | `true` | `src/shared/network/outboundUrlGuard.ts` | Block provider calls targeting private/loopback/link-local IP ranges. Disable only in isolated test envs. |
| `OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS` | `false` | `src/shared/network/outboundUrlGuard.ts` | Allow provider URLs pointing to private/local networks (localhost, 192.168.x.x, 10.x.x.x, etc.). **REQUIRED for self-hosted providers** (LM Studio, Ollama, vLLM, Llamafile, Triton, SearXNG). When `false`, the dashboard rejects validation of local URLs. |
| Variable | Default | Source File | Description |
| --------------------------------------- | ----------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MACHINE_ID_SALT` | `endpoint-proxy-salt` | `src/lib/auth` | Salt combined with hardware identifiers for machine fingerprinting. Change per-deployment for isolation. |
| `OMNIROUTE_CLI_SALT` | `omniroute-cli-auth-v1` | `src/lib/machineToken.ts` | HMAC salt for deriving the local CLI auth token. Changing this value rotates all CLI tokens on the machine. See `docs/security/CLI_TOKEN.md`. |
| `AUTH_COOKIE_SECURE` | `false` | `src/lib/auth` | Sets the `Secure` flag on session cookies. **Must be `true`** when running behind HTTPS. |
| `REQUIRE_API_KEY` | `false` | API middleware | When `true`, all `/v1/*` proxy requests must include a valid API key. |
| `ALLOW_API_KEY_REVEAL` | `false` | `src/shared/constants/featureFlagDefinitions.ts` | Allows revealing full API key values in the Dashboard UI. Configurable from Dashboard Feature Flags; security risk on shared instances. |
| `NO_LOG_API_KEY_IDS` | _(empty)_ | `src/lib/compliance/index.ts` | Comma-separated API key IDs that bypass request logging (GDPR compliance). |
| `DEFAULT_RATE_LIMIT_PER_DAY` | `1000` | `src/shared/utils/apiKeyPolicy.ts` | Fallback per-day request budget applied to API keys whose `rate_limits` column is null. Default (unset/empty/malformed) keeps the legacy 1000/day, 5000/week, 20000/month windows. Set explicitly to `0` to opt out (unlimited). Any positive integer N enables N/day, 5N/week, 20N/month. Zod-validated; invalid values log a warning and use the legacy default. |
| `MAX_BODY_SIZE_BYTES` | `10485760` (10 MB) | `src/shared/middleware/bodySizeGuard.ts` | Maximum allowed request body size. Rejects payloads exceeding this limit. |
| `CORS_ORIGIN` | `*` | Next.js middleware | CORS `Access-Control-Allow-Origin` value. Restrict for production. |
| `OUTBOUND_SSRF_GUARD_ENABLED` | `true` | `src/shared/network/outboundUrlGuard.ts` | Block provider calls targeting private/loopback/link-local IP ranges. Disable only in isolated test envs. |
| `OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS` | `false` | `src/shared/network/outboundUrlGuard.ts` | Allow provider URLs pointing to private/local networks (localhost, 192.168.x.x, 10.x.x.x, etc.). **REQUIRED for self-hosted providers** (LM Studio, Ollama, vLLM, Llamafile, Triton, SearXNG). When `false`, the dashboard rejects validation of local URLs. |

### Hardening Checklist

Expand Down
21 changes: 18 additions & 3 deletions open-sse/services/autoCombo/virtualFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { MODE_PACKS } from "./modePacks";
import { DEFAULT_WEIGHTS, ScoringWeights } from "./scoring";
import { AutoVariant } from "./autoPrefix";
import { getProviderConnections } from "@/lib/db/providers";
import { getSettings } from "@/lib/db/settings";
import { getProviderRegistry } from "./providerRegistryAccessor";
import type { ConnectionFields } from "@/lib/db/encryption";
import { NOAUTH_PROVIDERS } from "@/shared/constants/providers";
Expand Down Expand Up @@ -123,7 +124,10 @@ function getFirstRegistryModelId(providerInfo: { models?: Array<{ id?: string }>
: undefined;
}

function getNoAuthCandidates(excludedProviders: Set<string>): VirtualAutoComboCandidate[] {
function getNoAuthCandidates(
excludedProviders: Set<string>,
blockedProviders: Set<string>
): VirtualAutoComboCandidate[] {
const registry = getProviderRegistry();
const candidates: VirtualAutoComboCandidate[] = [];

Expand All @@ -132,6 +136,11 @@ function getNoAuthCandidates(excludedProviders: Set<string>): VirtualAutoComboCa

const providerId = providerDef.id;
if (!providerId || excludedProviders.has(providerId)) continue;
if (
blockedProviders.has(providerId) ||
(typeof providerDef.alias === "string" && blockedProviders.has(providerDef.alias))
)
continue;

const providerInfo = registry[providerId];
const modelId = getFirstRegistryModelId(providerInfo);
Expand Down Expand Up @@ -206,7 +215,13 @@ export function computeAdvertisedLimits(candidates: Array<{ provider: string; mo
export async function createVirtualAutoCombo(
variant: AutoVariant | undefined
): Promise<VirtualAutoCombo> {
const connections = (await getProviderConnections({ isActive: true })) as VirtualFactoryConn[];
const [connections, settings] = await Promise.all([
getProviderConnections({ isActive: true }) as Promise<VirtualFactoryConn[]>,
getSettings().catch(() => ({}) as Record<string, unknown>),
]);
const blockedProviders = new Set(
Array.isArray(settings.blockedProviders) ? (settings.blockedProviders as string[]) : []
);

const validConnections = connections.filter(hasUsableConnectionCredential);

Expand All @@ -232,7 +247,7 @@ export async function createVirtualAutoCombo(
}

candidatePool.push(
...getNoAuthCandidates(new Set(validConnections.map((conn) => conn.provider)))
...getNoAuthCandidates(new Set(validConnections.map((conn) => conn.provider)), blockedProviders)
);

if (candidatePool.length === 0) {
Expand Down
Loading