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.
Providers
Section titled “Providers”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.
Presets
Section titled “Presets”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.
Consent picks a set and a default
Section titled “Consent picks a set and a default”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 session names one
Section titled “The session names 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.
Connections
Section titled “Connections”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.