Documentation
Table of Contents

Provider secrets

An entry of the provider file never holds its key: it names where the key is read from, and its endpoint is the address the key is sent to. This page covers secret references, a resolver of your own, endpoints and plain http, and the edits flagged as widening. Provider file covers the entries, the roles, the model aliases and the hosts a call may reach.

Secret references

An entry names its key by reference. env:NAME reads the environment variable NAME; config:Key reads Key from the application's configuration, under AddIntentSharp only. With the default resolver, any other scheme does not load. A key is read on the provider's first request; a reference that resolves to nothing fails the call with ProviderConfigurationException, naming the provider and the reference, before anything is sent.

A field that holds a key itself, such as apiKey, token or password, does not load; the problem names the field and never repeats the value. The file names references, never keys, and a version with a key in such a field is refused. Keep the .intentsharp folder the store writes beside the file out of source control.

A resolver of your own

The default resolver reads any environment variable and, under AddIntentSharp, any configuration key, so a file can name any value the process holds, a connection string or your license key included, and the entry's endpoint decides where it is sent. To bound what a file may name, give the client a resolver of your own with UseSecretResolver. It receives every reference, and the default resolver is not used:

/// <summary>Resolves only the references this application allows; any other reference resolves to nothing.</summary>
public sealed class AllowedSecrets(params string[] allowed) : ISecretResolver
{
    public ValueTask<string?> ResolveAsync(string reference, CancellationToken ct = default) =>
        ValueTask.FromResult(
            allowed.Contains(reference, StringComparer.Ordinal) && reference.StartsWith("env:", StringComparison.Ordinal)
                ? Environment.GetEnvironmentVariable(reference["env:".Length..])
                : null);
}
var client = new IntentClientBuilder()
    .UseProviderOptions(store)
    .UseSecretResolver(new AllowedSecrets("env:ANTHROPIC_API_KEY", "env:OPENAI_API_KEY"))
    .Build();

Under dependency injection, register it as an ISecretResolver service.

Endpoints and plain http

An endpoint is an absolute http or https address. It holds no user name or key: one that does, such as https://user:key@host/, does not load, and the problem never repeats the address. A query, such as ?api-version=2024-10-21, is sent with every request, so put no key in it either.

An entry with a secret and a plain http endpoint loads only when the host is loopback, localhost, an address in 127.0.0.0/8 or ::1, since its key would otherwise cross the network unencrypted. No other name counts, so a container host such as ollama does not. This file does not load:

{
  "intentsharp": "providers/1",
  "providers": [
    { "name": "gateway", "kind": "openai", "endpoint": "http://gateway.internal:8080/v1/", "secret": "env:GATEWAY_KEY", "model": "<model id>" }
  ],
  "roles": { "answer": "gateway" }
}

Add "allowInsecureHttp": true to the entry to accept that its key travels in clear text, or use https. Entries your own IProviderOptionsSource builds are not checked.

Edits that widen

The provider file is a document: a saved version applies to the next call, with no restart, and a version that does not load is reported while the version before it keeps running. Activating an earlier version reverts it.

An entry's endpoint, secret, kind and key decide where its key and its calls go. A version that changes one of them, or turns allowInsecureHttp on, is applied and flagged as widening: EditImpact.WidensSecurity is true, and WidenedSubjects names the item, such as providers.backup.endpoint. A new or renamed entry widens too, and a removed one does not. A change of a model or of a role's chain widens nothing. A writer of your own can hold the flagged edits for review (Guardrails).

The output starts with the refusal of the file above, then shows two edits submitted through the client: a model change, and an endpoint and secret change. It ends with two calls a rule on request.host refuses (Provider file):

The entry gateway: plain http, a secret and no allowInsecureHttp:
  intentsharp.providers.json:4: providers[0].endpoint is a plain http address of a host that is not loopback, so the entry's secret would be sent unencrypted; use https, or set allowInsecureHttp: true on the entry to accept that.
An edit of the model of main:
  Status: Applied, WidensSecurity: false
An edit of the endpoint and the secret of backup:
  Status: Applied, WidensSecurity: true
  providers.backup.endpoint
  providers.backup.secret
A call to gateway.example.net, under the set hosts:
  Success: false
  [Guardrail] attempt 1: The answer call was refused at the Request checkpoint: Calls go only to the hosts this application uses (rule known-hosts in guardrail set "hosts").
A call whose provider reports no host, under the set hosts:
  Success: false
  [Guardrail] attempt 1: The answer call was refused at the Request checkpoint: Calls go only to the hosts this application uses (rule known-hosts in guardrail set "hosts").