Install, trust, diagnose, upgrade, roll back, and remove executable optional backend connectors without rebuilding cmd/lipstd. Hybrid composition is ADR 0008. Authoring details remain in authoring.md.
Trust posture (operator): an installed connector runs as a separate process behind approved local IPC and digest checks. That process isolation is not a malicious-code sandbox. Treat installed plugins as trust-equivalent to code you chose to run as the proxy service account. The dedicated threat model and accepted controls are in threat-model.md (make backend-plugin-security-checks).
There is no runtime download of plugins. Operators (or installers) place artifacts on disk; Go-LIP only discovers and launches what is already present under trusted roots.
| Profile | Contents | Typical use |
|---|---|---|
| minimal | Standard lipstd binary layout only — no optional connector executables |
Essentials-only deployments; add plugins later |
| curated-full | Structurally discovers every connectors/*/release.yaml whose profiles include full (not a maintained name list) and stages digests + manifests |
Dogfood, offline bundles, curated appliance images |
Commands (repo root):
make package-minimal PACKAGE_DEST=.golip-package-staging/minimal
make package-full PACKAGE_DEST=.golip-package-staging/full
make package-plugin-smoke
go run ./tools/backendplugin/package_plugins -profile full -dest .golip-plugins/fullEach staged tree includes ACCESS.txt (ownership posture metadata) and package-index.json. See fixtures examples/operator/package-index.minimal.json and examples/operator/package-index.full.json.
Default machine-scoped plugin roots (installer/admin owned; proxy account read + execute only):
| Platform | Default plugin root |
|---|---|
| Linux | /opt/go-lip/plugins |
| macOS | /Library/Application Support/Go-LIP/plugins |
| Windows | %ProgramFiles%\Go-LIP\plugins |
Guidance:
- Linux/macOS: directories
0755or tighter; plugin executables0755(or0555); manifests0644. Prefer root/admin ownership; the proxy UID/GID should not be able to rewrite digests or executables. - Windows: Administrators (or installer SID) modify; service/user SID Read & execute. Avoid granting the proxy account write/modify on the plugin tree. Named-pipe ACL enforcement is host-owned; do not widen pipe DACLs for convenience.
- Packagers may inject another installation-owned default (for example
/usr/libexec/go-lip/plugins); the runtime never guesses mutable per-user locations unlessdevelopment_modeis on with explicitpaths.
Copy one plugin directory (manifest + bin/ + digest metadata) into a trusted root. Removing that directory uninstalls that artifact; other plugins remain. No root Go rebuild required.
plugins:
backend_discovery:
enabled: true
paths:
- /opt/go-lip/plugins
strict: true
development_mode: false
backends: []| Field | Operator meaning |
|---|---|
enabled |
When false, optional connectors are not discovered |
paths |
Explicit trusted roots (non-recursive install directories containing *.backendplugin.json) |
strict |
Fail closed on discovery/layout errors |
development_mode |
Allows only the explicit paths you list; still no implicit home-directory plugin root |
Closed manifest: unknown JSON fields fail closed. Required shape matches examples/operator/closed-manifest.backendplugin.json:
{
"schema": "golip.backendplugin.manifest/v1",
"plugin_id": "io.golip.backend.localstub",
"version": "0.1.0",
"build_id": "REPLACE_BUILD_ID",
"executable": "bin/lip-backend-localstub",
"sha256": "REPLACE_SHA256",
"protocol_major": 1,
"protocol_min_minor": 0,
"protocol_max_minor": 0,
"platforms": [{"os": "linux", "arch": "amd64"}],
"exports": [
{
"kind": "local-stub",
"credential_mode": "none",
"access_scope": "any",
"process_sharing": "per_instance"
}
]
}Digest / exact artifact / private staging: the host verifies sha256, binds a private staged copy of the executable, and launches those exact bytes — not a mutable path-only trust. Staging is cleaned on shutdown/upgrade paths exercised by packaging smoke tests.
Configured-missing: an enabled backend whose kind is not built-in and not discovered fails closed at composition (inspect/check-config surface the gap). Installed but unconfigured plugins stay inactive (no process launch).
Validated configs:
config/examples/plugin-operator-minimal.yaml— discovery off / essentials + optional stub after single-plugin installconfig/examples/plugin-operator-full-discovery.yaml— curated-full discovery pathexamples/operator/discovery-development.yamlexamples/operator/discovery-production.yaml
- Hosts use approved secure local IPC profiles (platform-specific; Windows uses a host-provided pipe such as
LIP_PLUGIN_CHANNEL_PIPE). Unauthorized local peers cannot negotiate/configure. - Peer-authentication failure: doctor/configure stops; connector credentials are never sent after channel/peer failure.
- Secrets arrive only in authenticated configure payloads — not via unprotected process-environment bootstrap. Do not put provider API keys into plugin launch env.
- Local-only connectors (
access_scope/ local-only posture) are rejected whenaccess.mode: multi_user. Keep them on single-user loopback deployments.
access.mode: multi_user is a shared-service deployment: unrelated downstream principals multiplex through one proxy. Two independent things must both hold before any backend may run in that mode, and the most restrictive source wins.
| Requirement | Declared by | Enforced |
|---|---|---|
access_scope: any |
connector manifest export | host, at composition |
credential_mode is not oauth_user and not unknown |
connector manifest export | host, at composition |
execution_class: inference (agent runtimes are never shared) |
connector manifest export | host, at composition |
| Explicit entry in the host-owned approval registry | internal/standardplugins/multi_user_backend_policy.go |
host, at composition |
The registry lives in the host/distribution tree, never in connectors/<name>. Connector packaging cannot add itself to it, and no connector can read the effective access mode (architecture guards reject both an import of host internals and a locally declared multi-user authority).
Absence from the registry is denial. A brand-new connector that declares access_scope: any + credential_mode: static + execution_class: inference — even a real agent runtime that misclassifies itself — is rejected in a multi-user deployment. Promoting a factory is an explicit, separately reviewed change to the registry file, which is a privilege grant and should be treated as such (CODEOWNERS / branch protection).
The generic invariants behind this are:
execution_class: agent_runtime => access_scope: local_only # documented product policy
credential_mode: oauth_user => access_scope: local_only # personal/subscription credentials
manifest validation rejects credential_mode: oauth_user with any access scope other than local_only
An approval can never widen a posture: a factory that is approved but whose packaged manifest says local_only, agent_runtime, oauth_user, or an undeclared credential is still denied in a multi-user deployment.
The manifest, not the runtime descriptor, is the host's security-policy authority. Connector parity tests keep FactoryDescriptor.AccessScope / CredentialMode in agreement with the packaged manifest so a disagreement is a diagnostic failure, not a silent divergence. The access decision itself is never made inside Describe() or Configure().
Go-LIP's boundary is about credential ownership under the current principal model, not about a blanket provider-terms rule:
- User-scoped / personal / subscription credentials — the credential belongs to one human's account or device. Every such factory is
access_scope: local_only;credential_modethen records how it authenticates and is not the same for all of them:- Agent runtimes and local-device backends are
local_onlywithcredential_mode: staticornone, because they read the local user's own CLI login, device state, or agent process rather than an upstream OAuth profile. This covers the ACP family, Cursor SDK under its current contract, and both Codex exports (openai-codexisstatic/inference;openai-codex-app-serverisnone/agent_runtime). - The subscription-OAuth connectors are
local_onlyandcredential_mode: oauth_user, because the effective credential is a user OAuth session:gitlab-duo,minimax-oauth,nous-portal,qwen-oauth,xai-oauth, plus a future ChatGPT-plan / SIWC profile. - In both groups the scope is what denies shared use, and
local_onlyalone already rejects amulti_userdeployment. OpenAI explicitly supports ChatGPT-plan usage in a same-user self-hosted VM; what Go-LIP refuses is any of these credentials becoming an operator-wide credential that unrelated downstream principals can multiplex. There is no pooling or subscriber failover across different users' accounts.
- Agent runtimes and local-device backends are
- Legitimate shared-service identities — provider API keys, workload identity from the runtime environment, and provider service accounts are ordinary multi-user-safe credentials. A backend that uses one of those, runs remote provider inference, and is explicitly registered may be approved.
There is no escape hatch. No allow_local_only_in_multi_user (or equivalent) configuration exists, and none should be added. A future shared agent backend must be a distinct, reviewed factory with a provider-supported service identity, explicit per-principal credential binding, and an explicit exception — not a reclassified existing connector.
The failure is a typed, actionable composition error carrying instance and factory context, raised at startup / check-config / reload candidate compilation before backend construction, connector Dial/Configure, or child-process launch. A configured-but-unapproved backend fails the whole candidate; it is never silently disabled or downgraded.
Remediate by choosing one of:
- Run a single-user loopback deployment —
access.mode: single_useron an explicit loopback listener (127.0.0.1,::1,localhost). Note thatsingle_useris about the listener, not about auth: a single-user loopback deployment may still use an external auth handler. - Use a credential type intended for shared service operation — a provider API key, workload identity, or service account on a backend that is explicitly approved for
multi_user.
Discover which kinds the current distribution approves:
go run ./cmd/lipstd inspect --config CONFIGgo run ./cmd/lipstd check-config --config CONFIG
go run ./cmd/lipstd inspect --config CONFIG
go run ./cmd/lipstd doctor --config CONFIG --instance INSTANCE_ID| Command | Launches plugins? | Meaning |
|---|---|---|
check-config |
No | Validates YAML + composition readiness |
inspect |
No | Built-in vs discovered kinds, versions, conflicts, configured-missing, activation needed |
doctor --instance ID |
Only that configured instance | Handshake / secure-channel / peer checks; never all discovered plugins |
Inspect states operators commonly see: discovered, configured, missing kind, manifest invalid, digest mismatch, builtin collision, local-only rejected. Doctor failures on peer/channel leave no credential exposure to the plugin.
Compatibility: host and plugin negotiate protocol major/minor from the manifest. Incompatible major versions fail before configure.
Atomic upgrade:
- Stage the new artifact beside the live tree (or into a versioned directory).
- Verify digest/
package-index.json. - Atomically replace the published plugin directory (packaging uses staging + publish).
- Point discovery at the new root if you use versioned roots — see
config/examples/plugin-operator-upgrade.yamlandexamples/operator/upgrade-candidate.yaml:
plugins:
backend_discovery:
enabled: true
development_mode: true
strict: true
paths:
- .golip-plugins/upgrade-candidate/localstubcheck-config+inspect; optionaldoctor --instance ….
Rollback: keep the previous published directory; retarget backend_discovery.paths (or restore the prior atomic publish) — config/examples/plugin-operator-rollback.yaml, examples/operator/rollback-previous.yaml. No lipstd rebuild.
plugins:
backend_discovery:
enabled: true
development_mode: true
strict: true
paths:
- .golip-plugins/previous/localstubUninstall / cleanup: delete the plugin install directory; remove or disable its plugins.backends rows. Locked source artifacts and private staging must not remain after tested shutdown/upgrade (packaging smoke covers staged cleanup). Unrelated plugins keep working.
By default, Go-LIP applies a safe routing execution composition policy (routing.execution_composition_policy: safe). Under this policy:
- Backends classified as
agent_runtime(such as ACP agent connectors, Cursor SDK agents, or OpenAI Codex App-Server) and backends withunknownexecution class cannot be mixed into composite routing selectors (failover|, parallel/race!, weighted^, or thinker hybrid chains) with other backends. - Direct routing to any backend (e.g.
acp:claude-3-7-sonnet) is always permitted. - Pure inference composition (e.g.
openai:gpt-4o|anthropic:claude-3-5-sonnet) is fully permitted.
To explicitly permit mixed agent runtime and inference composition at operator risk, set:
routing:
execution_composition_policy: unrestrictedWarning
In unrestricted mode, failover or parallel execution against agent runtimes may trigger duplicate side-effects (e.g. tool execution, file edits, git commands) across multiple backends or retries.
The Cloudflare external backend connector (kind: cloudflare) integrates Go-LIP with the Cloudflare AI Gateway REST API.
Key characteristics:
- Cloudflare AI Gateway REST API: Requests are dispatched to
{api_origin}/client/v4/accounts/{account_id}/ai/v1(production default origin:https://api.cloudflare.com). The account-scoped path is deterministically constructed by the connector. - Responses preferred: ambiguous or OpenResponses traffic routes to the
/responsesendpoint. Chat completions routes to/chat/completions. - Not Workers AI: this connector is an external HTTP backend target for Cloudflare AI Gateway REST endpoints, not an in-worker or Workers AI SDK binding.
- Not
/compat: the deprecated/compatendpoint is forbidden for ordinary calls. - Not ACP: the connector is an
execution_class: inferencebackend, not an Agent Client Protocol runtime. - Gateway selection: when
gateway_idis configured, each request passescf-aig-gateway-id: <gateway_id>. When omitted, the header is not sent. - Credentials: API tokens must be supplied via
ConfigureRequest.Secrets(api_tokenpreferred,api_keyfallback). Literal tokens in configuration YAML are strictly forbidden and rejected. - Model inventory:
ListModelsqueries GET/modelsand filters results to Responses-capable text and coding models, omitting embedding, rerank, image, and audio models.
plugins:
backends:
- id: cloudflare-gateway
kind: cloudflare
config:
account_id: "your-cloudflare-account-id"
gateway_id: "my-gateway"The Azure OpenAI external backend connector (kind: azure-openai) integrates Go-LIP with Azure OpenAI and Azure AI Foundry endpoints. A single azure-openai connector kind covers both Azure OpenAI and Azure AI Foundry v1, as Foundry v1 shares the same OpenAI-compatible resource/deployment REST surface.
Key characteristics:
- Base URL construction: Outgoing requests target
{endpoint}/openai/v1orhttps://{resource_name}.openai.azure.com/openai/v1. The/openai/v1path is deterministically formed by the connector. - Responses preferred & Hard-negative: Ambiguous or OpenResponses traffic routes to the
/responsesendpoint and never falls back to chat completions (hard-negative). Chat completions traffic explicitly routes to/chat/completions. - API Version: The required
api_versionconfiguration (e.g.2024-10-21) is injected as an HTTP query parameter (?api-version=<version>) on every outgoing call. - Dual credential modes:
credential_mode: api_key(default): sendsapi-key: <key>header (noAuthorizationheader). The API key is supplied viaConfigureRequest.Secrets(api_key).credential_mode: entra: authenticates via Microsoft Entra ID credential chain (DefaultAzureCredentialor workload identity/service principal using optional typedtenant_id,client_id, and secretclient_secret). SendsAuthorization: Bearer <token>header (noapi-keyheader). Tokens are resolved dynamically per request and never persisted to YAML, diagnostics, or descriptors. Never paste a static JWT bearer token into configuration.
- Not
/compat: The/compatendpoint is forbidden for ordinary calls. - Not ACP: The connector is an
execution_class: inferencebackend, not an Agent Client Protocol runtime. - Deployments: A required
deploymentsmapping (<deployment-name>: <underlying-model>). Azure routes inference requests by deployment name rather than model ID. At least one deployment is required. - Model inventory:
ListModelsmaps deployed models to canonical IDs prefixed withazure-openai/and filters out non-Responses models (embeddings, rerank, audio, image).
plugins:
backends:
- id: azure-openai-eastus
kind: azure-openai
config:
resource_name: "my-openai-resource"
api_version: "2024-10-21"
credential_mode: "api_key"
deployments:
my-gpt-4o: gpt-4o
my-gpt-4o-mini: gpt-4o-miniThe Snowflake Cortex external backend connector (kind: snowflake-cortex) integrates Go-LIP with Snowflake Cortex REST APIs.
Key characteristics:
- Base URL construction: Outgoing requests target
https://{account}.snowflakecomputing.com/api/v2/cortex/v1. The/api/v2/cortex/v1path is deterministically formed by the connector from the typedaccountidentifier. - Not
/compat: The/compatendpoint is forbidden for ordinary calls. - Not ACP: The connector is an
execution_class: inferencebackend, not an Agent Client Protocol runtime. - Role header: When optional
roleis configured, outgoing requests includeX-Snowflake-Role: <role>. When omitted, the header is not sent. - Credentials: Programmatic Access Tokens (PAT) or JWTs must be supplied via
ConfigureRequest.Secrets(patpreferred,api_keyfallback). Literal tokens in configuration YAML are strictly forbidden and rejected. - Responses preferred & Hard-negative: Ambiguous or OpenResponses operations route to
/responsesand never fall back to chat completions. Chat completions operations route to/chat/completions. - Model inventory:
ListModelsmaps Snowflake-hosted foundation models (e.g.mistral-large2,llama3.3-70b,snowflake-arctic,deepseek-r1) to canonical IDs prefixed withsnowflake-cortex/, filtering out non-coding/non-language models (embeddings, rerank, audio, image).
plugins:
backends:
- id: snowflake-cortex-primary
kind: snowflake-cortex
config:
account: "xy12345.us-east-1"
role: "cortex_user_role"The Databricks AI external backend connector (kind: databricks-ai) integrates Go-LIP with Databricks AI Gateway OpenAI-compatible endpoints.
Key characteristics:
- Base URL construction: Outgoing requests target
https://{host}/ai-gateway/mlflow/v1. The host is normalized (stripping schemes and trailing slashes) and the gateway path/ai-gateway/mlflow/v1is deterministically appended by the connector. - Default model-service name: When optional
serving_endpointis configured, it serves as the default target model for the mlflowmodelfield when the invocation model is omitted or unversioned. When the invocation supplies an explicit model, that model is used. No custom routing headers are injected. - Not
/compat: The/compatendpoint is forbidden for ordinary calls. - Not ACP: The connector is an
execution_class: inferencebackend, not an Agent Client Protocol runtime. - Credentials: Databricks workspace Personal Access Tokens must be supplied via
ConfigureRequest.Secrets(tokenpreferred,api_keyfallback). Literal tokens in configuration YAML are strictly forbidden and rejected. - Responses preferred & Hard-negative: Ambiguous or OpenResponses operations route to
/responsesand never fall back to chat completions. Chat completions operations route to/chat/completions. - Model inventory:
ListModelsmaps Databricks serving endpoints and foundation models (e.g.databricks-dbrx-instruct,databricks-meta-llama-3-3-70b-instruct) to canonical IDs prefixed withdatabricks-ai/, filtering out non-coding/non-language models (embeddings, rerank, audio, image).
plugins:
backends:
- id: databricks-gateway
kind: databricks-ai
config:
host: "adb-123.azuredatabricks.net"
serving_endpoint: "my-endpoint"The Infomaniak AI external backend connector (kind: infomaniak-ai) integrates Go-LIP with Infomaniak AI OpenAI-compatible endpoints.
Key characteristics:
- Base URL construction: Outgoing requests target
https://api.infomaniak.com/2/ai/{product_id}/openai/v1. The requiredproduct_idis supplied in configuration (accepting an integer or numeric string). - Chat default: In contrast to other providers, Infomaniak AI documents standard chat completions (
/chat/completions) and does not document a/responsesendpoint. Ambiguous and default traffic routes to/chat/completions. Explicit Responses traffic routes to/responsesand fails closed if unsupported. - Not
/compat: The/compatendpoint is forbidden for ordinary calls. - Not ACP: The connector is an
execution_class: inferencebackend, not an Agent Client Protocol runtime. - Credentials: API tokens must be supplied via
ConfigureRequest.Secrets(api_keypreferred,tokenfallback). Literal tokens in configuration YAML are strictly forbidden and rejected. - Model inventory:
ListModelsqueries GET/modelsfrom the constructed product base and maps models to canonical IDs prefixed withinfomaniak-ai/, filtering out non-coding/non-language models (embeddings, rerank, audio, image).
plugins:
backends:
- id: infomaniak-ai-primary
kind: infomaniak-ai
config:
product_id: 103281The Google Vertex AI external backend connector (kind: vertex) integrates Go-LIP with Google Vertex AI generative model endpoints using the native generateContent and streamGenerateContent REST protocol.
Key characteristics:
- Constructed URL: Requests target
https://{location}-aiplatform.googleapis.com/v1/projects/{project}/locations/{location}/publishers/{publisher}/models/{model}:generateContent(or:streamGenerateContent?alt=ssefor streaming). If location isglobal, the origin ishttps://aiplatform.googleapis.com. The default publisher isgooglewhen omitted. - Native generateContent contract: Uses native Vertex/Gemini
contents/candidatespayloads instead of forcing models through OpenAI compatibility endpoints or gateways. - Distinct from Gemini API-key backend: This connector is specifically for Google Cloud Vertex AI (
kind: vertex) using OAuth2 / Bearer authorization, distinct from the in-process Gemini API-key backend (kind: geminiatgenerativelanguage.googleapis.com). - Not ACP: The connector is an
execution_class: inferencebackend, not an Agent Client Protocol runtime. - Credentials: Supports Google Application Default Credentials (ADC) or explicit service account credentials via
ConfigureRequest.Secrets(service_account_json). Literal secrets or private keys in configuration YAML are strictly forbidden and rejected. - Model inventory:
ListModelsqueries the Model Garden catalog viaGET /v1beta1/publishers/{publisher}/models(default publishergoogle) without embedding project or location in the inventory path, and maps models to canonical IDs prefixed withvertex/, dropping embeddings, image generation, video, and audio models.
The Amazon SageMaker external backend connector (kind: sagemaker) integrates Go-LIP with Amazon SageMaker model deployments using the AWS SDK v2 with SigV4 request signing.
Key characteristics:
- SigV4 signing: All runtime invoke requests are signed with AWS Signature Version 4 (SigV4) using standard AWS credentials.
- Runtime invocation: Directly targets SageMaker Runtime
InvokeEndpoint(unary) at/endpoints/{endpoint_name}/invocations. Provider streaming (InvokeEndpointWithResponseStream) is intentionally not used; streaming delivery is served by collecting the unary response. - Frozen v1 inference contract: Requires
inference_contract: hf-text-generation. Requests send{"inputs": "<user text>", "parameters": {"max_new_tokens": ...}}and parse{"generated_text": "..."}or[{"generated_text": "..."}]responses into canonical text delta events. Unsupported roles or tools fail closed rather than silently drop semantics. - Distinct from AWS Bedrock: This connector is specifically for Amazon SageMaker custom and jumpstart endpoint deployments (
kind: sagemaker), completely distinct from the in-process Amazon Bedrock backend (kind: bedrock). - Not ACP: The connector is an
execution_class: inferencebackend, not an Agent Client Protocol runtime. - Credentials: Supports default AWS credential chain (environment variables, AWS shared credentials/config, IAM roles) via
NewProduction(), or explicit static credentials viaConfigureRequest.Secrets(aws_access_key_id,aws_secret_access_key, optionalaws_session_token). Literal secrets or access keys in configuration YAML are strictly forbidden and rejected. - Model inventory:
ListModelsexposes only the configuredendpoint_nameassagemaker/{endpoint_name}(no control-plane enumeration). Executing an unconfigured endpoint fails closed. Required IAM permission:sagemaker:InvokeEndpointon the configured endpoint; no SageMaker control-plane permissions are needed.
The Oracle Cloud Infrastructure (OCI) Generative AI external backend connector (kind: oci-generative-ai) integrates Go-LIP with OCI Generative AI inference service via native HTTP request signing.
Key characteristics:
- Constructed chat URL: Requests target
https://inference.generativeai.{region}.oci.oraclecloud.com/20231130/actions/chat. - OCI HTTP signing: All requests are signed using OCI HTTP Signatures (
Authorization: Signature ...) with an RSA private key. Bearer authorization is not used. - Native GENERIC chat contract: Sends documented OCI
ChatDetailswithchatRequest.apiFormat: "GENERIC"using on-demand or dedicated serving modes. Responses parsechatResponsechoices and text contents into canonical text events. Unsupported message roles or tools fail closed rather than silently drop semantics. - Not OpenAI-compatible: Operates directly against native OCI Generative AI inference action endpoints, not the OCI
/openai/v1compatibility layer. - Not ACP: The connector is an
execution_class: inferencebackend, not an Agent Client Protocol runtime. - Credentials: Supports OCI standard credential providers (file / instance principal) via
NewProduction(), or explicit static credentials viaConfigureRequest.Secrets(private_keyPEM, optionalpassphrase,tenancy_ocid,user_ocid,fingerprint). Literal private keys or tokens in configuration YAML are strictly forbidden and rejected. - Model inventory:
ListModelsqueries the Generative AI management API athttps://generativeai.{region}.oci.oraclecloud.com/20231130/models?compartmentId={compartmentId}, dropping non-language models (embed, rerank, image) and mapping models to canonical IDs prefixed withoci-generative-ai/.
The IBM watsonx.ai external backend connector (kind: watsonx) integrates Go-LIP with IBM watsonx.ai regional machine learning endpoints using native chat and text generation REST APIs.
Key characteristics:
- Constructed regional ML URL: Requests target
https://{region}.ml.cloud.ibm.com/ml/v1/text/chat?version={api_version}(or_streamfor streaming). Text generation targets/ml/v1/text/generationand/ml/v1/text/generation_stream. Deployed models target/ml/v1/deployments/{id}/text/chat. - IBM Cloud IAM API key exchange and refresh: Production credentials use IBM Cloud IAM (
POST https://iam.cloud.ibm.com/identity/tokenwithgrant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={apikey}), caching and proactively refreshing Bearer tokens before expiry. - Native watsonx contract: Direct integration with native IBM watsonx chat and text generation payloads. Not OpenAI-compatible (
/ml/v1/openaior/v1/chat/completions), not ACP, and not LiteLLM. - Project XOR Space scope: Exactly one of
project_idorspace_idmust be configured; configuring neither or both fails closed. - Credentials: API key is supplied via
ConfigureRequest.Secrets(api_keyorapikey). Literal secrets or API keys in configuration YAML are strictly forbidden and rejected. - Model inventory:
ListModelsqueries foundation model specs atGET /ml/v1/foundation_model_specsand ready deployments atGET /ml/v4/deployments, dropping withdrawn/deprecated and non-language models (embed, rerank, image) and mapping models to canonical IDs prefixed withwatsonx/orwatsonx/deployment/.
The SAP AI Core external backend connector (kind: sapaicore) integrates Go-LIP with SAP AI Core Generative AI Hub deployments using OpenAI-compatible chat completion endpoints.
Key characteristics:
- Deployment-routed inference URL: Requests target
POST {AI_API_URL}/v2/inference/deployments/{deploymentId}/chat/completions. The deployment ID is resolved per request (sapaicore/{deployment-id}or defaultdeployment_idfrom configuration). - Service key parsing and OAuth: Production credentials require a standard SAP BTP service key passed connector-locally via
ConfigureRequest.Secrets(service_keyJSON containingclientid,clientsecret,url,serviceurls.AI_API_URL). The connector performs OAuth2 client credentials exchange (POST {url}/oauth/token) and caches Bearer tokens with proactive refresh before expiry. Service key JSON andclientsecretare never exposed in diagnostics or error logs. - Resource group enforcement: Every AI API call (both inference and model inventory) applies the required
AI-Resource-Groupheader matchingresource_group. - Chat completions only: Reuses compatible transport strictly for OpenAI-compatible chat completions (
inference_contract: openai-chat). Responses operations, orchestration pipelines (.../v2/completion), and embeddings fail closed. - Not ACP: The connector is an
execution_class: inferencebackend, not an Agent Client Protocol runtime. - Model inventory:
ListModelsqueriesGET {AI_API_URL}/v2/lm/deploymentswithAI-Resource-Groupand Bearer token, enumerating deployments inRUNNINGstate and mapping them to canonical IDs prefixed withsapaicore/.
The Cohere external backend connector (kind: cohere) integrates Go-LIP with Cohere's native v2 chat endpoint using native request and response mapping.
Key characteristics:
- Native v2 chat endpoint: Requests target
POST https://api.cohere.com/v2/chatwith native payload structure (model,messages,stream, optionalmax_tokens). Not OpenAI Chat completions, not LiteLLM, and not legacy Cohere v1 (/v1/chat). - Bearer API key credentials: Production credentials require an API key passed via
ConfigureRequest.Secrets(api_keyortoken) sent asAuthorization: Bearer {api_key}. Literal secrets in configuration YAML are strictly forbidden and rejected. Error logs and diagnostics never expose the API key. - Native streaming and content parsing: Streaming requests use
/v2/chatwithstream: trueand parse Cohere v2 SSE chunks (content-delta,message-end). Unary responses parse string or content-block array structures losslessly. - Fail closed on unsupported semantics: Supported roles are
system,user, andassistant. Tools, non-text parts (vision, images, files), and Responses operations fail closed. - Not ACP: The connector is an
execution_class: inferencebackend, not an Agent Client Protocol runtime. - Model inventory:
ListModelsqueriesGET https://api.cohere.com/v1/models?endpoint=chat, dropping non-language models (embed, rerank, image, audio) and mapping models to canonical IDs prefixed withcohere/.
The Replicate external backend connector (kind: replicate) integrates Go-LIP with Replicate language-model prediction endpoints using native prediction lifecycle orchestration.
Key characteristics:
- Explicit prediction lifecycle: Creates predictions via
POST https://api.replicate.com/v1/models/{owner}/{name}/predictions. Connects directly to SSE stream (urls.stream) when streaming is requested, or pollsurls.getuntil a terminal status (succeeded,failed,canceled,aborted) is reached. Context cancellation immediately aborts local I/O and issuesPOST urls.cancelto cancel the remote prediction. - Frozen v1 inference contract: Requires
inference_contract: prompt-text. Concatenates user text parts intoinput.prompt. Tools, vision/non-text parts, non-user roles, and Responses operations fail closed. - Not OpenAI-compatible, not ACP, not LiteLLM: Direct integration with native Replicate model prediction endpoints without translation gateways or Chat Completions compatibility layers.
- Bearer API token credentials: Production credentials require an API token passed via
ConfigureRequest.Secrets(api_token,token, orapi_key) sent asAuthorization: Bearer {token}. Literal secrets in configuration YAML are strictly forbidden and rejected. Error logs and diagnostics never expose the token. - Model inventory:
ListModelsexposes only the configuredowner/nameasreplicate/{owner}/{name}, verified againstGET /v1/models/{owner}/{name}(404 fails closed). Executing a model different from the configured model fails closed.
The GitLab Duo external backend connector (kind: gitlab-duo) integrates Go-LIP with GitLab Duo and the Duo Agent Platform (DAP) inference endpoints using GitLab's direct-access token exchange protocol.
Multi-user deployment: ships access_scope: local_only and credential_mode: oauth_user, because its upstream credential is one human user's OAuth session rather than an operator, workload, or service-account credential. See multi-user eligibility.
Key characteristics:
- Direct-access token exchange: Requests exchange an authorized GitLab Personal Access Token (PAT) or OAuth session token with the GitLab instance (
POST /api/v4/ai/third_party_agents/direct_access, accepting 201 Created or any 2xx response) with documented feature flags to obtain a short-lived JSON Web Token (JWT) and required routing headers for the AI Gateway. Entitlement denials (403) fail closed immediately without retry loops. - AI Gateway inference targets: Routes agentic chat inference to GitLab AI Gateway endpoints at
POST /ai/v1/proxy/anthropic/v1/messages(withanthropic-beta: context-1m-2025-08-07) andPOST /ai/v1/proxy/openai/v1/chat/completions. An optionalai_gateway_urloverride can be configured to point to an enterprise private AI gateway. - Truthful client identification: All HTTP requests truthfully report user agent identifying
go-llm-interactive-proxyand the GitLab Duo connector without impersonating OpenCode or editor plugins. - Authentication & OAuth lifecycle: Supports static PATs via
ConfigureRequest.Secrets(patortoken) or file-backed OAuth sessions viaoauth_token_fileusing the sharedoauthcredpattern module. Expired OAuth tokens refresh proactively viaPOST /oauth/token; terminal refresh failures quarantine the stored token record to protect downstream accounts. Self-managed GitLab instances strictly requireoauth_client_id. Literal secrets or tokens in configuration YAML are strictly forbidden and rejected. - Model inventory and lifecycle scoping: When a
root_namespace_idorproject_pathis configured, dynamic discovery queries GitLab GraphQLaiChatAvailableModelsatPOST /api/graphqlto discover available model refs alongside static agentic chat models (duo-chat-haiku-4-5,duo-chat-sonnet-4-5,duo-chat-opus-4-5). When neither is configured, GraphQL discovery is skipped and only static models are returned. Discovery queries and project lookups fail closed on non-200 responses. Dynamic model caches are strictly generation-scoped and bound to the configured instance lifecycle. - Strictly an inference connector (Not ACP, No repository tools): Focuses strictly on model execution and completions. Requests specifying ACP operations (
agent_control) or non-inference repository management tools (gitlab_mr_*,gitlab_issue_*,gitlab_pipeline_*, repository actions) fail closed with clear out-of-scope errors.
The Nous Portal external backend connector (kind: nous-portal) integrates Go-LIP with Nous Portal subscription gateway and Nous inference endpoints using scoped OAuth JSON Web Tokens (JWT).
Multi-user deployment: ships access_scope: local_only and credential_mode: oauth_user, because its upstream credential is one human user's subscription session rather than an operator, workload, or service-account credential. See multi-user eligibility.
Key characteristics:
- Scoped JWT subscription gateway: Connects to Nous Portal (
https://portal.nousresearch.com) and Nous Inference API (https://inference-api.nousresearch.com/v1). Mints and refreshes scopedinference:invokeJWTs from stored OAuth refresh tokens viaPOST /api/oauth/token. Legacy opaque session-keys or static API keys are supported when provided via secrets. - Truthful client identity: Truthfully reports User-Agent
go-llm-interactive-proxy/0.1.0 (nous-portal)across all Portal and Inference requests. Never sends Hermes client tags (client=hermes-client-*), never claims Hermes, and forbids hardcoded Hermes client IDs in production constructor paths. - Authentication & OAuth lifecycle: File-backed OAuth credentials manage refresh tokens via
oauthcredwith 0600 file permissions and 120s skew margin. Strictly requires configuredoauth_client_idfor OAuth flows. Terminal refresh errors (invalid_grant,refresh_token_reused, 4xx) quarantine stored credentials and prevent refresh replay. Literal secrets in configuration YAML are strictly forbidden and rejected. - Entitlement access control: HTTP 403 Forbidden responses from the inference API indicate entitlement or credit limits and fail closed immediately without entering token refresh loops. Transient HTTP 401 Unauthorized responses trigger token refresh once and retry.
- Dynamic model catalog: Queries
GET /modelson the inference endpoint to dynamically discover available models, prefixing canonical model IDs asnous-portal/{id}while preserving vendor slugs (e.g.nous-portal/anthropic/claude-sonnet-4.6). Discovery queries fail closed on non-200 responses. - Inference execution: Provides OpenAI-compatible Chat completions (
/chat/completions) for streaming and non-streaming requests. Out-of-scope services (such as Tool Gateway, browser automation, audio/TTS, or ACP) are not supported.
The xAI Subscription OAuth external backend connector (kind: xai-oauth) integrates Go-LIP with xAI's subscription service using standard OAuth 2.0 / OIDC credentials and the xAI OpenAI-compatible Chat API.
Multi-user deployment: ships access_scope: local_only and credential_mode: oauth_user, because its upstream credential is one human user's subscription session rather than an operator, workload, or service-account credential. Use the catalog-driven API-key xai profile for shared deployments. See multi-user eligibility.
Key characteristics:
- Distinct from API-key xAI profile: Operates as a distinct backend plugin (
kind: xai-oauth, plugin IDio.golip.backend.xaioauth) separate from the catalog-driven API-keyxaiprofile. - OIDC discovery & subscription token refresh: Performs standard OIDC discovery against
https://auth.x.ai/.well-known/openid-configuration(or a configured issuer URL) to locate the token endpoint (https://auth.x.ai/oauth2/token), exchanging OAuth refresh tokens for short-lived access tokens viagrant_type=refresh_token. - Chat family only (No Responses): Strictly routes inference to the standard Chat Completions endpoint (
POST /chat/completions). OpenAI Responses operations (/v1/responsesorOperationOpenAIResponses) are not used or supported for xAI OAuth, ensuring predictable streaming behavior and compatibility. - Truthful client identity: Truthfully reports User-Agent
go-llm-interactive-proxy/0.1.0 (xai-oauth)across all auth and inference requests. Never sends Hermes or Grok CLI client tags, never claims Hermes or Grok CLI identity, and forbids hardcoded Hermes client IDs in production constructor paths. - Entitlement access control: HTTP 403 Forbidden responses from the inference API indicate subscription tier or feature entitlement denials and fail closed immediately without entering token refresh loops. Transient HTTP 401 Unauthorized responses trigger token refresh once and retry.
- Authentication & OAuth lifecycle: File-backed OAuth credentials manage refresh tokens via
oauthcredwith 0600 file permissions and 60s skew margin. Strictly requires configuredoauth_client_idfor OAuth flows. Terminal refresh errors (invalid_grant,unauthorized_client, 4xx) quarantine stored credentials and prevent refresh replay. Literal secrets in configuration YAML are strictly forbidden and rejected.xai-oauthispre-provisioned-refresh-only: initial browser/device login is not implemented (no documented public third-party authorization endpoint exists, so reimplementing xAI's first-party login would be private-interface scraping); provision the token file via xAI's official authorization channels first.Configurefails closed with an actionable error until a usable credential exists. - Dynamic model catalog: Queries
GET /modelson the inference endpoint (https://api.x.ai/v1) to dynamically discover available Grok models, prefixing canonical model IDs asxai-oauth/{id}(e.g.xai-oauth/grok-2). Discovery queries fail closed on non-200 responses.
The Qwen Portal OAuth external backend connector (kind: qwen-oauth) integrates Go-LIP with Alibaba's Qwen Portal subscription service using standard OAuth 2.0 PKCE / refresh tokens and OpenAI-compatible Chat completions at https://portal.qwen.ai/v1.
Multi-user deployment: ships access_scope: local_only and credential_mode: oauth_user, because its upstream credential is one human user's subscription session rather than an operator, workload, or service-account credential. Use the catalog-driven alibaba/DashScope API-key profile for shared deployments. See multi-user eligibility.
Key characteristics:
- Distinct from Alibaba/DashScope API-key profiles: Operates as a distinct external backend plugin (
kind: qwen-oauth, plugin IDio.golip.backend.qwenoauth) separate from catalog-drivenalibabaand DashScope API-key profiles. - Wire request adaptations: Applies 5 connector-local request adaptations on outbound HTTP Chat payloads:
- Normalizes string message content into typed text parts (
[{"type": "text", "text": ...}]). - Preserves image URL objects (
{"type": "image_url", "image_url": ...}). - Injects
cache_control: {"type": "ephemeral"}on the last part of the system message. - Sets top-level
vl_high_resolution_images: truefor vision-language models. - Places session metadata at top-level
body["metadata"]rather than nested insideextra_body.
- Normalizes string message content into typed text parts (
- Chat family only (No Responses): Strictly routes inference to Chat Completions (
POST /chat/completions). OpenAI Responses operations (/v1/responsesorOperationOpenAIResponses) are explicitly rejected. - Truthful client identity: Truthfully reports User-Agent
go-llm-interactive-proxy/0.1.0 (qwen-oauth)across all requests. Never sends Hermes or Qwen CLI client tags, never claims Hermes or Qwen CLI identity, and forbids hardcoded Hermes client IDs in production constructor paths. - Authentication & OAuth lifecycle: File-backed OAuth credentials manage refresh tokens via
oauthcredwith 0600 file permissions and 120s skew margin. Strictly requires configuredoauth_client_idfor OAuth flows. Refresh tokens exchange againsthttps://chat.qwen.ai/api/v1/oauth2/token. Terminal refresh errors (invalid_grant,unauthorized_client, 4xx) quarantine stored credentials and prevent refresh replay. Literal secrets in configuration YAML are strictly forbidden and rejected.qwen-oauthispre-provisioned-refresh-only: initial browser/PKCE login belongs to Qwen's first-party surfaces and is not reimplemented here (reimplementing that consumer login would be private-interface scraping); provision the token file via Qwen's official authorization channels first.Configurefails closed with an actionable error until a usable credential exists. - Entitlement access control: HTTP 403 Forbidden responses from the inference API indicate subscription tier or quota denials and fail closed immediately without entering token refresh loops. Transient HTTP 401 Unauthorized responses trigger token refresh once and retry.
- Dynamic model catalog: Queries
GET /modelson the inference endpoint (https://portal.qwen.ai/v1) to dynamically discover available Qwen models, prefixing canonical model IDs asqwen-oauth/{id}(e.g.qwen-oauth/qwen-coder-plus). Discovery queries fail closed on non-200 responses.
The MiniMax OAuth external backend connector (kind: minimax-oauth) integrates Go-LIP with MiniMax's subscription service using standard OAuth 2.0 PKCE / refresh tokens and Anthropic Messages inference endpoints at https://api.minimax.io/anthropic (global) or https://api.minimaxi.com/anthropic (China).
Multi-user deployment: ships access_scope: local_only and credential_mode: oauth_user, because its upstream credential is one human user's subscription session rather than an operator, workload, or service-account credential. Use the catalog-driven API-key minimax profile for shared deployments. See multi-user eligibility.
Key characteristics:
- Distinct from API-key MiniMax profile: Operates as a distinct external backend plugin (
kind: minimax-oauth, plugin IDio.golip.backend.minimexoauth) separate from catalog-driven API-keyminimaxprofiles. It strictly rejectsMINIMAX_API_KEYconfiguration. - Anthropic Messages wire transport: Routes all inference requests via the Anthropic Messages protocol (
POST /v1/messages) withanthropic-version: 2023-06-01andAuthorization: Bearer {token}. Streaming uses Anthropic Server-Sent Events (SSE) message and content block deltas. OpenAI Responses operations (/v1/responsesorOperationOpenAIResponses) are explicitly rejected. - Regional routing: Defaults to global endpoints (portal
https://api.minimax.io, inferencehttps://api.minimax.io/anthropic). Supports China mainland region (region: cn,region: china, orregion: minimax-cn) routing to portalhttps://api.minimaxi.comand inferencehttps://api.minimaxi.com/anthropic. Endpoints can also be overridden explicitly viaportal_base_urlandinference_base_url. - Truthful client identity: Truthfully reports User-Agent
go-llm-interactive-proxy/0.1.0 (minimax-oauth)across all auth and inference requests. Never sends Hermes client tags, never claims Hermes identity, and forbids hardcoded Hermes client IDs in production constructor paths. - Authentication & OAuth lifecycle: File-backed OAuth credentials manage refresh tokens via
oauthcredwith 0600 file permissions and 60s skew margin. Strictly requires configuredoauth_client_idfor OAuth flows. Device/browser code acquisition viaPOST /oauth/codeand polling viaPOST /oauth/tokenwithgrant_type: urn:ietf:params:oauth:grant-type:user_code. Initial login is operator-driven:StartLoginacquires and displays the verification URI + user code,LoginSession.Completepolls for approval and persists the minted credential via the existing credential store (clearing any prior quarantine).Configurefails closed with an actionable run-login-first error when no usable credential exists. Terminal refresh errors (invalid_grant,refresh_token_reused, 4xx) quarantine stored credentials and prevent refresh replay. Stored tokens can be re-established upon fresh login. Literal secrets in configuration YAML are strictly forbidden and rejected. - Entitlement access control: HTTP 403 Forbidden responses from the inference API indicate subscription tier or quota denials and fail closed immediately without entering token refresh loops. Transient HTTP 401 Unauthorized responses trigger token refresh once and retry.
- Dynamic model catalog: Queries
GET /v1/modelson the inference endpoint to dynamically discover available models alongside defaultsMiniMax-M2.7andMiniMax-M2.7-highspeed, prefixing canonical model IDs asminimax-oauth/{id}. Dynamic discovery queries fail closed on non-200 responses.
| Symptom | Likely cause | Action |
|---|---|---|
| Unsafe execution composition error | Mixed agent_runtime / unknown backend in composite route selector |
Use direct routing for agent runtimes, or set routing.execution_composition_policy: unrestricted if intended |
| Kind missing in inspect | Artifact not under trusted paths, or discovery enabled: false |
Install manifest+bin; fix paths; re-run inspect |
| Unknown field / invalid manifest | Closed schema violation | Fix manifest; unknown keys are rejected |
| Digest mismatch | File rewritten after package | Re-package; do not hand-edit binaries |
| Peer/channel failure in doctor | IPC/ACL/profile mismatch | Fix install permissions/ACLs; do not disable peer checks |
| Configured-missing fail-closed | Enabled backend kind not discovered | Install artifact or disable the row |
| Local-only rejected | access.mode: multi_user |
Single-user loopback or different connector |
| User OAuth credential rejected | credential_mode: oauth_user in access.mode: multi_user |
Single-user loopback, or a shared-service credential backend |
| Backend not approved for multi-user | Factory absent from the host-owned approval registry (including a brand-new connector) | Single-user loopback, or request an explicit registry change; there is no config override |
Manifest oauth_user + access_scope: any rejected |
Contradictory manifest semantics | Declare access_scope: local_only |
| Development path ignored | development_mode: false with only loose paths |
Set development_mode: true only for explicit lab paths |
| Wanted “download plugin” | Unsupported | Package offline; copy artifacts — no runtime download |
authoring.md— connector authorsdocs/dogfood-local.md— no-key stub workflowEchoesVault/pages/backend-connector-plugins.md