mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
This reverts commit e61b75f007.
This commit is contained in:
@@ -71,7 +71,6 @@ Lookup material — API surface, environment variables, CLI flags, provider cata
|
||||
|
||||
- [API_REFERENCE.md](reference/API_REFERENCE.md) — REST API endpoints and shapes.
|
||||
- [PROVIDER_REFERENCE.md](reference/PROVIDER_REFERENCE.md) — auto-generated provider catalog (do not edit by hand).
|
||||
- [PROVIDER_PLUGIN_MANIFEST.md](reference/PROVIDER_PLUGIN_MANIFEST.md) — sidecar-safe provider plugin contract for Bifrost and CLIProxyAPI migration.
|
||||
- [openapi.yaml](openapi.yaml) — OpenAPI spec for the public API.
|
||||
- [ENVIRONMENT.md](reference/ENVIRONMENT.md) — environment variables reference.
|
||||
- [FEATURE_FLAGS.md](reference/FEATURE_FLAGS.md) — feature flags and their defaults.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "Extending the Compression Pipeline"
|
||||
version: 3.8.44
|
||||
lastUpdated: 2026-07-02
|
||||
version: 3.8.40
|
||||
lastUpdated: 2026-06-28
|
||||
---
|
||||
|
||||
# Extending the Compression Pipeline
|
||||
@@ -512,62 +512,6 @@ To drive it from config, set `mode: "stacked"` and provide the step array under
|
||||
|
||||
---
|
||||
|
||||
## Upstream Sync Policy
|
||||
|
||||
OmniRoute's compression engines credit several upstream projects in the README
|
||||
("inspired by RTK, Caveman, LLMLingua-2, Troglodita"). A common contributor
|
||||
question is: **when upstream RTK adds a new tool filter or Caveman adds a rule
|
||||
pack, how does that reach OmniRoute?** This section is the authoritative answer.
|
||||
|
||||
### Vendored copies vs. independent implementations
|
||||
|
||||
| Engine | Relationship to upstream | Location |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
||||
| **RTK** | **Independent reimplementation** (inspired-by, not a copy) | `open-sse/services/compression/engines/rtk/` |
|
||||
| **Caveman** | **Independent reimplementation** (inspired-by) | `open-sse/services/compression/engines/cavemanAdapter.ts` |
|
||||
| **Headroom** | Mostly internal; only the `gcf/` codec is **genuinely vendored** from `gcf-typescript` (MIT, SPDX-marked, generic profile only) | `open-sse/services/compression/engines/headroom/gcf/` |
|
||||
| **LLMLingua-2 / Troglodita** | Inspired-by (drive the `llmlingua` + `session-dedup` engines) | `open-sse/services/compression/engines/llmlingua/`, `session-dedup` |
|
||||
|
||||
Key point: **RTK and Caveman are clean-room TypeScript implementations of the
|
||||
_ideas_ (filter rules, rule packs), not vendored source trees.** There is no
|
||||
upstream copy to `git pull` from — which is exactly why the README says
|
||||
"inspired by" rather than "bundled".
|
||||
|
||||
### How upstream improvements are merged
|
||||
|
||||
There is **no automated upstream-release tracking and no `compression-sync`
|
||||
label** — by design. Because the engines are reimplementations, an upstream RTK
|
||||
filter or Caveman rule pack is not merged as code; it is **re-expressed as a new
|
||||
rule/filter in OmniRoute's own format** (see
|
||||
[COMPRESSION_RULES_FORMAT.md](./COMPRESSION_RULES_FORMAT.md)) and lands ad-hoc via
|
||||
a normal PR. The extension points above (custom engine, language pack, RTK filter)
|
||||
are the sanctioned way to contribute one.
|
||||
|
||||
Recent examples of exactly this flow:
|
||||
|
||||
- RTK filters for Gradle & `dotnet` build output (v3.8.42)
|
||||
- RTK filters for kubectl / docker-build / composer / gh (#2824)
|
||||
- Caveman Indonesian language pack (#3975), plus German / French / Japanese / Chinese packs
|
||||
|
||||
### Headroom (input-compression proxy)
|
||||
|
||||
Headroom is **fully internal** — a pinned vendored `gcf` codec snapshot plus
|
||||
OmniRoute's own `smartcrusher` / `toon` / `tabular` layers. There is no live
|
||||
upstream to track beyond the vendored copy; updates to `gcf` are refreshed
|
||||
manually when the codec changes and re-validated against the compression budget
|
||||
gate (`check:compression-budget`).
|
||||
|
||||
### Proposing an upstream-inspired improvement
|
||||
|
||||
1. **Don't vendor** — re-express the upstream rule/filter in OmniRoute's format.
|
||||
2. Add it via the matching extension point below (language pack, RTK filter, or
|
||||
custom engine).
|
||||
3. Reference the upstream project in the PR description (attribution), not by
|
||||
copying its license-bearing source.
|
||||
4. Include tests and confirm the `check:compression-budget` gate still passes.
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Engine Development
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: "Embedded Services"
|
||||
description: "Reference for 9Router, CLIProxyAPI, Mux, and Bifrost"
|
||||
description: "Reference for 9Router and CLIProxyAPI"
|
||||
---
|
||||
|
||||
# Embedded Services
|
||||
|
||||
> **Version:** v3.8.44
|
||||
> **Last updated:** 2026-07-03
|
||||
> **Audience:** Engineers adding, maintaining, or debugging embedded services (9Router, CLIProxyAPI, Mux, Bifrost).
|
||||
> **Version:** v3.8.4
|
||||
> **Last updated:** 2026-06-28
|
||||
> **Audience:** Engineers adding, maintaining, or debugging embedded services (9Router, CLIProxyAPI).
|
||||
|
||||
Embedded services are locally-installed process sidecar tools that OmniRoute installs, supervises, and
|
||||
exposes as first-class routing targets. Unlike external providers (which are reached over the internet
|
||||
@@ -32,20 +32,18 @@ via API keys), embedded services run on the same machine as OmniRoute and commun
|
||||
|
||||
### Why embedded services?
|
||||
|
||||
Four services are embedded as of v3.8.44:
|
||||
Two services are embedded as of v3.8.4:
|
||||
|
||||
| Service | npm package | Default port | Purpose |
|
||||
| --------------- | ----------------------------------------------- | :----------: | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| **9Router** | `9router` | 20130 | AI router that OmniRoute can use as a sub-provider. Models exposed as `9router/{sub}/{model}` |
|
||||
| **CLIProxyAPI** | `@anthropic/cli-proxy` (via `cliproxy` binary) | auto | Local proxy adapter for Anthropic CLI auth flows. Provides fallback routing when OAuth tokens expire |
|
||||
| **Mux** | `mux` (headless `mux server`) | 8322 | Local agent-orchestration daemon (coder/mux). Lifecycle-managed only — not a routing target (no LLM proxying). |
|
||||
| **Bifrost** | `@maximhq/bifrost` | 8080 | Go AI-gateway relay backend. When running, auto-selected by the relay route (`/v1/relay/`) |
|
||||
| Service | npm package | Default port | Purpose |
|
||||
| --------------- | ---------------------------------------------- | :----------: | ---------------------------------------------------------------------------------------------------- |
|
||||
| **9Router** | `9router` | 20130 | AI router that OmniRoute can use as a sub-provider. Models exposed as `9router/{sub}/{model}` |
|
||||
| **CLIProxyAPI** | `@anthropic/cli-proxy` (via `cliproxy` binary) | auto | Local proxy adapter for Anthropic CLI auth flows. Provides fallback routing when OAuth tokens expire |
|
||||
|
||||
All four follow the same supervisory model:
|
||||
Both follow the same supervisory model:
|
||||
|
||||
- OmniRoute installs them under `DATA_DIR/services/{name}/` (isolated from OmniRoute's own `package.json`)
|
||||
- OmniRoute spawns and monitors them as child processes
|
||||
- OmniRoute injects an ephemeral API key into the child's environment and rotates it without downtime (where applicable)
|
||||
- OmniRoute injects an ephemeral API key into the child's environment and rotates it without downtime
|
||||
- All management routes (`/api/services/*`) are **LOCAL_ONLY** — accessible only from loopback (hard rule #17)
|
||||
|
||||
### Key decisions (from design plan)
|
||||
@@ -56,7 +54,7 @@ All four follow the same supervisory model:
|
||||
| Installation mechanism | `npm install {package}` via `execFile` (no shell interpolation) |
|
||||
| Consumption mode | Provider registered as `9router/{sub}/{model}` in routing engine |
|
||||
| API key management | OmniRoute generates, encrypts at-rest (AES-256-GCM), and injects via env |
|
||||
| Dashboard location | `/dashboard/providers/services` (three tabs) |
|
||||
| Dashboard location | `/dashboard/providers/services` (two tabs) |
|
||||
| Auto-start | Toggle per service, default OFF |
|
||||
|
||||
---
|
||||
@@ -66,13 +64,12 @@ All four follow the same supervisory model:
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ Layer 1 — UI │
|
||||
│ /dashboard/providers/services (tabs: CLIProxyAPI | 9Router | Mux)│
|
||||
│ /dashboard/providers/services (tabs: CLIProxyAPI | 9Router) │
|
||||
│ Logs live (SSE), Start/Stop/Restart/Update, Settings, Install │
|
||||
│ │
|
||||
│ src/app/(dashboard)/dashboard/providers/services/ │
|
||||
│ ├── page.tsx Shell + tab routing by ?tab= │
|
||||
│ ├── tabs/ CliproxyServiceTab, NinerouterServiceTab,│
|
||||
│ │ MuxServiceTab │
|
||||
│ ├── tabs/ CliproxyServiceTab, NinerouterServiceTab│
|
||||
│ └── components/ ServiceStatusCard, ServiceLifecycleButtons,│
|
||||
│ ServiceLogsPanel, ApiKeyCard, ... │
|
||||
└──────────────────────┬─────────────────────────────────────────────┘
|
||||
@@ -84,8 +81,6 @@ All four follow the same supervisory model:
|
||||
│ rotate-key|status|auto-start|logs} │
|
||||
│ /api/services/cliproxy/{install|start|stop|restart|update| │
|
||||
│ status|auto-start|logs} │
|
||||
│ /api/services/mux/{install|start|stop|restart|update| │
|
||||
│ status|auto-start|logs} │
|
||||
│ /dashboard/providers/services/9router/embed/[...path] │
|
||||
│ (reverse HTTP + WebSocket proxy → 9Router upstream) │
|
||||
│ │
|
||||
@@ -111,8 +106,7 @@ All four follow the same supervisory model:
|
||||
│ modelSync.ts Periodic GET /v1/models → service_models table │
|
||||
│ ringBuffer.ts Circular log buffer (5 MB per service) │
|
||||
│ healthCheck.ts Polling HTTP health probe │
|
||||
│ installers/ ninerouter.ts, cliproxy.ts, mux.ts │
|
||||
│ (installer adapters) │
|
||||
│ installers/ ninerouter.ts, cliproxy.ts (installer adapters)│
|
||||
└──────────────────────┬─────────────────────────────────────────────┘
|
||||
│ OpenAI-compatible HTTP (loopback)
|
||||
┌──────────────────────▼─────────────────────────────────────────────┐
|
||||
@@ -129,10 +123,6 @@ All four follow the same supervisory model:
|
||||
│ open-sse/config/providerRegistry.ts │
|
||||
│ Models stored as "9router/{sub}/{model}" (prefixed). │
|
||||
│ Synced every 5 min by modelSync.ts. │
|
||||
│ │
|
||||
│ Mux is lifecycle-managed ONLY (Layers 1-3) — it is an agent- │
|
||||
│ orchestration daemon, not an LLM proxy, so it has no Layer 4 │
|
||||
│ executor/provider entry and is never a routing target. │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -149,7 +139,6 @@ All four follow the same supervisory model:
|
||||
| `src/lib/services/healthCheck.ts` | HTTP health probe (configurable interval) |
|
||||
| `src/lib/services/installers/ninerouter.ts` | npm install/update/uninstall for 9Router |
|
||||
| `src/lib/services/installers/cliproxy.ts` | npm install/update/uninstall for CLIProxyAPI |
|
||||
| `src/lib/services/installers/mux.ts` | npm install/update/uninstall for Mux |
|
||||
| `src/app/api/services/9router/_lib.ts` | `getOrInitSupervisor()` helper |
|
||||
| `src/app/api/services/[name]/logs/route.ts` | Shared SSE logs endpoint |
|
||||
| `open-sse/executors/ninerouter.ts` | Provider executor (Layer 4) |
|
||||
@@ -445,56 +434,12 @@ config) and `status` includes fewer fields.
|
||||
| `GET` | `/api/services/cliproxy/status` | Live + DB status (no `apiKeyMasked`) |
|
||||
| `POST` | `/api/services/cliproxy/auto-start` | Toggle auto-start |
|
||||
|
||||
The shared `GET /api/services/{name}/logs` endpoint (see §4.1) works for all
|
||||
four services using the `[name]` dynamic segment.
|
||||
The shared `GET /api/services/{name}/logs` endpoint (see §4.1) works for both
|
||||
services using the `[name]` dynamic segment.
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Mux endpoints (7 routes)
|
||||
|
||||
Mux has the same endpoint shape as CLIProxyAPI — no `rotate-key` route in the API
|
||||
surface (the bearer token is generated the same way as 9Router's via
|
||||
`getOrCreateApiKey("mux")` and injected via the `MUX_SERVER_AUTH_TOKEN` env var, but
|
||||
there is no dedicated rotation endpoint yet). Mux is lifecycle-managed only: unlike
|
||||
9Router, it has no Layer 4 executor and is never registered as a routing provider.
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | -------------------------------- | ------------------------------------- |
|
||||
| `POST` | `/api/services/mux/install` | Install Mux from npm (`npm i mux`) |
|
||||
| `POST` | `/api/services/mux/start` | Start Mux (`mux server`) |
|
||||
| `POST` | `/api/services/mux/stop` | Stop Mux |
|
||||
| `POST` | `/api/services/mux/restart` | Restart Mux |
|
||||
| `POST` | `/api/services/mux/update` | Update to newer npm version |
|
||||
| `GET` | `/api/services/mux/status` | Live + DB status |
|
||||
| `POST` | `/api/services/mux/auto-start` | Toggle auto-start |
|
||||
|
||||
---
|
||||
|
||||
### 4.4 Bifrost endpoints (7 routes)
|
||||
|
||||
Bifrost is a Go AI-gateway relay backend (`@maximhq/bifrost`). It uses the same
|
||||
endpoint shape as CLIProxyAPI (no `rotate-key` — Bifrost manages its own provider
|
||||
keys in `config.json` under its `-app-dir`).
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | ---------------------------------- | ------------------------------------------------------ |
|
||||
| `POST` | `/api/services/bifrost/install` | Install Bifrost from npm (`@maximhq/bifrost`) |
|
||||
| `POST` | `/api/services/bifrost/start` | Start Bifrost on port 8080 (default) |
|
||||
| `POST` | `/api/services/bifrost/stop` | Stop Bifrost |
|
||||
| `POST` | `/api/services/bifrost/restart` | Restart Bifrost |
|
||||
| `POST` | `/api/services/bifrost/update` | Update to newer version |
|
||||
| `GET` | `/api/services/bifrost/status` | Live + DB status |
|
||||
| `POST` | `/api/services/bifrost/auto-start` | Toggle auto-start |
|
||||
| `GET` | `/api/services/bifrost/logs` | SSE log tail (via shared `[name]/logs` dynamic route) |
|
||||
|
||||
**Routing wiring:** When `BIFROST_BASE_URL` is unset and the supervised Bifrost
|
||||
instance is running, `getBifrostRoutingConfig()` (in `routingBackend.ts`) automatically
|
||||
uses `http://127.0.0.1:{port}` as the relay base URL. Explicit `BIFROST_BASE_URL` env
|
||||
always takes precedence.
|
||||
|
||||
---
|
||||
|
||||
### 4.4 Reverse proxy (9Router dashboard embed)
|
||||
### 4.3 Reverse proxy (9Router dashboard embed)
|
||||
|
||||
The dashboard embeds the 9Router web UI inside an iframe via an internal reverse
|
||||
proxy at:
|
||||
@@ -541,20 +486,14 @@ matrix.
|
||||
|
||||
### API key injection
|
||||
|
||||
9Router and Mux require an API key/bearer token for their own HTTP endpoints.
|
||||
OmniRoute:
|
||||
9Router requires an API key for its own HTTP endpoints. OmniRoute:
|
||||
|
||||
1. Generates a key via `crypto.randomBytes(32).toString("base64url")` with a
|
||||
service-specific prefix (`nr_` for 9Router, `mx_` for Mux).
|
||||
service-specific prefix (`nr_` for 9Router).
|
||||
2. Encrypts it at-rest using AES-256-GCM (same cipher used for provider credentials).
|
||||
3. Decrypts and injects it as an environment variable at spawn time —
|
||||
`NINEROUTER_API_KEY` for 9Router, `MUX_SERVER_AUTH_TOKEN` for Mux (never a CLI
|
||||
flag, so the token never appears in `ps`/process listings).
|
||||
3. Decrypts and injects it as `NINEROUTER_API_KEY` environment variable at spawn time.
|
||||
4. Never returns the plaintext key in any HTTP response.
|
||||
|
||||
CLIProxyAPI does not require an injected key (it authenticates via the host's
|
||||
existing CLI config).
|
||||
|
||||
### SSRF defense
|
||||
|
||||
The reverse HTTP proxy (`/dashboard/.../embed/[...path]`) is hardcoded to forward
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
|
||||
## [3.8.31] — 2026-06-20
|
||||
|
||||
## [3.8.44] — TBD
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
_TBD_
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
_TBD_
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
- **chore(release):** release-pipeline hardening — `check:test-masking` (vs `origin/main`) is now a HARD gate in the release-green pre-flight (`validate-release-green.mjs`), so a non-allowlisted net-assert reduction surfaces locally instead of in a ~40-min CI layer on the release PR; plus two reconciliation helpers — `npm run release:contributors` (reproducible `### 🙌 Contributors` table via a parenthetical-group parser) and `npm run release:uncovered` (lists commits with no CHANGELOG bullet). ([#5926](https://github.com/diegosouzapw/OmniRoute/pull/5926) — thanks @diegosouzapw)
|
||||
|
||||
- **chore(ci):** the `check:pr-evidence` FAIL report now tells you that editing the PR body does not re-run the gate (`ci.yml` ignores the `edited` event) — push a commit to re-validate. ([#5944](https://github.com/diegosouzapw/OmniRoute/pull/5944) — thanks @diegosouzapw)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.43] — 2026-07-02
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: OmniRoute API
|
||||
version: 3.8.44
|
||||
version: 3.8.43
|
||||
description: |
|
||||
OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible
|
||||
endpoint that routes requests to multiple AI providers with load balancing,
|
||||
@@ -3444,276 +3444,6 @@ paths:
|
||||
"400":
|
||||
description: Invalid request body
|
||||
|
||||
/api/services/mux/install:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Install Mux from npm
|
||||
description: >-
|
||||
Installs the `mux` npm package (coder/mux — local agent-orchestration
|
||||
daemon) under DATA_DIR/services/mux/. **LOCAL_ONLY** — loopback only.
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
default: latest
|
||||
responses:
|
||||
"200":
|
||||
description: Install succeeded
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
ok:
|
||||
type: boolean
|
||||
installedVersion:
|
||||
type: string
|
||||
"400":
|
||||
description: Invalid request body
|
||||
"500":
|
||||
description: npm install failed
|
||||
|
||||
/api/services/mux/start:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Start Mux
|
||||
description: >-
|
||||
Spawns `mux server --host 127.0.0.1 --port <port>`. Idempotent if
|
||||
already running. **LOCAL_ONLY** — loopback only.
|
||||
responses:
|
||||
"200":
|
||||
description: Service started
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ServiceStatus"
|
||||
"409":
|
||||
description: Mux is not installed
|
||||
"503":
|
||||
description: Start failed
|
||||
|
||||
/api/services/mux/stop:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Stop Mux
|
||||
description: >-
|
||||
Gracefully stops Mux. Idempotent.
|
||||
**LOCAL_ONLY** — loopback only.
|
||||
responses:
|
||||
"200":
|
||||
description: Service stopped
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ServiceStatus"
|
||||
|
||||
/api/services/mux/restart:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Restart Mux
|
||||
description: >-
|
||||
stop() then start() under the operation lock.
|
||||
**LOCAL_ONLY** — loopback only.
|
||||
responses:
|
||||
"200":
|
||||
description: Service restarted
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ServiceStatus"
|
||||
|
||||
/api/services/mux/update:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Update Mux to a newer npm version
|
||||
description: >-
|
||||
Stops, installs newer version, restarts.
|
||||
**LOCAL_ONLY** — loopback only.
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
default: latest
|
||||
responses:
|
||||
"200":
|
||||
description: Update succeeded
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
ok:
|
||||
type: boolean
|
||||
installedVersion:
|
||||
type: string
|
||||
"500":
|
||||
description: Update failed
|
||||
|
||||
/api/services/mux/status:
|
||||
get:
|
||||
tags: [Embedded Services]
|
||||
summary: Get Mux status
|
||||
description: >-
|
||||
Returns live supervisor state and DB metadata.
|
||||
**LOCAL_ONLY** — loopback only.
|
||||
responses:
|
||||
"200":
|
||||
description: Status response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ServiceStatus"
|
||||
|
||||
/api/services/mux/auto-start:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Toggle Mux auto-start
|
||||
description: >-
|
||||
When enabled, Mux starts automatically on the next OmniRoute boot.
|
||||
**LOCAL_ONLY** — loopback only.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [enabled]
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: Auto-start flag updated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
autoStart:
|
||||
type: boolean
|
||||
"400":
|
||||
description: Invalid request body
|
||||
|
||||
/api/services/bifrost/install:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Install Bifrost
|
||||
description: >-
|
||||
Installs the `@maximhq/bifrost` npm package under DATA_DIR/services/bifrost/.
|
||||
The package downloads the Go binary on first run. Accepts an optional `version`
|
||||
field (semver or `latest`). **LOCAL_ONLY** — loopback only.
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
default: latest
|
||||
responses:
|
||||
"200":
|
||||
description: Installation result
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
ok:
|
||||
type: boolean
|
||||
installedVersion:
|
||||
type: string
|
||||
installPath:
|
||||
type: string
|
||||
durationMs:
|
||||
type: number
|
||||
|
||||
/api/services/bifrost/start:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Start Bifrost
|
||||
description: Starts the supervised Bifrost process. **LOCAL_ONLY** — loopback only.
|
||||
responses:
|
||||
"200":
|
||||
description: Service status after start
|
||||
"409":
|
||||
description: Bifrost is not installed
|
||||
|
||||
/api/services/bifrost/stop:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Stop Bifrost
|
||||
description: Stops the supervised Bifrost process. **LOCAL_ONLY** — loopback only.
|
||||
responses:
|
||||
"200":
|
||||
description: Service status after stop
|
||||
|
||||
/api/services/bifrost/restart:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Restart Bifrost
|
||||
description: Restarts the supervised Bifrost process. **LOCAL_ONLY** — loopback only.
|
||||
responses:
|
||||
"200":
|
||||
description: Service status after restart
|
||||
"409":
|
||||
description: Bifrost is not installed
|
||||
|
||||
/api/services/bifrost/update:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Update Bifrost
|
||||
description: >-
|
||||
Updates Bifrost to the latest npm version. Stops the running process,
|
||||
installs the new version, and restarts if it was previously running.
|
||||
**LOCAL_ONLY** — loopback only.
|
||||
responses:
|
||||
"200":
|
||||
description: Update result
|
||||
|
||||
/api/services/bifrost/status:
|
||||
get:
|
||||
tags: [Embedded Services]
|
||||
summary: Get Bifrost status
|
||||
description: Returns live and DB status for the supervised Bifrost service. **LOCAL_ONLY** — loopback only.
|
||||
responses:
|
||||
"200":
|
||||
description: Bifrost service status
|
||||
|
||||
/api/services/bifrost/auto-start:
|
||||
post:
|
||||
tags: [Embedded Services]
|
||||
summary: Toggle Bifrost auto-start
|
||||
description: >-
|
||||
When enabled, Bifrost starts automatically on the next OmniRoute boot.
|
||||
**LOCAL_ONLY** — loopback only.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [enabled]
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
responses:
|
||||
"204":
|
||||
description: Auto-start flag updated
|
||||
"400":
|
||||
description: Invalid request body
|
||||
|
||||
/api/services/{name}/logs:
|
||||
get:
|
||||
tags: [Embedded Services]
|
||||
@@ -5534,214 +5264,6 @@ paths:
|
||||
$ref: "#/components/responses/InternalError"
|
||||
"503":
|
||||
description: Generator module not available
|
||||
/api/v1/ocr:
|
||||
post:
|
||||
tags:
|
||||
- Images
|
||||
summary: Document OCR
|
||||
description: >-
|
||||
Mistral OCR–compatible document OCR endpoint. Accepts a JSON body
|
||||
referencing a document/image and returns extracted text. Success
|
||||
responses carry the `X-OmniRoute-*` cost-telemetry headers.
|
||||
security:
|
||||
- BearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
model:
|
||||
type: string
|
||||
document:
|
||||
type: object
|
||||
responses:
|
||||
"200":
|
||||
description: OCR result with extracted text.
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
/api/v1/audio/translations:
|
||||
post:
|
||||
tags:
|
||||
- Audio
|
||||
summary: Translate audio to English
|
||||
description: >-
|
||||
OpenAI Whisper–compatible audio translation (multipart/form-data).
|
||||
Unlike `/api/v1/audio/transcriptions`, output is always English
|
||||
regardless of the source language. Success responses carry the
|
||||
`X-OmniRoute-*` cost-telemetry headers.
|
||||
security:
|
||||
- BearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- file
|
||||
properties:
|
||||
file:
|
||||
type: string
|
||||
format: binary
|
||||
model:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: English translation of the audio.
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
/api/v1/providers/suggested-models:
|
||||
get:
|
||||
tags:
|
||||
- Providers
|
||||
summary: Suggested media models
|
||||
description: >-
|
||||
Read-only server-side proxy to the public HuggingFace Hub models search
|
||||
API, used by the dashboard to suggest models for a media provider kind
|
||||
without exposing an HF token client-side. Never accepts or returns
|
||||
credentials.
|
||||
parameters:
|
||||
- name: type
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
description: Media kind to search for (e.g. `image`, `audio`, `video`).
|
||||
responses:
|
||||
"200":
|
||||
description: List of suggested HuggingFace Hub models.
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
/api/v1/provider-plugin-manifest:
|
||||
get:
|
||||
tags:
|
||||
- Providers
|
||||
summary: Provider plugin manifest
|
||||
description: Returns the manifest describing installed provider plugins.
|
||||
responses:
|
||||
"200":
|
||||
description: Provider plugin manifest.
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
/api/keys/{id}/devices:
|
||||
get:
|
||||
tags:
|
||||
- API Keys
|
||||
summary: List devices for an API key
|
||||
description: >-
|
||||
Lists the distinct devices (masked IP + User-Agent fingerprints)
|
||||
tracked for an API key by the in-memory device tracker. IPs are masked
|
||||
before storage; the route never sees the raw client IP.
|
||||
x-internal: true
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: Distinct devices seen for the API key.
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"404":
|
||||
$ref: "#/components/responses/NotFound"
|
||||
/api/settings/purge-usage-history:
|
||||
post:
|
||||
tags:
|
||||
- Settings
|
||||
summary: Purge usage history
|
||||
description: Dashboard-only. Purges stored usage-history records.
|
||||
x-internal: true
|
||||
responses:
|
||||
"200":
|
||||
description: Usage history purged.
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
/api/oauth/codex/import-token:
|
||||
post:
|
||||
tags:
|
||||
- OAuth
|
||||
summary: Import a Codex connection from a bare access token
|
||||
description: >-
|
||||
Dashboard-only. Creates a Codex (ChatGPT/OpenAI) connection from a raw
|
||||
access token with no refresh token (authType `access_token`).
|
||||
x-internal: true
|
||||
responses:
|
||||
"200":
|
||||
description: Connection imported.
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
/api/cli-tools/crush-settings:
|
||||
get:
|
||||
tags:
|
||||
- CLI Tools
|
||||
summary: Read Crush CLI OmniRoute config
|
||||
description: Local-only. Reads the OmniRoute provider block in Crush's config.
|
||||
x-internal: true
|
||||
responses:
|
||||
"200":
|
||||
description: Current Crush config state.
|
||||
post:
|
||||
tags:
|
||||
- CLI Tools
|
||||
summary: Write Crush CLI OmniRoute config
|
||||
description: Local-only. Registers OmniRoute as an `openai-compat` provider in Crush's config.
|
||||
x-internal: true
|
||||
responses:
|
||||
"200":
|
||||
description: Crush config updated.
|
||||
delete:
|
||||
tags:
|
||||
- CLI Tools
|
||||
summary: Remove OmniRoute from Crush CLI config
|
||||
description: Local-only. Removes the OmniRoute provider block from Crush's config.
|
||||
x-internal: true
|
||||
responses:
|
||||
"200":
|
||||
description: Crush config entry removed.
|
||||
/api/cli-tools/codewhale-settings:
|
||||
get:
|
||||
tags:
|
||||
- CLI Tools
|
||||
summary: Read CodeWhale CLI OmniRoute config
|
||||
description: >-
|
||||
Local-only. Reads the OmniRoute config block from
|
||||
`~/.codewhale/config.toml` (with `~/.deepseek/config.toml` legacy
|
||||
fallback).
|
||||
x-internal: true
|
||||
responses:
|
||||
"200":
|
||||
description: Current CodeWhale config state.
|
||||
post:
|
||||
tags:
|
||||
- CLI Tools
|
||||
summary: Write CodeWhale CLI OmniRoute config
|
||||
description: Local-only. Writes the OmniRoute config block in CodeWhale TOML format.
|
||||
x-internal: true
|
||||
responses:
|
||||
"200":
|
||||
description: CodeWhale config updated.
|
||||
delete:
|
||||
tags:
|
||||
- CLI Tools
|
||||
summary: Remove OmniRoute from CodeWhale CLI config
|
||||
description: Local-only. Removes the OmniRoute config block from CodeWhale's config.
|
||||
x-internal: true
|
||||
responses:
|
||||
"200":
|
||||
description: CodeWhale config entry removed.
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
|
||||
@@ -18,7 +18,6 @@ Complete reference for all OmniRoute API endpoints.
|
||||
- [Embeddings](#embeddings)
|
||||
- [Image Generation](#image-generation)
|
||||
- [List Models](#list-models)
|
||||
- [Provider Plugin Manifest](#provider-plugin-manifest)
|
||||
- [Compatibility Endpoints](#compatibility-endpoints)
|
||||
- [Files API](#files-api)
|
||||
- [Batches API](#batches-api)
|
||||
@@ -179,22 +178,6 @@ Selecting this id (e.g. in a Claude Code config that always attaches a `thinking
|
||||
|
||||
---
|
||||
|
||||
## Provider Plugin Manifest
|
||||
|
||||
```bash
|
||||
GET /api/v1/provider-plugin-manifest
|
||||
```
|
||||
|
||||
Returns the JSON-safe provider plugin manifest used by Bifrost, CLIProxyAPI, and
|
||||
future sidecar routers. The response is generated from the TypeScript provider
|
||||
registry and intentionally excludes OAuth client secrets, runtime environment
|
||||
resolution, executor functions, request headers, and account data.
|
||||
|
||||
Use this endpoint when a sidecar runs out-of-process and cannot import
|
||||
`open-sse/config/providerPluginManifestRegistry.ts` directly.
|
||||
|
||||
---
|
||||
|
||||
## Compatibility Endpoints
|
||||
|
||||
| Method | Path | Format |
|
||||
|
||||
@@ -12,7 +12,7 @@ OmniRoute integrates with three categories of CLI tools spread across three dedi
|
||||
|
||||
| Page | Route | Concept | Count |
|
||||
| -------------- | ----------------------- | ------------------------------------------------------------------------- | ------------ |
|
||||
| **CLI Code's** | `/dashboard/cli-code` | Coding tools you point at OmniRoute (Client → CLI → OmniRoute → Provider) | 20 |
|
||||
| **CLI Code's** | `/dashboard/cli-code` | Coding tools you point at OmniRoute (Client → CLI → OmniRoute → Provider) | 19 |
|
||||
| **CLI Agents** | `/dashboard/cli-agents` | Autonomous agents you point at OmniRoute (same flow, broader scope) | 6 |
|
||||
| **ACP Agents** | `/dashboard/acp-agents` | CLIs that OmniRoute spawns as backend via stdio/ACP (reverse flow) | see registry |
|
||||
|
||||
@@ -90,7 +90,7 @@ Entries with `baseUrlSupport: "none"` are **not shown** in the dashboard pages
|
||||
|
||||
---
|
||||
|
||||
## 1. CLI Code's Catalog (20 tools)
|
||||
## 1. CLI Code's Catalog (19 tools)
|
||||
|
||||
Tools that support custom base URL and appear in `/dashboard/cli-code`:
|
||||
|
||||
@@ -107,7 +107,6 @@ Tools that support custom base URL and appear in `/dashboard/cli-code`:
|
||||
| forge | ForgeCode | Antinomy HQ | full | custom | true |
|
||||
| jcode | jcode | 1jehuang (OSS) | full | custom | false |
|
||||
| deepseek-tui | DeepSeek TUI | Hunter Bown (OSS) | full | custom | false |
|
||||
| codewhale | CodeWhale | Hmbown (OSS) | full | custom | false |
|
||||
| opencode | OpenCode | Anomaly (ex-SST) | full | guide | true |
|
||||
| droid | Factory Droid | Factory AI | partial | guide | false |
|
||||
| copilot | GitHub Copilot CLI | GitHub/MS | full | custom | false |
|
||||
@@ -199,8 +198,7 @@ New tools with `configType: "custom"` have dedicated settings API routes:
|
||||
| ------------------------------------------- | ------------------------------ |
|
||||
| `POST /api/cli-tools/forge-settings` | ForgeCode (.forge.toml) |
|
||||
| `POST /api/cli-tools/jcode-settings` | jcode (--base-url flag) |
|
||||
| `POST /api/cli-tools/deepseek-tui-settings` | DeepSeek TUI (OPENAI_BASE_URL, legacy) |
|
||||
| `POST /api/cli-tools/codewhale-settings` | CodeWhale (OPENAI_BASE_URL, primary + legacy `~/.deepseek` sync) |
|
||||
| `POST /api/cli-tools/deepseek-tui-settings` | DeepSeek TUI (OPENAI_BASE_URL) |
|
||||
| `POST /api/cli-tools/smelt-settings` | Smelt |
|
||||
| `POST /api/cli-tools/pi-settings` | Pi coding agent |
|
||||
|
||||
|
||||
@@ -117,7 +117,6 @@ OmniRoute uses **SQLite** (via `better-sqlite3`) for all persistence. These vari
|
||||
| Variable | Default | Source File | Description |
|
||||
| ------------------------------------------- | ------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `PORT` | `20128` | `src/lib/runtime/ports.ts` | Primary port for both Dashboard UI and API endpoints (single-port mode). |
|
||||
| `OMNIROUTE_BASE_PATH` | _(empty = root)_ | `next.config.mjs` | URL subpath for serving OmniRoute behind a reverse proxy under a subpath (sets Next.js `basePath`; auth redirects are basePath-aware). E.g. `/omniroute`. |
|
||||
| `API_PORT` | _(unset)_ | `src/lib/runtime/ports.ts` | When set, serves the `/v1/*` proxy API on this separate port. |
|
||||
| `API_HOST` | `0.0.0.0` | `src/lib/runtime/ports.ts` | Bind address for the API port. |
|
||||
| `DASHBOARD_PORT` | _(unset)_ | `src/lib/runtime/ports.ts` | When set, serves the Dashboard UI on this separate port. |
|
||||
@@ -265,8 +264,6 @@ OmniRoute provides a two-layer defense: request-side injection scanning and resp
|
||||
| `NEXT_PUBLIC_CLOUD_URL` | _(empty)_ | Client-side | Client-side mirror of `CLOUD_URL`. |
|
||||
| `NEXT_PUBLIC_APP_URL` | _(unset)_ | `src/shared/services/cloudSyncScheduler.ts` | Legacy fallback for `NEXT_PUBLIC_BASE_URL`. |
|
||||
| `OMNIROUTE_PUBLIC_BASE_URL` | _(unset)_ | Public-origin resolver, image URLs | Highest-priority browser-facing OmniRoute origin used for public URL generation and non-dashboard browser-origin validation (for example `/v1/chatgpt-web/image/<id>`). Set this when OpenWebUI or another relay reaches OmniRoute by an internal URL but the user's browser must fetch images from a LAN, tunnel, or public origin. Do **not** include `/v1`. |
|
||||
| `OMNIROUTE_PROVIDER_MANIFEST_URL` | _(unset)_ | `open-sse/config/providerPluginManifestUrl.ts` | Absolute provider plugin manifest URL advertised to sidecar clients. When unset, OmniRoute derives `/api/v1/provider-plugin-manifest` from request origin or HOST/PORT. |
|
||||
| `OMNIROUTE_PUBLIC_PROTOCOL` | `http` | `open-sse/config/providerPluginManifestUrl.ts` | Protocol used when deriving the provider plugin manifest URL from HOST/PORT without a request origin. Set to `https` behind a TLS-terminating public proxy when no explicit `OMNIROUTE_PROVIDER_MANIFEST_URL` is set. |
|
||||
| `OMNIROUTE_TRUST_PROXY` | _(unset)_ | `src/server/origin/publicOrigin.ts` | Optional trust mode for forwarded public-origin headers. Unset = do not trust `Forwarded` / `X-Forwarded-*` for security decisions. `true` / `loopback` trusts forwarded host/proto only from a token-stamped loopback proxy. `private` / `lan` also trusts private-LAN proxy peers. Prefer explicit `NEXT_PUBLIC_BASE_URL` in production. |
|
||||
| `OMNIROUTE_CGPT_WEB_IMAGE_TIMEOUT_MS` | `180000` (3 min) | `open-sse/executors/chatgpt-web.ts` | Max wait time for an async chatgpt-web image to land via the celsius WebSocket. Increase during upstream queue-deep windows. |
|
||||
| `OMNIROUTE_CGPT_WEB_IMAGE_CACHE_MAX_MB` | `256` | `open-sse/services/chatgptImageCache.ts` | Total in-memory byte budget (MB) for the chatgpt-web image cache serving `/v1/chatgpt-web/image/<id>`. Lower on memory-constrained hosts; raise if image generation is heavy and clients race the 30-minute TTL. |
|
||||
@@ -356,8 +353,6 @@ Controls how OmniRoute discovers and launches CLI sidecars (Claude Code, Codex,
|
||||
| `CLI_QODER_BIN` | `qoder` | `src/shared/services/cliRuntime.ts` | Custom path to Qoder CLI binary. |
|
||||
| `CLI_QWEN_BIN` | `qwen` | `src/shared/services/cliRuntime.ts` | Custom path to the Qwen Code CLI binary. |
|
||||
| `CLI_DEVIN_BIN` | `devin` | `open-sse/executors/devin-cli.ts` | Custom path to the Devin CLI binary (v3.8.0). Used by the Windsurf/Devin executor. |
|
||||
| `AUGGIE_BIN` | `auggie` | `open-sse/executors/auggie.ts` | Absolute-path override for the Augment (Auggie) CLI binary used by the local `auggie` provider. Falls back to `CLI_AUGGIE_BIN`, then a PATH lookup. |
|
||||
| `CLI_AUGGIE_BIN` | `auggie` | `open-sse/executors/auggie.ts` | Alias override for the Augment (Auggie) CLI binary path (checked after `AUGGIE_BIN`). |
|
||||
| `HERMES_HOME` | `~/.hermes` | `src/lib/cli-helper/config-generator/hermesHome.ts` | Hermes Agent home directory where OmniRoute reads/writes the Hermes CLI config. Matches the env var the Hermes PowerShell installer sets on Windows (`%LOCALAPPDATA%\hermes`). |
|
||||
|
||||
### CLI Profile Auto-Sync
|
||||
@@ -480,7 +475,6 @@ Built-in credentials for **localhost development**. For remote deployments, regi
|
||||
| `QODER_PERSONAL_ACCESS_TOKEN` | Qoder | Direct API key fallback (bypasses OAuth). |
|
||||
| `QODER_CLI_WORKSPACE` | Qoder | Workspace ID for Qoder CLI. |
|
||||
| `OMNIROUTE_QODER_WORKSPACE` | Qoder | Alias for `QODER_CLI_WORKSPACE`. |
|
||||
| `QODER_CLI_CONFIG_DIR` | Qoder | Override the Qoder CLI config dir (isolated PAT session, avoids clobbering a browser login). |
|
||||
| `BLACKBOX_WEB_VALIDATED_TOKEN` | Blackbox Web | Frontend `tk` token to send as `validated` on `/api/chat`. Required when Blackbox enforces token matching; otherwise OmniRoute falls back to a random UUID. See issue #2252. |
|
||||
| `VISION_BRIDGE_BASE_URL` | Vision Bridge guardrail | OpenAI-compatible base URL for non-Anthropic vision-bridge calls. Defaults to the legacy OpenAI URL env or api.openai.com. Point at OmniRoute's `/v1` self-loop or any OpenAI-compat endpoint (Gemini OpenAI-compat, OpenRouter). Issue #2232. |
|
||||
| `VISION_BRIDGE_API_KEY` | Vision Bridge guardrail | API key for the URL above. Overrides per-provider OpenAI / Google env vars for non-Anthropic vision-bridge calls. Anthropic models keep their dedicated Anthropic key path. Issue #2232. |
|
||||
@@ -624,8 +618,6 @@ REQUEST_TIMEOUT_MS (global override)
|
||||
| `FETCH_KEEPALIVE_TIMEOUT_MS` | `4000` | Keep-alive socket idle timeout. |
|
||||
| `TLS_CLIENT_TIMEOUT_MS` | = `FETCH_TIMEOUT_MS` | TLS fingerprint proxy (wreq-js) timeout. |
|
||||
| `API_BRIDGE_PROXY_TIMEOUT_MS` | `30000` | Proxy hop timeout for `/v1` bridge requests. |
|
||||
| `FIRECRAWL_BASE_URL` | `https://api.firecrawl.dev` | Point the Firecrawl web-fetch executor at a self-hosted instance (API key optional off-cloud). |
|
||||
| `FIRECRAWL_TIMEOUT_MS` | `30000` | Per-request timeout for the Firecrawl web-fetch executor. |
|
||||
| `API_BRIDGE_SERVER_REQUEST_TIMEOUT_MS` | `300000` | Overall server request timeout for the bridge. |
|
||||
| `API_BRIDGE_SERVER_HEADERS_TIMEOUT_MS` | `60000` | Time to send response headers via the bridge. |
|
||||
| `API_BRIDGE_SERVER_KEEPALIVE_TIMEOUT_MS` | `5000` | Bridge keep-alive idle timeout. |
|
||||
@@ -814,7 +806,6 @@ Automatic model pricing data synchronization from external sources.
|
||||
| `CLIPROXYAPI_HOST` | `127.0.0.1` | `open-sse/executors/cliproxyapi.ts` | CLIProxyAPI bridge host (legacy integration). |
|
||||
| `CLIPROXYAPI_PORT` | `5544` | `open-sse/executors/cliproxyapi.ts` | CLIProxyAPI bridge port. |
|
||||
| `CLIPROXYAPI_CONFIG_DIR` | `~/.cli-proxy-api` | `src/lib/versionManager/processManager.ts` | CLIProxyAPI config directory. |
|
||||
| `MUX_SERVICE_PORT` | `8322` | `src/lib/services/bootstrap.ts` | Override the port where the embedded Mux (coder/mux) agent-orchestration daemon listens (always 127.0.0.1). |
|
||||
| `LOCAL_HOSTNAMES` | _(empty)_ | `open-sse/config/providerRegistry.ts` | Comma-separated additional hostnames treated as "local" (Docker service names, etc.). |
|
||||
|
||||
`ENABLE_CC_COMPATIBLE_PROVIDER` is only for third-party relays that accept Claude Code clients
|
||||
@@ -831,11 +822,6 @@ Anthropic-compatible provider instead.
|
||||
| `PROXY_FAST_FAIL_TIMEOUT_MS` | `2000` | `src/lib/proxyHealth.ts` | Fast-fail health check timeout. |
|
||||
| `PROXY_HEALTH_CACHE_TTL_MS` | `30000` | `src/lib/proxyHealth.ts` | Health check result cache TTL. |
|
||||
| `PROXY_HEALTH_UNHEALTHY_CACHE_TTL_MS` | `2000` | `src/lib/proxyHealth.ts` | Cache TTL for failed proxy health probes. Keep this shorter than `PROXY_HEALTH_CACHE_TTL_MS` so transient proxy timeouts under high concurrency retry quickly without disabling fast-fail for truly dead proxies. |
|
||||
| `PROXY_HEALTH_ENABLED` | `true` | `src/lib/proxyHealth/scheduler.ts` | Set `false` to disable the background proxy health scheduler that periodically probes registered proxies. |
|
||||
| `PROXY_HEALTH_INTERVAL_MS` | `600000` | `src/lib/proxyHealth/scheduler.ts` | Background health-scheduler sweep interval in ms (minimum `60000`). |
|
||||
| `PROXY_HEALTH_TEST_URL` | `https://httpbin.org/ip` | `src/lib/proxyHealth/scheduler.ts` | Reachability probe target used by the scheduler and the `/api/settings/proxies/auto-test` endpoint. Point it at an internal/self-hosted URL to avoid the public default. |
|
||||
| `PROXY_AUTO_REMOVE` | `false` | `src/lib/proxyHealth/scheduler.ts` | Set `true` to let the scheduler auto-remove proxies after repeated consecutive failures. |
|
||||
| `PROXY_AUTO_REMOVE_AFTER` | `3` | `src/lib/proxyHealth/scheduler.ts` | Consecutive failures before the scheduler auto-removes a proxy (when `PROXY_AUTO_REMOVE=true`). |
|
||||
| `OMNIROUTE_CONTROL_PLANE_PROXY_DIRECT_FALLBACK` | `false` | `src/shared/constants/featureFlagDefinitions.ts` | Allow OAuth and provider validation flows to bypass a pinned proxy and connect directly when proxy reachability pre-checks fail. Effective precedence is Feature Flags DB override > env var > default. |
|
||||
| `RATE_LIMIT_MAX_WAIT_MS` | `120000` (2 min) | `open-sse/services/rateLimitManager.ts` | Max time to wait on a 429 before failing the request. |
|
||||
| `RATE_LIMIT_AUTO_ENABLE` | _(unset)_ | `open-sse/services/rateLimitManager.ts` | Force the auto-enable rate limit safety net on/off regardless of the persisted Dashboard setting. Accepts `true`/`1`/`on` to force on, `false`/`0`/`off` to force off. |
|
||||
@@ -1019,10 +1005,6 @@ Provider quota endpoints, network tunnels (Tailscale, Ngrok, MITM debug proxy),
|
||||
| `FREE_PROXY_PROXIFLY_ANONYMITY` | `elite` | `src/lib/freeProxyProviders/proxifly.ts` | Anonymity level filter for Proxifly (`elite`, `anonymous`, `transparent`). |
|
||||
| `FREE_PROXY_IPLOCATE_ENABLED` | `false` | `src/lib/freeProxyProviders/iplocate.ts` | Enable the IPLocate free proxy source. Opt-in only. |
|
||||
| `FREE_PROXY_IPLOCATE_BASE_URL` | `https://raw.githubusercontent.com/iplocate/free-proxy-list/main/protocols` | `src/lib/freeProxyProviders/iplocate.ts` | IPLocate proxy list base URL override. |
|
||||
| `FREE_PROXY_WEBSHARE_ENABLED` | `true` | `src/lib/freeProxyProviders/webshare.ts` | Enable the Webshare proxy pool source. Set to `false` to disable; also requires `FREE_PROXY_WEBSHARE_API_KEY` to be set. |
|
||||
| `FREE_PROXY_WEBSHARE_API_KEY` | _(none)_ | `src/lib/freeProxyProviders/webshare.ts` | Webshare account API token (`Authorization: Token <key>`). Required — the provider stays disabled without it. |
|
||||
| `FREE_PROXY_WEBSHARE_API_URL` | `https://proxy.webshare.io/api/v2/proxy/list/` | `src/lib/freeProxyProviders/webshare.ts` | Webshare proxy list API URL override. |
|
||||
| `FREE_PROXY_WEBSHARE_MAX` | `500` | `src/lib/freeProxyProviders/webshare.ts` | Maximum proxies imported per Webshare sync. |
|
||||
| `NEXT_PUBLIC_VERCEL_RELAY_ENABLED` | `true` | `src/app/(dashboard)/…/ProxyPoolTab.tsx` | Show/hide the Deploy Vercel Relay button in the Proxy Pool tab. |
|
||||
| `VERCEL_API_BASE` | `https://api.vercel.com` | `src/app/api/settings/proxy/vercel-deploy/route.ts` | Vercel API base URL override (for testing). |
|
||||
| `NEXT_PUBLIC_VERCEL_RELAY_DEFAULT_PROJECT` | `omniroute-relay` | `src/app/(dashboard)/…/VercelRelayModal.tsx` | Default project name pre-filled in the Vercel Relay deploy modal. |
|
||||
@@ -1056,7 +1038,6 @@ Provider quota endpoints, network tunnels (Tailscale, Ngrok, MITM debug proxy),
|
||||
| `PLAYGROUND_IMPROVE_PROMPT_DEFAULT_MODEL` | _(unset)_ | `src/app/(dashboard)/dashboard/playground/` | Default model for the Playground 'improve prompt' action (falls back to the active model when unset). |
|
||||
| `BIFROST_ENABLED` | `1` | `src/app/api/v1/relay/chat/completions/bifrost/route.ts` | Master kill switch for the bifrost sidecar proxy. When set to `0`, the route returns 503 with the `X-Bifrost-Killswitch` header and the operator is bounced to the TS path. Use to disable the sidecar without redeploying (tier-1 router incident, key rotation). |
|
||||
| `BIFROST_BASE_URL` | _(unset)_ | `src/app/api/v1/relay/chat/completions/bifrost/route.ts` | When set, the Bifrost sidecar proxy route forwards `/v1/chat/completions` traffic to this Go gateway instead of the TS relay handler. Unset → 503-with-fallback. Trailing slash is stripped. |
|
||||
| `BIFROST_PORT` | `8080` | `src/lib/services/bootstrap.ts` | Port the supervised Bifrost embedded service binds to (`127.0.0.1:<port>`) when OmniRoute manages the Bifrost sidecar lifecycle. Defaults to `8080`. |
|
||||
| `BIFROST_API_KEY` | _(unset)_ | `src/app/api/v1/relay/chat/completions/bifrost/route.ts` | API key for the Bifrost gateway (sent as `Authorization: Bearer ...`). If unset, the route expects the request to carry a valid OmniRoute API key; this key is for gateway-side auth only. |
|
||||
| `BIFROST_STREAMING_ENABLED` | `true` | `src/app/api/v1/relay/chat/completions/bifrost/route.ts` | When true, the Bifrost sidecar route streams responses back via SSE through the gateway rather than the TS streaming executor. Set to `0` to force non-streaming JSON responses through the gateway. |
|
||||
| `BIFROST_TIMEOUT_MS` | `30000` | `src/app/api/v1/relay/chat/completions/bifrost/route.ts` | Per-request timeout when proxying to the Bifrost gateway (ms). On timeout the route returns the TS relay path via the `X-Bifrost-Fallback` header. |
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
---
|
||||
title: "Provider Plugin Manifest"
|
||||
version: 3.8.42
|
||||
lastUpdated: 2026-07-01
|
||||
---
|
||||
|
||||
# Provider Plugin Manifest
|
||||
|
||||
`open-sse/config/providerPluginManifest.ts` defines the JSON-safe provider
|
||||
plugin contract. `open-sse/config/providerPluginManifestRegistry.ts` binds that
|
||||
contract to the current provider registry for sidecars such as Bifrost,
|
||||
CLIProxyAPI, or a future Go/Rust router. The TypeScript registry remains the
|
||||
source of truth, but sidecars can consume the manifest without importing
|
||||
executor code, OAuth defaults, headers, or process environment state.
|
||||
|
||||
The same manifest is available over HTTP at
|
||||
`GET /api/v1/provider-plugin-manifest` for sidecars that run out-of-process.
|
||||
|
||||
OmniRoute advertises that URL to Bifrost and CLIProxyAPI via the
|
||||
`X-OmniRoute-Provider-Manifest-Url` request header. Set
|
||||
`OMNIROUTE_PROVIDER_MANIFEST_URL` when the sidecar needs a public or container
|
||||
network URL instead of the local request origin.
|
||||
|
||||
## Goal
|
||||
|
||||
Move provider metadata toward a plugin contract so the hot request path can
|
||||
eventually be owned by a lower-latency sidecar while OmniRoute keeps the
|
||||
TypeScript route as the policy gate and fallback. The manifest is additive: it
|
||||
does not change request routing by itself.
|
||||
|
||||
## Contract
|
||||
|
||||
The manifest contains:
|
||||
|
||||
- provider id and alias
|
||||
- upstream format and executor name
|
||||
- auth type, auth header, and optional auth prefix
|
||||
- static endpoint metadata
|
||||
- sidecar eligibility and explicit reasons when a provider should stay on TS
|
||||
- JSON-safe model metadata such as context length, vision/reasoning flags, and
|
||||
unsupported params
|
||||
- capability tags including `apikey`, `oauth`, `custom-executor`,
|
||||
`passthrough-models`, `responses`, and `sidecar-candidate`
|
||||
|
||||
The manifest intentionally excludes:
|
||||
|
||||
- OAuth client secrets and default secret values
|
||||
- runtime environment resolution
|
||||
- request headers and public credential helpers
|
||||
- dynamic URL builders
|
||||
- executor functions
|
||||
- session pool internals
|
||||
|
||||
## Sidecar Use
|
||||
|
||||
Sidecars should treat `sidecar.eligible` as a conservative candidate signal, not
|
||||
as an unconditional routing decision. The first import target should be
|
||||
API-key, static-endpoint providers using the default executor. Providers with
|
||||
custom web executors, OAuth/session flows, dynamic URL builders, or pool config
|
||||
stay on the TypeScript fallback path until a sidecar implements equivalent
|
||||
behavior and telemetry proves parity.
|
||||
|
||||
Suggested migration phases:
|
||||
|
||||
1. Generate and validate the provider plugin manifest from the TS registry.
|
||||
2. Teach Bifrost or CLIProxyAPI to import the manifest for API-key/static
|
||||
providers.
|
||||
3. Route eligible providers through the sidecar behind `OMNIROUTE_RELAY_BACKEND`
|
||||
while keeping TS fallback enabled.
|
||||
4. Promote providers only when success rate, p99 latency, streaming behavior,
|
||||
and unsupported-param handling match the TS path.
|
||||
5. Add sidecar-native plugins for custom executors one provider family at a
|
||||
time.
|
||||
|
||||
## Why Not Embed Providers Directly In Next
|
||||
|
||||
The Next frontend should not own provider execution. It should call the API
|
||||
boundary. The backend can then decide whether to use the TypeScript executor,
|
||||
Bifrost, CLIProxyAPI, or a future native sidecar. This keeps request signing,
|
||||
allowlist checks, DB policy, and fallback behavior centralized before any
|
||||
sidecar handoff.
|
||||
@@ -75,10 +75,3 @@ For sustained high RPM/RPS and strict success SLO:
|
||||
- `BIFROST_ENABLED=1`
|
||||
- Keep API keys, allowlist, sanitizer, and rate-limit checks enabled in route handlers (they always run before downstream forwarding).
|
||||
- Export fallback metrics from your reverse proxy and request logs so sidecar outages are visible within one minute.
|
||||
|
||||
## Provider plugin contract
|
||||
|
||||
Sidecars should import provider metadata through the JSON-safe provider plugin
|
||||
manifest instead of depending on TypeScript executor internals. See
|
||||
[Provider Plugin Manifest](./PROVIDER_PLUGIN_MANIFEST.md) for the sidecar
|
||||
eligibility contract and migration phases.
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
---
|
||||
title: CORS Configuration & Security
|
||||
---
|
||||
|
||||
# CORS Configuration & Security
|
||||
|
||||
OmniRoute controls which **browser origins** may read cross-origin responses
|
||||
from a single, centralized allowlist. The model is **fail-closed by default**:
|
||||
no origin is allowed until you opt one in. This page documents how the allowlist
|
||||
resolves, what `CORS_ALLOW_ALL=true` actually exposes (and, importantly, what it
|
||||
does **not**), how to configure dev vs production safely, and the runtime warning
|
||||
the dashboard shows when a wildcard is live.
|
||||
|
||||
**Source of truth:** `src/server/cors/origins.ts` (`resolveAllowedOrigin`,
|
||||
`applyCorsHeaders`, `getCorsStatus`). The allowlist is applied once, in the
|
||||
middleware (`src/server/authz/pipeline.ts`) — per-route handlers do not set
|
||||
`Access-Control-Allow-Origin` themselves.
|
||||
|
||||
## How an origin is resolved
|
||||
|
||||
For each request the middleware computes the `Access-Control-Allow-Origin` value
|
||||
in this order:
|
||||
|
||||
1. **`CORS_ALLOW_ALL=true`** (or the legacy `CORS_ORIGIN=*`) → echo the caller's
|
||||
`Origin` back (or `*` when there is no `Origin` header), with `Vary: Origin`
|
||||
so caches stay correct.
|
||||
2. Otherwise, the request `Origin` is normalized (lower-cased, trailing slash
|
||||
stripped) and matched against the **merged allowlist**:
|
||||
- env **`CORS_ALLOWED_ORIGINS`** — comma-separated list, and
|
||||
- the runtime **`corsOrigins`** setting (Dashboard → Security → _CORS Allowed
|
||||
Origins_), injected via `setRuntimeAllowedOrigins()` from
|
||||
`src/lib/config/runtimeSettings.ts`.
|
||||
3. No match → **no `Access-Control-Allow-Origin` header is emitted**. The browser
|
||||
blocks the cross-origin read. This is the intended fail-closed default.
|
||||
|
||||
| Env var | Meaning |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------ |
|
||||
| `CORS_ALLOWED_ORIGINS` | CSV of exact origins to allow (recommended). |
|
||||
| `CORS_ALLOW_ALL` | `true`/`1` → echo any origin (wildcard). Dev only. |
|
||||
| `CORS_ORIGIN` | Legacy. `*` behaves like `CORS_ALLOW_ALL`; a single value is added to the allowlist. |
|
||||
|
||||
## Threat model — what `CORS_ALLOW_ALL=true` really exposes
|
||||
|
||||
The generic OWASP warning ("wildcard CORS = any site can call your API") is worth
|
||||
taking seriously, but OmniRoute's exposure is **narrower than the generic case**,
|
||||
because of one concrete implementation fact:
|
||||
|
||||
> **The central `applyCorsHeaders()` never emits
|
||||
> `Access-Control-Allow-Credentials`.** A browser will not expose a _credentialed_
|
||||
> (cookie-bearing) cross-origin response unless the server sends
|
||||
> `Access-Control-Allow-Credentials: true`. OmniRoute's shared CORS path never
|
||||
> does.
|
||||
|
||||
What that means per surface, even with `CORS_ALLOW_ALL=true`:
|
||||
|
||||
| Surface | Auth mechanism | Effect of wildcard CORS |
|
||||
| ----------------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Dashboard / MANAGEMENT `/api/*` | Cookie session | Origin is echoed, but **without `Allow-Credentials`** the browser **blocks** the credentialed read. A malicious cross-origin site **cannot read** your authenticated dashboard responses, and the session cookie is not exposed. |
|
||||
| Client API `/v1/*`, `/v1beta/*` | Bearer / `x-api-key` header | Already permissive **by design** (`relaxForTokenAuth`): browsers never auto-attach `Authorization`/`x-api-key`, so an attacker's page cannot supply your key. `CORS_ALLOW_ALL` does not widen this. |
|
||||
| Public read-only (`/api/health`, …) | None | Non-sensitive; wildcard is harmless. |
|
||||
|
||||
So the **residual** exposure of `CORS_ALLOW_ALL=true` is limited to: (a)
|
||||
non-credentialed cross-origin **reads** of already-unauthenticated data, and (b)
|
||||
letting CORS **preflight pass** on management routes — which still require auth
|
||||
that a cross-origin page cannot provide. It is **not** a session-hijack or
|
||||
credential-theft vector on the shared CORS path.
|
||||
|
||||
### One genuine exception — `/api/v1/agents/`
|
||||
|
||||
The Cloud-Agent routes (`/api/v1/agents/{health,credentials,tasks,tasks/[id]}`) set
|
||||
their **own** CORS headers
|
||||
(`src/lib/cloudAgent/api.ts`, `getCloudAgentCorsHeaders`) and **do** emit
|
||||
`Access-Control-Allow-Origin: <origin>|*` together with
|
||||
`Access-Control-Allow-Credentials: true`. This is the single surface where
|
||||
origin-echo and credentials coexist, and it is **independent of
|
||||
`CORS_ALLOW_ALL`**. These routes are management-authenticated
|
||||
(`requireManagementAuth`); operators who expose the dashboard off-host should be
|
||||
aware that this is the one place a cross-origin credentialed read is permitted by
|
||||
the response headers. Tightening it to an explicit allowlist is tracked
|
||||
separately from this CORS guidance.
|
||||
|
||||
## Production checklist
|
||||
|
||||
- **Never set `CORS_ALLOW_ALL=true` in production.** Leave it unset.
|
||||
- Set an **explicit** origin list — either the env var or the Security-tab field:
|
||||
|
||||
```bash
|
||||
CORS_ALLOWED_ORIGINS="https://app.example.com, https://admin.example.com"
|
||||
```
|
||||
|
||||
- If OmniRoute runs behind a reverse proxy / tunnel (nginx, Caddy, Cloudflare
|
||||
Tunnel, Tailscale), CORS is **not** your only control — the loopback route
|
||||
guard still protects spawn-capable routes (see
|
||||
[ROUTE_GUARD_TIERS](./ROUTE_GUARD_TIERS.md)). Do not forge
|
||||
`X-Forwarded-For: 127.0.0.1` to "fix" a 403; that re-opens the RCE class the
|
||||
route guard closes.
|
||||
- Confirm the runtime state: the dashboard shows a **persistent amber banner**
|
||||
under Dashboard → Security → Authorization Inventory whenever
|
||||
`CORS_ALLOW_ALL=true` is live, and `/api/settings/authz-inventory` returns a
|
||||
`cors: { allowAll, allowedOrigins }` envelope monitoring tools can poll.
|
||||
|
||||
## Development convenience — allow specific local origins
|
||||
|
||||
You rarely need the wildcard even in dev. Allow just the dev servers you use:
|
||||
|
||||
```bash
|
||||
# Vite (5173) + Next.js (3000) dev servers calling a local OmniRoute
|
||||
CORS_ALLOWED_ORIGINS="http://localhost:5173, http://localhost:3000"
|
||||
```
|
||||
|
||||
Origins are matched case-insensitively with the trailing slash ignored, so
|
||||
`http://localhost:3000` and `http://localhost:3000/` are equivalent. The same CSV
|
||||
can be set at runtime in **Dashboard → Security → CORS Allowed Origins** without a
|
||||
restart.
|
||||
|
||||
## API keys vs cookie sessions
|
||||
|
||||
- **Bearer / `x-api-key` (the `/v1/*` inference surface):** browsers never attach
|
||||
these automatically. CORS is not a meaningful barrier here — the API key is the
|
||||
barrier — which is why that surface is intentionally permissive so browser and
|
||||
Electron clients can read responses they are already entitled to.
|
||||
- **Cookie session (the dashboard):** protected by the fail-closed default **and**
|
||||
by the absence of `Access-Control-Allow-Credentials` on the shared path. Keep
|
||||
management/dashboard origins out of any permissive config; they must stay exactly
|
||||
fail-closed.
|
||||
|
||||
## Example: reverse proxy in front of OmniRoute
|
||||
|
||||
CORS is enforced by OmniRoute itself, so the proxy generally should **not** add or
|
||||
rewrite `Access-Control-*` headers (double headers break browsers). Terminate TLS
|
||||
and forward — let OmniRoute answer preflight:
|
||||
|
||||
```nginx
|
||||
# nginx — forward to OmniRoute; do NOT inject Access-Control-* here
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:20128;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# Do NOT set X-Forwarded-For to 127.0.0.1 — it defeats the loopback route guard.
|
||||
}
|
||||
```
|
||||
|
||||
Set the allowed browser origins in OmniRoute (`CORS_ALLOWED_ORIGINS` or the
|
||||
Security tab), not in the proxy.
|
||||
|
||||
## Source files
|
||||
|
||||
| Concern | File |
|
||||
| ----------------------------------------------- | -------------------------------------------------------------------- |
|
||||
| Allowlist resolution + `getCorsStatus()` | `src/server/cors/origins.ts` |
|
||||
| Middleware application (single source of truth) | `src/server/authz/pipeline.ts` |
|
||||
| Settings → runtime origin injection | `src/lib/config/runtimeSettings.ts` |
|
||||
| Runtime status for the dashboard | `src/app/api/settings/authz-inventory/route.ts` |
|
||||
| Dashboard warning banner | `src/app/(dashboard)/dashboard/settings/components/AuthzSection.tsx` |
|
||||
| CORS Allowed Origins field | `src/app/(dashboard)/dashboard/settings/components/SecurityTab.tsx` |
|
||||
| Cloud-Agent per-route CORS (the exception) | `src/lib/cloudAgent/api.ts` |
|
||||
|
||||
## See also
|
||||
|
||||
- [Route Guard Tiers](./ROUTE_GUARD_TIERS.md) — loopback enforcement for
|
||||
spawn-capable routes (a separate, complementary control).
|
||||
- [Authorization Guide](../architecture/AUTHZ_GUIDE.md) — the full auth pipeline.
|
||||
Reference in New Issue
Block a user