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").