Table of Contents

Plugin Credentials

Last updated:

Use CredentialSlotAttribute to publish a single-field API-key slot for a registered plugin toolset or provider. Toolsets resolve the current user's value through IToolCredentialResolver. Model and speech providers receive the resolved value through their provider interface.

Note

A credential declaration describes what users can configure. It never contains the secret itself.

Declare a credential slot

Apply one CredentialSlotAttribute directly to a toolset, model provider, speech recognition provider, or speech synthesis provider class that the plugin registers:

using System.ComponentModel;
using Oscova.Agents;
using Oscova.Agents.Credentials;

[CredentialSlot(
    Key = "example-api",
    Label = "Example API",
    Description = "API key used by the Example tools.",
    EnvVariable = "EXAMPLE_API_KEY")]
public sealed class ExampleToolset(
    IToolCredentialResolver credentialResolver) : ICrewToolset
{
    public const string CredentialKey = "example-api";

    [DisplayName("example_check_configuration")]
    [Description("Checks whether the Example API credential is available.")]
    public async Task<string> CheckConfigurationAsync(
        CancellationToken cancellationToken = default)
    {
        var apiKey = await credentialResolver
            .ResolveAsync(CredentialKey, cancellationToken)
            .ConfigureAwait(false);

        return string.IsNullOrWhiteSpace(apiKey)
            ? "Example API is not configured. Add the Example API credential in Settings > Credentials."
            : "Example API is configured.";
    }
}

The declaration is discovered only when the plugin is enabled and the declaring type is registered. An attribute on an unrelated class is ignored.

The properties are:

  • Key: the stable lookup key. Use a short lowercase service-specific value.
  • Label: the name shown in Credentials.
  • Description: one-line setup guidance.
  • EnvVariable: an optional environment-variable name suggested to the user.

Key, Label, and Description must contain non-whitespace text. Invalid plugin credential metadata prevents all plugin-declared slots from being published, but it does not stop otherwise valid plugins from loading.

Each declaration creates one required, secret field named apiKey in the current user's credential scope. CredentialSlotAttribute does not support custom fields or multiple fields.

Keys and duplicate declarations

Credential keys are matched after trimming and lowercasing. Two toolsets or providers may intentionally share one key, but they then share one user-facing slot and value.

Do not rely on declaration scan order. The first discovered plugin declaration for a key supplies its label, description, and suggested environment variable. Built-in reserved declarations take precedence over a plugin declaration with the same key.

Avoid accidental collisions. If several contributions share a credential, define the key once as a constant and use identical metadata on every declaration.

Resolve a toolset credential

Keep the resolver supplied through AgentPluginContext and resolve the credential when an operation runs:

var apiKey = await credentialResolver
    .ResolveAsync(ExampleToolset.CredentialKey, cancellationToken)
    .ConfigureAwait(false);

For a declared plugin slot, the single-field overload is the normal choice. The named-field overload is equivalent when called with "apiKey":

var apiKey = await credentialResolver.ResolveAsync(
    CredentialKey,
    "apiKey",
    cancellationToken);

Credential keys are normalized, but field names are exact and case-sensitive. The resolver returns null when the credential, field, stored value, referenced environment variable, or stored value's unlocked user key is unavailable. Treat a locked credential exactly like other missing setup. Cancellation and unexpected storage failures can propagate to the caller.

Receive a provider credential

Provider factories receive an AgentPluginContext for HTTP and logging capabilities, but that context does not expose user credential values. When a model or speech configuration selects a credential, Oscova resolves it for the current user. Provider operations that need credentials receive the plaintext value through their string? apiKey parameter.

The attribute publishes the slot but does not automatically select it in a provider configuration. Treat a null or blank apiKey as missing setup and follow the failure contract of the corresponding IModelProvider, ISpeechRecognitionProvider, or ISpeechSynthesisProvider method.

Storage and trust boundaries

Declared slots belong to the current user. A stored secret is encrypted with that user's in-memory confidential-data key and is not displayed again. After restart it resolves as unavailable until the user unlocks. An environment-backed slot does not use that key: it stores only the variable name and reads its value from the Oscova host process when the resolver is called.

The resolver returns plaintext to the trusted plugin operation. Keep that value in the smallest possible scope:

  • Do not store it in plugin files, settings, static fields, or long-lived caches.
  • Do not include it in tool arguments, results, exceptions, URLs, or logs.
  • Send it only to the intended service, preferably in an authorization header.
  • Resolve it again on the next operation so user rotation takes effect.

The supported toolset resolver accepts only keys declared directly by that plugin's registered toolset and provider types. Requests for other keys return null without reading credential storage. This prevents accidental cross-plugin access through the framework context, but it is not hostile-code isolation. Plugins remain trusted in-process code, so administrators must install only plugins they trust.

Oscova applies redaction to known user secrets and secret-shaped tool arguments at execution boundaries, but redaction is defense in depth. It does not make logging or returning a secret safe.

Failure guidance

  • Missing value: name the slot and direct the user to Settings > Credentials.
  • Unset environment variable: ask the user to verify the variable in the host process environment.
  • Unauthorized provider response: say that the configured credential is invalid or expired without echoing the response body.
  • Cancellation: allow OperationCanceledException to propagate.
  • Remote or parse failure: log service and operation context, never the credential value.