Skip to content

Inference options and connections

A tenant owns what its sessions run on and what they may reach. Two kinds of resource carry that: providers with their presets, which say where a session’s inferences go and as what, and connections, the MCP servers whose tools a tenant may add to an install. The vendor brings neither; the tenant’s owner keeps both in the console and chooses among them at consent.

A provider is where inference requests go: its kind (OpenRouter today), an optional base URL (absent, the deployment’s configured OpenRouter URL) and its API key. The key goes in once, with PUT /providers/{id}, is sealed at the door and rests sealed; no response ever carries it, and a replacement that leaves it out keeps the one the provider holds.

PUT /providers/openrouter
{ "kind": "openrouter", "secret": "sk-or-…" }

GET /providers and GET /providers/{id} read a provider’s id, kind and base URL under the console token. A provider that presets still run on cannot be deleted; the refusal names them.

An inference option is a preset on a provider: a name such as fast or smart, the provider’s model slug, default generation parameters (temperature and friends), the prompt-token count at which the session compacts, and how a transient provider failure is retried. A provider holds as many presets as the tenant wants.

PUT /inferences/fast
{ "provider": "openrouter", "model": "z-ai/glm-5.3-flash", "params": { "temperature": 0.2 } }

A preset is replaced under its name, and every install that consents to it follows from its next session on.

At consent the tenant’s owner selects a set of presets from every provider the tenant keeps and names one as the default; the default must be in the set. Both land in the install’s tenant configuration. The tenant decides which models an app may run on: there is no wildcard and no model outside the set.

{ "inference": "smart", "inferences": ["fast", "smart"] }

A consent written before sets existed names its default alone, and consents to exactly that one.

The vendor reads the consented set with its install key on GET /appdoc, or await canopy.appdoc() in the SDK: each preset’s id, name, provider kind, model and compaction threshold, and which one is the default. No secret, and no secret’s name, is ever read there.

A session names a preset when it opens, or runs on the default:

const session = await canopy.open({ principal: "user:42", inference: "fast" });

Open resolves the preset against the consented set. One outside the set is refused with 403 {"error": "inference_not_allowed", "inference": "…"} (InferenceNotAllowed in the SDK) and nothing lands. The preset’s parameters are defaults: every parameter the session’s dial sets overrides the preset’s.

The session’s opening record shows the preset it runs on, by id, with its provider and the parameters it landed with, so audit and metering by model read it as they always have. The choice is fixed for the session’s life; running on another preset is a new session.

A connection is an MCP server the tenant links: a name that namespaces its tools, the endpoint, and either a bearer secret or an authorization the owner grants once in a browser. Linking captures the server’s catalog; consent adds tools from it to an install, and session start checks each consented tool still reads as captured.