| Field | Value |
|---|---|
| RFC | 0108 |
| Title | Self-hosted / OpenAI-compatible provider-class advertisement — aiProviders.selfHosted[], the capability-non-inference rule, and the endpoint-non-disclosure invariant |
| Status | Accepted |
| Author(s) | David Tufts (@davidscotttufts) |
| Created | 2026-06-23 |
| Updated | 2026-06-24 (Active → Accepted — reference-host (openwop-app, ADR 0121, PR #725 merged) conformance-harness witness, single-witness bootstrap steward waiver; see Status history). 2026-06-24 (Draft → Active — bootstrap-phase steward waiver, 7-day comment window waived) |
| Affects | spec/v1/capabilities.md (§aiProviders — adds the selfHosted[] field + the self-hosted advertisement rules) · spec/v1/host-capabilities.md (cross-reference) · schemas/capabilities.schema.json (adds optional aiProviders.selfHosted) · SECURITY/invariants.yaml (adds self-hosted-endpoint-no-disclosure) · SECURITY/threat-model-secret-leakage.md (cross-reference) · ≥1 new conformance scenario · INTEROP-MATRIX.md · CHANGELOG |
| Compatibility | additive per COMPATIBILITY.md §2.1 |
| Supersedes | — |
| Superseded by | — |
Summary
Adds an optional aiProviders.selfHosted: string[] advertisement (a subset of aiProviders.supported, mirroring the existing byok[] shape) that marks which advertised provider ids are operator- or tenant-configured OpenAI-compatible endpoints (Ollama, vLLM, LM Studio, or any /v1/chat/completions-compatible server) rather than a host-managed connection to a known public vendor. It pins three normative rules a host MUST follow to advertise such a provider honestly: (1) advertise it only when a real configured + reachable endpoint backs it (the truthful-advertisement honesty rule, enforced by OPENWOP_REQUIRE_BEHAVIOR); (2) never disclose the endpoint's network location (base-URL / host / port) on any wire surface — operator-private infrastructure; (3) a client MUST NOT infer model capabilities from a self-hosted provider id — capability is governed solely by the host's existing RFC 0031 modelCapabilities.advertised[] and RFC 0091 modality advertisement. It is the wire-gate that unblocks openwop-app ADR 0121 (local / OpenAI-compatible model provider support).
Motivation
OpenWOP hosts today advertise the AI providers their proxy can route to in capabilities.aiProviders.supported[] (an array of provider-id strings) and the subset that permits BYOK in aiProviders.byok[] (schemas/capabilities.schema.json §aiProviders, lines 749–868). The conventional provider vocabulary (RFC 0067 §C, advisory, not a closed set) already lists ollama and vllm, and supported[] tolerates arbitrary ids ("clients MUST tolerate unknown ids"). So a host can already put ollama in supported[].
What the wire cannot express today is the distinction that makes such an advertisement honest and safe:
1. Managed-cloud vs operator-configured endpoint is invisible. A client reading supported: ["anthropic", "ollama"] cannot tell that anthropic is a host-managed connection to a known public vendor while ollama is an OpenAI-compatible endpoint someone stood up at a private base-URL. These are materially different trust, capability, and key-custody surfaces. 2. No honesty rule binds the claim to a real endpoint. A host could list ollama with no endpoint configured; a client (or OPENWOP_REQUIRE_BEHAVIOR=true conformance) has no normative hook that says "advertise this class only when a reachable endpoint actually backs it." This is the exact dishonest-capability failure mode RFC 0031 §C ("truthful advertisement") and capabilities.md ("advertise what you implement") guard against for other surfaces, but it has never been stated for endpoint-backed providers. 3. Capability inference from a compat id is unsafe. For a known vendor a client may reasonably assume a vendor capability set. For an opaque OpenAI-compatible endpoint it must not — the operator may run any model, of any capability, behind that id. There is no rule today forbidding that inference. 4. The endpoint URL is operator-private infrastructure with no non-disclosure rule. A tenant's internal endpoint (e.g. https://vllm.internal:8000/v1) leaks network topology and enables SSRF reconnaissance if it surfaces in the capabilities document, a run.* event, an error envelope, or a debug bundle. The corpus has no SSRF/egress invariant; the wire concern here is narrower and belongs on the wire: non-disclosure of the endpoint location.
Every surveyed reference app ships this class of provider — LibreChat (api/app/clients/OllamaClient.js + the custom-endpoint path), Jan (RemoteOAIEngine), AnythingLLM (server/utils/AiProviders/{ollama,lmStudio,localai,...}), Open WebUI (routers/ollama.py + OpenAI-compatible routers/openai.py) — and openwop-app ADR 0121 wants to. The host plumbing (a compat dispatch case, a Connection-stored base-URL, a BYOK key) is non-normative and rides Accepted RFCs (0046 credentials, 0007 dispatch, 0031 capabilities). The advertisement is the normative gate, and it is the right place to put rules (1)–(4) so every host expresses this class the same honest way.
The spec is the right place because the advertisement is read cross-host by clients and the conformance suite; an ad-hoc per-host convention would re-introduce exactly the silent-incompatibility and dishonest-claim problems the capability surface exists to prevent.
Proposal
§A — aiProviders.selfHosted[] (additive optional field)
Add one optional field to the aiProviders object in schemas/capabilities.schema.json:
"aiProviders": {
"type": "object",
"description": "Optional v1 companion to `secrets`. Advertises which AI providers the host's AI-proxy can route to and which permit BYOK.",
"properties": {
"supported": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true, "description": "Provider ids the host's AI-proxy can route to. …" },
"byok": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true, "description": "Subset of `supported` for which BYOK is permitted. …" },
+ "selfHosted": {
+ "type": "array",
+ "items": { "type": "string", "minLength": 1 },
+ "uniqueItems": true,
+ "description": "Subset of `supported` whose entries are operator- or tenant-configured OpenAI-compatible endpoints (e.g. an Ollama / vLLM / LM Studio / any `/v1/chat/completions`-compatible server), as opposed to a host-managed connection to a known public vendor. Each entry MUST also appear in `supported`. An entry MAY also appear in `byok` (the endpoint requires a client/tenant-supplied key) or be absent from it (the endpoint needs no key, e.g. a default Ollama). The provider id is an OPAQUE label chosen by the host; it MUST NOT encode the endpoint's network location (per RFC 0108 §A.2). A client MUST NOT infer model capabilities from a `selfHosted` id (per RFC 0108 §B)."
+ },
"…": "… (authModes, policies, speechSynthesis, input, realtimeVoice, maxInlineMediaBytes unchanged)"
},
"additionalProperties": false
}
Add to spec/v1/capabilities.md §aiProviders, after the byok[] paragraph:
A.1 — Subset constraint. Every entry of
aiProviders.selfHosted[]MUST also appear inaiProviders.supported[]. AselfHostedentry MAY additionally appear inaiProviders.byok[](the endpoint requires a tenant-supplied key) or not (the endpoint needs no key).selfHostedis OPTIONAL; an absent or empty array means the host advertises no self-hosted/compatible endpoints (itssupported[]entries are all host-managed or BYOK-cloud connections — today's behavior).A.2 — Truthful advertisement (normative). A host MUST list a provider id in
aiProviders.selfHosted[]only when the host can route to at least one configured, reachable OpenAI-compatible endpoint for that id within the advertising scope. Listing a self-hosted provider with no backing endpoint is a dishonest capability claim percapabilities.md§"Truthful advertisement" and is non-conformant;OPENWOP_REQUIRE_BEHAVIOR=trueMUST fail it. (The capabilities document is host-scoped: a host MAY advertise the class when at least one tenant in the advertising scope has a configured endpoint; per-tenant existence is not separately advertised.)A.3 — Endpoint non-disclosure (normative). The provider id in
selfHosted[]is an opaque host-chosen label. A host MUST NOT encode the endpoint's network location (scheme, host, port, path, or base-URL) in the provider id, and MUST NOT disclose that location on any wire surface (the capabilities document, anyrun.*event payload, error envelopes, the debug bundle, exports, or replay state). See theself-hosted-endpoint-no-disclosureSECURITY invariant (§D).
§B — Capability non-inference for self-hosted providers (normative)
Add to spec/v1/capabilities.md §aiProviders (and cross-reference from host-capabilities.md §"Model-capability declarations"):
For a provider id present in
aiProviders.selfHosted[], a client MUST NOT infer model capabilities (e.g.structured-output,discriminator-enum,function-calling,long-context,reasoningper RFC 0031 §C, or input modalities per RFC 0091) from the provider id or from any known-vendor capability mapping. The only authoritative source of a self-hosted provider's capabilities is what the host actually advertises and gates on:capabilities.modelCapabilities.advertised[](RFC 0031 §E) andaiProviders.input.modalities(RFC 0091). A self-hosted endpoint whose capabilities the host does not advertise is treated as text-only; the host MUST refuse a request for an unadvertised capability or modality per the RFC 0031 §B model-capability gate (capability_not_provided), exactly as for any other provider. This rule prevents a client from assuming, e.g., that aselfHostedid namedollamasupports vision merely because some public deployment of that engine does.
How a host derives a self-hosted endpoint's capabilities — a static declaration captured at configuration time, or a runtime probe of the endpoint — is host-internal and out of scope (see Unresolved question 2). The wire surface is the advertised result, identical to RFC 0031 §C ("the protocol does not normate how the host derives the mapping").
§C — Key custody (carry-forward, non-normative restatement)
When a selfHosted endpoint requires a key, that key is a BYOK credential governed by RFC 0046 (transmitted only as an opaque CredentialReference, resolved into the dispatch path, never in plaintext on the wire) and the credential-payload-redaction / SR-1 (secret-leakage-eventlog-payload) invariants. This RFC adds no new credential surface; it relies on RFC 0046 verbatim. The endpoint base-URL is configuration, not a credential — but it is operator-private infrastructure, which §A.3 + §D govern with their own non-disclosure rule.
§D — SECURITY invariant: self-hosted-endpoint-no-disclosure
Add to SECURITY/invariants.yaml:
- id: self-hosted-endpoint-no-disclosure
tier: protocol
severity: medium
threat_model: SECURITY/threat-model-secret-leakage.md
tests:
- conformance/src/scenarios/aiproviders-selfhosted-shape.test.ts
note: |
RFC 0108 §A.3: the network location (scheme, host, port, path, base-URL)
of an `aiProviders.selfHosted[]` endpoint is operator-private
infrastructure and MUST NOT appear on any wire surface — the capabilities
document, any run.* event payload, error envelopes, the debug bundle,
exports, or replay state. The provider id is an opaque host-chosen label
and MUST NOT encode the endpoint URL. Disclosing the endpoint leaks
internal network topology and enables SSRF reconnaissance against the
operator's network. Distinct from credential-payload-redaction (RFC 0046):
the endpoint URL is not a credential, but it is private infrastructure.
Severity is medium (not critical): the leaked datum is a network location, not key material; the operator chose to expose an OpenAI-compatible endpoint to their host; and the principal harm (SSRF reconnaissance / topology disclosure) is a step toward, not itself, compromise. It pairs with the §E shape scenario, which statically asserts that no selfHosted[] id is URL-shaped.
§E — Conformance
One new scenario lands with this RFC; a second behavioral scenario is sketched for the Active→Accepted gate.
aiproviders-selfhosted-shape.test.ts(server-free, always-run shape gate; soft-skips whenaiProviders.selfHostedis absent percoverage.md§"Capability-gated scenarios"). Asserts, for the host's published capabilities document:
1. aiProviders.selfHosted (when present) is a string[] with uniqueItems; 2. every entry is a subset of aiProviders.supported (§A.1); 3. no entry is URL-shaped — no entry contains ://, a bare host:port, or a leading / path (a cheap static enforcement of the §A.3 / §D non-disclosure rule that the opaque id does not encode the endpoint). Uses driver.describe('capabilities.md §aiProviders', 'selfHosted is a non-URL subset of supported') so failures cite the requirement. <1s, no LLM.
aiproviders-selfhosted-honesty.test.ts(Active→Accepted gate; gated onaiProviders.selfHosted.length > 0+ the existing capabilities fetch). Host-attested behavioral check: drives one chat/dispatch against aselfHostedprovider id and asserts (1) the dispatch succeeds or fails with a transport error from a real endpoint (not a "no provider configured"capability_not_provided, which would prove the §A.2 dishonest-advertisement violation), and (2) neither the resultingrun.*events nor any error payload contains a URL-shaped string matching the host's (test-supplied, out-of-band) endpoint location (the §D disclosure check). Gated; soft-skips for hosts that don't advertise the class.
The behaviorGate helper gains a requireSelfHostedProviders() predicate, mirroring requireModelCapabilities() (RFC 0031).
Compatibility
Additive per COMPATIBILITY.md §2.1:
- New optional field in a server-emitted shape (the capabilities document).
aiProvidersisadditionalProperties: false, soselfHostedis added to itsproperties(the additive schema change); server-emitted shapes are open by convention, so v1.x hosts that don't emit it remain conformant, and clients that don't read it see today's behavior (aselfHostedprovider simply appears as an opaque entry insupported[], exactly as a host could already listollama). - No existing field changes.
supported[],byok[],authModes,policiesare untouched. - No event-type, endpoint, or error-code changes. §B reuses RFC 0031's
capability_not_provided; §C reuses RFC 0046 verbatim. - New
MUSTs apply only to hosts that advertise the new field. §A.2/§A.3/§B/§D bind a host once it lists a provider inselfHosted[]. A host that never advertises the class is unaffected. - New SECURITY invariant is additive (a new MUST-NOT scoped to the new field), gated by the §E scenario.
Forward-compatibility guarantees: a client on an older spix that ignores selfHosted[] treats every supported[] entry as it does today (managed or BYOK per byok[]); a host that omits selfHosted[] makes no new claim. There is no migration; nothing existing must change.
Conformance
- Existing coverage:
aiproviders-input-shape.test.ts(RFC 0091 modality shape),aiproviders-speechsynth-shape.test.ts(RFC 0105),providerPolicyEnforcement.test.ts(RFC 0067 policies),provider-usage.test.ts(RFC 0026) — all capability-gated on anaiProviders.*sub-field. This RFC follows the same server-free-shape-first pattern. - New scenarios: §E above (
aiproviders-selfhosted-shape.test.tsat Draft→Active;aiproviders-selfhosted-honesty.test.tsat Active→Accepted). - Capability gating: both soft-skip (SKIPPED, not FAILED) when
aiProviders.selfHostedis absent/empty, percoverage.md.
Alternatives considered
1. Do nothing — rely on the advisory vocabulary. supported[] already tolerates ollama/vllm. Rejected: it leaves the four motivating gaps open — managed vs self-hosted is invisible, no honesty rule, no capability-inference ban, no endpoint-non-disclosure invariant. A host listing ollama today is making an ambiguous, ungoverned claim; that is exactly the dishonest-capability failure the surface exists to prevent. 2. A richer aiProviders.providerClasses map ({ "<id>": { class: "managed-cloud" | "byok-cloud" | "byok-self-hosted", capabilitySource: "advertised" | "probe" } }). Deferred (Unresolved question 1). A boolean-style selfHosted[] subset mirrors the existing byok[] minimalism and closes the gate; the richer taxonomy is itself additive and can land later without breaking selfHosted[] (a class map would make selfHosted derivable). Shipping the map now over-specifies a taxonomy before any host needs the byok-cloud vs managed-cloud distinction on the wire. 3. A new top-level capability block (capabilities.selfHostedProviders) separate from aiProviders. Rejected — this is intrinsically an aiProviders refinement (a subset-of-supported classifier); a sibling block fragments the provider surface and forces clients to reconcile two provider lists. 4. Define a normative SSRF / egress requirement on the wire (the host MUST SSRF-guard the configured base-URL). Rejected — egress enforcement is host-internal (CONTRIBUTING.md §"What's in scope" excludes internal data structures + implementation); the corpus has no egress invariant for good reason. The wire-observable concern is non-disclosure of the endpoint (§A.3/§D), not how the host fetches it. SSRF-guarding the configured endpoint remains the host's responsibility (openwop-app ADR 0121 §egress; the LibreChat #13919 lesson) and is called out as an implementation note, not normated here. 5. "do nothing" / defer to per-host READMEs. Rejected — cross-host clients and the conformance suite read the capabilities document, not host READMEs; an unspecified convention reproduces silent incompatibility.
Unresolved questions
1. Flag vs taxonomy. Ship the minimal selfHosted[] subset (this RFC) or the richer providerClasses map (managed-cloud / byok-cloud / byok-self-hosted) now? Recommendation: flag now; promote to a map additively when a host needs the managed-vs-byok-cloud distinction on the wire. (Blocks nothing — the map is a future additive bump.) 2. Declared vs probed capability source. Should the wire advertise how a self-hosted provider's capabilities were derived (operator declaration vs runtime probe)? Recommendation: no — capability advertisement is RFC 0031's job and the source is host-internal (RFC 0031 §C precedent); a client cares about the advertised result, not its provenance. (openwop-app ADR 0121 OQ-2.) 3. Native-protocol vs OpenAI-wire endpoints. A few engines (e.g. Ollama's native /api/) speak a non-OpenAI protocol. Does the wire need to distinguish "OpenAI-compatible" from "native"? Recommendation: no — the provider id + the host's modelCapabilities/modality advertisement fully describe what a client may send; the transport is host-internal. The field name says "OpenAI-compatible" as the common case but the rules are protocol-agnostic. 4. Loopback vs internal-network self-hosted. ADR 0121 OQ-3 distinguishes true-local (http://localhost:11434) from internal-network endpoints for the host's SSRF policy. Does the wire need to express that? Recommendation: no — that is an egress-policy detail (host-internal, see Alternative 4); the wire only requires that the location is not disclosed. 5. Per-scope honesty semantics. §A.2 makes the capabilities document host-scoped ("advertise the class when ≥1 tenant has a configured endpoint"). Confirm clients do not read selfHosted[] as "you have an endpoint" but as "this host supports* configuring one." Recommendation: document the host-scope reading explicitly in capabilities.md (the same scope semantics byok[] already has).
Implementation notes (non-normative)
- Reference host: openwop-app ADR 0121 (
docs/adr/0121-local-model-provider-support.md). The host adds acompatdispatch case (reusing its existing OpenAI-compatible chat-completions path with a per-connection base-URL — theminimaxprecedent), stores{baseUrl, apiKey?, capabilities?}as an ADR 0024 Connection, and advertises the provider id inaiProviders.supported[]andaiProviders.selfHosted[]only when a reachable endpoint is configured for the advertising scope (the §A.2 honesty gate, enforced underOPENWOP_REQUIRE_BEHAVIOR). Itsroutes/discovery.tspopulatesselfHosted[]from the configuredcompatConnections; itsmodelCapabilityProbecontributes the endpoint's declared capabilities tomodelCapabilities.advertised[](§B). - Endpoint non-disclosure (§D) is satisfied by choosing a stable opaque id (e.g.
compatorcompat:<connectionLabel>with no URL) and never serialising the base-URL into discovery, events, or errors. The §E shape scenario statically catches a URL-shaped id; the host is responsible for keeping the base-URL out of event/error payloads (the same redaction harness as RFC 0046). - Egress (SSRF-guarding the operator-supplied base-URL: private-IP block, https-only except an explicitly-enabled loopback, redirect re-validation) is host-internal per Alternative 4 and lives in ADR 0121 §egress — not normated here.
- Effort: schema field + spec text ~0.5 day; the SECURITY invariant ~0.25 day; the shape conformance scenario ~0.5 day; reference-host advertisement + the honesty gate (mostly ADR 0121 work) ~1 day; CHANGELOG + INTEROP-MATRIX ~30 min.
Acceptance criteria
Promotion Draft → Active (✅ met 2026-06-24 — bootstrap-phase steward waiver; see Status history):
- [x] Comment window: waived under the bootstrap-phase steward waiver (RFC 0031 / 0046 / 0095 precedent) — single-steward repo, zero external reviewers.
- [x] Unresolved questions 1–5 each have a recommended resolution recorded (§Unresolved questions); none is critical or blocking.
- [x] Compatibility classification firm: additive (§Compatibility).
- [x] Wire-shape frozen: the
aiProviders.selfHosted[]field shape + the §A/§B/§D normative rules are locked — no shape changes before Accepted. - [x] All five architect passes complete; conformance scenarios sketched (§Conformance); threat model clear (§D).
- ↪ The
spec/v1/capabilities.md§A/§B prose +schemas/capabilities.schema.jsonfield edits land at Accepted (below), per the RFC 0031 Draft→Active precedent (shape locked at Active; text + conformance + reference-host at Accepted).
Promotion Active → Accepted (✅ met 2026-06-24 — see Status history):
- [x]
spec/v1/capabilities.md+host-capabilities.mdcross-reference text merged (#757). - [x]
schemas/capabilities.schema.jsonmerged withaiProviders.selfHosted(#757). - [x]
SECURITY/invariants.yamlgainsself-hosted-endpoint-no-disclosure(§D) (#757; protocol-tier, total 138 / protocol 108). - [x]
aiproviders-selfhosted-shape.test.tslands in@openwop/openwop-conformance; suite minor-version bumps (#757, suite1.36.0; the gatedaiproviders-selfhosted-honesty.test.tslanded #759, suite1.37.0). - [x] CHANGELOG entry under
[Unreleased]. - [x]
INTEROP-MATRIX.mdgains theaiProviders.selfHostedadvertisement column/note. - [x] Reference host (openwop-app, ADR 0121, PR #725 merged) advertises
aiProviders.selfHosted[]for a configured compat endpoint, passesaiproviders-selfhosted-shape.test.ts+ the honesty scenario non-vacuously underOPENWOP_REQUIRE_BEHAVIOR=true, and keeps the endpoint URL off the wire (§D). Closed under the single-witness bootstrap-phase steward waiver (the RFC 0031 / 0046 / 0095 precedent). Witness is the host's conformance harness (a real loopback OpenAI-compatible mock endpoint stood up byconformance/run.ts, production-dark) — the appropriate evidence tier for an intrinsically operator-private surface that §A.3/§D + the Motivation §4 SSRF-reconnaissance threat say MUST NOT be publicly disclosed, mirroring the RFC 0035 harness-graduation precedent (a deployed publicly-curl-able witness would contradict the surface's own non-disclosure model).
References
RFCS/0031-envelope-variants-and-model-capabilities.md— thecapabilities.modelCapabilities.advertised[]surface §B relies on for capability non-inference; the "truthful advertisement" normation §A.2 mirrors.RFCS/0046-host-credentials-capability.md— the BYOKCredentialReference+credential-payload-redactioninvariant §C carries forward for the optional endpoint key.RFCS/0067-*(provider auth modes / advisory vocabulary) —aiProviders.supported/byok/authModes/policiesshape and the advisory provider vocabulary that already listsollama/vllm.RFCS/0007-dispatch.md,RFCS/0026-provider-usage-event.md— adjacent provider-dispatch + usage surfaces (no change here).RFCS/0091-*—aiProviders.input.modalities, the other authoritative capability source named in §B.spec/v1/capabilities.md§aiProviders— the surface extended.SECURITY/threat-model-secret-leakage.md,SECURITY/threat-model-provider-policy.md— the threat models §D/§B reference.schemas/capabilities.schema.json§aiProviders(lines 749–868) — the object theselfHostedfield is added to.- openwop-app
docs/adr/0121-local-model-provider-support.md— the reference-host consumer this RFC unblocks;docs/research/2026-06-23-ai-chat-competitive-analysis.md§9 (B12), §11 — the cross-competitor motivation (LibreChatOllamaClient.js, JanRemoteOAIEngine, AnythingLLM local providers, Open WebUIrouters/ollama.py).
Status history
Active → Accepted (2026-06-24)
Graduated on the reference-host (openwop-app, ADR 0121) conformance-harness witness, closed under the single-witness bootstrap-phase steward waiver (the RFC 0031 / 0046 / 0095 precedent named in the Draft → Active evidence).
Evidence at promotion:
- Spec/schema/security/conformance landed (PR #757,
origin/main7d5367cd):aiProviders.selfHostedinschemas/capabilities.schema.json; §A.1/A.2/A.3 + §B prose inspec/v1/capabilities.md(+host-capabilities.mdcross-ref); theself-hosted-endpoint-no-disclosureSECURITY invariant (protocol-tier,medium— total 138 / protocol 108); the always-onaiproviders-selfhosted-shape.test.ts. The gated behavioralaiproviders-selfhosted-honesty.test.tslanded in PR #759 (9b12bd95). - Conformance published:
@openwop/openwop-conformance@1.36.0(shape leg, tagopenwop-conformance/v1.36.0) and1.37.0(honesty leg, tagopenwop-conformance/v1.37.0), both live on npm. - Reference-host witness (openwop-app, ADR 0121, PR #725 merged): the host advertises
aiProviders { supported:[…,"compat"], selfHosted:["compat"] }and passes bothaiproviders-selfhosted-shape(always-on) andaiproviders-selfhosted-honesty(gated) non-vacuously underOPENWOP_REQUIRE_BEHAVIOR=truevs conformance1.37.0. The honesty leg dispatches against the advertisedcompatid and reaches a real configured endpoint (HTTP 200 from a loopback OpenAI-compatible mock — notcapability_not_provided, proving §A.2 truthful advertisement), the id is non-URL (compat, §A.3), and the endpoint location appears in neither the response body nor any error payload (§D, via thecompat_transport_errorscrub). Production stays dark (OPENWOP_COMPAT_PROVIDER_ENABLEDunset ⇒selfHosted: []) — no dishonest live claim. - Evidence-tier rationale (steward + architect): the witness is the host's conformance harness (
conformance/run.tsstands up the real loopback mock + setsOPENWOP_TEST_COMPAT_ENDPOINT+OPENWOP_REQUIRE_BEHAVIOR), not a deployed publicly-curl-able discovery doc as the RFC 0100 / 0105 / 0106 managed-cloud graduations used. This is the appropriate tier for this surface: a self-hosted / OpenAI-compatible endpoint is intrinsically operator-private infrastructure that §A.3 / §D + the Motivation §4 SSRF-reconnaissance threat say MUST NOT be publicly disclosed, so a public deployed witness would contradict the surface's own non-disclosure model. It mirrors the RFC 0035 sandbox harness-graduation precedent (behavioral invariants proven by a conformance probe driving a seam, not a public deployment). The steward independently verified the witness by code-inspecting the honesty-gated emission (discovery.ts) + the real-mock setup (conformance/run.ts), confirming it is non-vacuous and not a fabricated result. - Single-witness, no dual-witness required: per the RFC's own §Acceptance text, the bootstrap-phase steward waiver permits closing on the single openwop-app reference-host witness; MyndHyve dual-witness is not required for this operator-config surface (unlike the 0105 / 0106 dual-witness path).
Compatibility: this promotion is non-normative — a status change only; no wire, schema, or behavior change in this commit. additive per COMPATIBILITY.md §2.1; RFC counts Accepted +1 / Active −1.
Draft → Active (2026-06-24)
Promoted under the bootstrap-phase steward waiver per CONTRIBUTING.md §"Bootstrap-phase notes" + MAINTAINERS.md §"Bootstrap-phase RFC waivers" — the same path RFC 0031, RFC 0046, and RFC 0095 used. The 7-day comment window is waived (single-steward bootstrap repo; zero external reviewers). The wire-shape is locked; the spec/schema text, the self-hosted-endpoint-no-disclosure SECURITY invariant, the aiproviders-selfhosted-shape.test.ts conformance scenario, and the reference-host (openwop-app ADR 0121) advertisement remain the path to Accepted.
Evidence at promotion:
- Classification firm —
additiveperCOMPATIBILITY.md§2.1: one optional field on a server-emitted shape; no existing field / event / error changes; new MUSTs bind only hosts that advertiseaiProviders.selfHosted[]; no migration. - Wire-shape locked:
aiProviders.selfHosted: string[](subset ofsupported, mirroringbyok[]) + §A.1/A.2/A.3 (subset constraint, truthful advertisement, endpoint non-disclosure) + §B (capability non-inference) + the §Dself-hosted-endpoint-no-disclosureinvariant. No shape changes expected before Accepted. - Five architect passes complete; no CRITICAL gaps. The gap register (G1–G7) and risk register (R1–R6) carry only Active/Accepted-stage items; the §Unresolved-questions recommendations stand as the working decisions (flag-now over a
providerClassesmap; declared-or-probed capability source host-internal; egress host-internal). - Path to
Active → Accepted: land the §A/§B prose inspec/v1/capabilities.md(+host-capabilities.mdcross-ref) and theaiProviders.selfHostedfield inschemas/capabilities.schema.json; add the SECURITY invariant + the shape conformance scenario; openwop-app (ADR 0121) advertisesselfHosted[]for a configured compat endpoint and passes — closable under the steward waiver or via dual-witness with a second advertising host.
Compatibility: this promotion is non-normative — a status change only; no wire, schema, or behavior change in this commit.
Draft (2026-06-23)
Authored via the five-architect pass (/prd). RFC + gap (G1–G7) + risk (R1–R6) registers landed (PR #755). The wire-gate for openwop-app ADR 0121 (local / OpenAI-compatible model provider).