docs(providers): register providers removed at their operator's request and guard against reintroduction (#12478)

Adds docs/reference/REMOVED_PROVIDERS.md (policy + register: puter #10210,
the keyless provider removed in #12440), links it from the docs index and
AGENTS.md's provider checklist, and adds a regression test that fails if any
registered id, alias or domain shows up again in the provider catalogs, the
executor map or the registry/executor sources.

Co-authored-by: Markus Hartung <diegosouzapw@users.noreply.github.com>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-09-02 15:11:08 -03:00
committed by GitHub
parent 84b345d9c0
commit bf0d902dfc
5 changed files with 170 additions and 0 deletions

View File

@@ -343,6 +343,7 @@ Documentation must describe verified behavior, not plausible behavior.
### Adding a New Provider
0. Check `docs/reference/REMOVED_PROVIDERS.md` first — providers removed at their operator's request must never be reintroduced (guarded by `tests/unit/removed-providers-blocklist.test.ts`)
1. Register in `src/shared/constants/providers.ts` (Zod-validated at load)
2. Add executor in `open-sse/executors/` if custom logic needed (extend `BaseExecutor`)
3. Add translator in `open-sse/translator/` if non-OpenAI format

View File

@@ -79,6 +79,7 @@ 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).
- [REMOVED_PROVIDERS.md](reference/REMOVED_PROVIDERS.md) — providers removed at their operator's request; never reintroduce without written permission.
- [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.

View File

@@ -0,0 +1,60 @@
# Providers removed at their operator's request
Some services were integrated into OmniRoute and later removed because the people who run
them asked for it. This page is the durable record of those removals. Its only purpose is to
keep them from coming back by accident: a contributor who finds an old fork, a cached npm
tarball, an archived issue or a "restore provider X" request needs one place that says **do
not reintroduce**.
This page is **not** a list of dead or discontinued services. Those are tracked in
[`FREE_TIERS.md`](FREE_TIERS.md) ("Removed / no free tier") and can come back if the service
does. The entries below can only come back with written permission from the operator named in
the request, and that permission must be linked from the entry.
## Policy
1. **A takedown request from a service operator is honored, not negotiated.** OmniRoute is
not affiliated with any upstream service. When the operator of a service asks for the
integration to go, it goes, whether the integration used an official API or not.
2. **"Removed" means every surface OmniRoute controls.** Executor, registry entry, provider
id and alias, model list, endpoints, environment variables, icon, dashboard cards, the
generated provider reference, `FREE_TIERS.md`, the environment reference, README counts,
`llm.txt` mirrors, dedicated tests and golden snapshots, code comments, CHANGELOG bullets
(with a ledgered reconciliation, see `config/release/changelog-reconciliations.json`),
GitHub Releases notes, the wiki, and the GitHub issues, discussions and pull requests whose
subject was that provider (issues and discussions deleted; pull requests retitled, their
description replaced and the thread locked, because GitHub cannot delete pull requests).
3. **Never reintroduce an entry on this page without written permission.** That includes
adding the id or alias back to any provider catalog, adding the domains to an executor,
accepting a contributor PR that "restores" it, adding it to the free-model catalog, or
documenting a manual way to reach it through OmniRoute. Close such PRs and issues with a
link to this page.
4. **Keep the entry minimal.** Record only what a reviewer needs to recognize a
reintroduction: identifiers, domains, dates and the pull request that did the removal.
Do not describe how the integration worked.
5. **The regression guard is `tests/unit/removed-providers-blocklist.test.ts`.** It fails when
any identifier or domain below shows up again in the provider catalogs, the executor map or
the provider registry sources. Add the new identifiers to that test in the same PR that
adds a row here.
## Register
| Removed on | Provider id | Alias | Domains | Requested by | Removal PR | Notes |
| ---------- | ----------- | ------ | --------------------------------------- | ------------------------------------ | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| 2026-08-12 | `puter` | `pu` | `puter.com` | Puter's owner (Nariman Jelveh) | [#10210](https://github.com/diegosouzapw/OmniRoute/pull/10210) | API-key provider. Migration `152_remove_puter_provider.sql` cleans stored config. |
| 2026-09-02 | `theoldllm` | `tllm` | `theoldllm.com`, `theoldllm.vercel.app` | The service operator (support email) | [#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440) | Keyless provider. Written request received 2026-08-30. Dedicated issues and discussion deleted, PRs retitled. |
## Adding an entry
When a new takedown request arrives:
1. Confirm the request comes from the operator of the service (their support address or a
domain they control), and keep the message privately.
2. Remove the integration following the checklist in policy item 2. Use
[#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440) as the reference for a
keyless provider and [#10210](https://github.com/diegosouzapw/OmniRoute/pull/10210) for an
API-key provider with stored connections (add a migration).
3. Add one row to the table above and the identifiers to
`tests/unit/removed-providers-blocklist.test.ts`, in the same PR.
4. Reply to the operator once the PR is merged, listing what was removed and what OmniRoute
cannot change (already-published npm and Docker versions, git history, third-party forks).

View File

@@ -8,6 +8,7 @@
"FREE_TIERS",
"FREE_PROXIES_API",
"PROVIDER_REFERENCE",
"REMOVED_PROVIDERS",
"PROVIDER_PLUGIN_MANIFEST",
"RELAY_BACKEND_STRATEGY",
"RELAY_TROUBLESHOOTING"

View File

@@ -0,0 +1,107 @@
/**
* Regression guard for docs/reference/REMOVED_PROVIDERS.md.
*
* Providers removed at their operator's request must never come back: not in the
* provider catalogs, not in the executor map, not in the registry sources and not
* as an upstream domain in any executor. Keep this list in sync with the table in
* the doc; add the identifiers of a new takedown here in the same PR.
*/
import { test } from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts");
const { getProviderById, getProviderByAlias } =
await import("../../src/shared/constants/providers.ts");
const { hasSpecializedExecutor } = await import("../../open-sse/executors/index.ts");
const { FREE_MODEL_BUDGETS } = await import("../../open-sse/config/freeModelCatalog.data.ts");
interface RemovedProvider {
id: string;
alias: string;
domains: string[];
removalPr: number;
}
export const REMOVED_PROVIDERS: readonly RemovedProvider[] = [
{ id: "puter", alias: "pu", domains: ["puter.com"], removalPr: 10210 },
{
id: "theoldllm",
alias: "tllm",
domains: ["theoldllm.com", "theoldllm.vercel.app"],
removalPr: 12440,
},
];
// Source trees where a reintroduction would land. Scanned for ids, aliases and domains.
const SCANNED_DIRS = [
"open-sse/config/providers",
"open-sse/executors",
"src/shared/constants/providers",
];
function walk(dir: string, out: string[] = []): string[] {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) walk(full, out);
else if (/\.(ts|tsx|mts|js|mjs|json)$/.test(entry.name)) out.push(full);
}
return out;
}
const ROOT = process.cwd();
const scannedFiles = SCANNED_DIRS.flatMap((dir) => walk(path.join(ROOT, dir)));
for (const removed of REMOVED_PROVIDERS) {
test(`removed provider "${removed.id}" (PR #${removed.removalPr}) stays out of the chat registry`, () => {
assert.equal(REGISTRY[removed.id], undefined, `${removed.id} must not be in REGISTRY`);
assert.equal(REGISTRY[removed.alias], undefined, `${removed.alias} must not be in REGISTRY`);
});
test(`removed provider "${removed.id}" stays out of the provider catalogs`, () => {
assert.equal(getProviderById(removed.id), undefined, `${removed.id} must not be a provider`);
assert.equal(
getProviderByAlias(removed.alias),
null,
`alias ${removed.alias} must not be reused by any provider`
);
});
test(`removed provider "${removed.id}" has no executor (id or alias)`, () => {
assert.equal(hasSpecializedExecutor(removed.id), false);
assert.equal(hasSpecializedExecutor(removed.alias), false);
});
test(`removed provider "${removed.id}" has no free-model catalog entries`, () => {
assert.deepEqual(
FREE_MODEL_BUDGETS.filter((b) => b.provider === removed.id),
[],
`${removed.id} must not appear in FREE_MODEL_BUDGETS`
);
});
test(`removed provider "${removed.id}" identifiers and domains are absent from registry/executor sources`, () => {
const needles = [`"${removed.id}"`, `"${removed.alias}"`, ...removed.domains];
const offenders: string[] = [];
for (const file of scannedFiles) {
const text = fs.readFileSync(file, "utf8");
for (const needle of needles) {
if (text.includes(needle)) offenders.push(`${path.relative(ROOT, file)} :: ${needle}`);
}
}
assert.deepEqual(
offenders,
[],
`reintroduction of "${removed.id}" detected — see docs/reference/REMOVED_PROVIDERS.md`
);
});
}
test("the REMOVED_PROVIDERS doc lists every guarded id", () => {
const doc = fs.readFileSync(path.join(ROOT, "docs/reference/REMOVED_PROVIDERS.md"), "utf8");
for (const removed of REMOVED_PROVIDERS) {
assert.ok(doc.includes(`\`${removed.id}\``), `${removed.id} must have a row in the doc`);
assert.ok(doc.includes(`#${removed.removalPr}`), `PR #${removed.removalPr} must be linked`);
}
});