Secrets

Public API for the secrets subsystem, covering credential resolution across multiple providers such as kwargs, configuration files, keyring backends, and SOPS encrypted payloads.

Tip

For usage guide and examples, see Secrets.

Quick overview

  • resolve_secret(name) is the main entry point for resolving a secret by name.

  • SecretResolver orchestrates credential resolution across multiple providers.

  • sensitive decorator marks functions whose return values should not be logged.

  • Exceptions distinguish not-found (SecretNotFoundError) from decryption failures (SecretDecryptionError).


Resolver

resolve_secret

kstlib.secrets.resolve_secret(name: 'str', *, config: 'Mapping[str, Any] | None' = None, secrets: 'Mapping[str, Any] | None' = None, **request_kwargs: 'Any') 'SecretRecord' -> SecretRecord[source]

Resolve a secret by name using the global resolver cascade.

Parameters:
  • name (str) – Identifier of the secret ("smtp.password" for example).

  • config (Mapping[str, Any] | None) – Optional configuration mapping describing providers. When not provided the function attempts to reuse the globally loaded config.

  • secrets (Mapping[str, Any] | None) – Optional mapping of secret overrides. These take precedence over all other providers (useful for testing).

  • request_kwargs (Any) – Additional keyword arguments forwarded to SecretRequest. Supported keys are scope, required, default and metadata.

Returns:

A SecretRecord describing the resolved secret and its provenance.

Raises:
  • SecretNotFoundError – If the secret is not found and required=True.

  • TypeError – If unsupported keyword arguments are provided.

Return type:

SecretRecord

Example

>>> from kstlib.secrets.resolver import resolve_secret
>>> # Override for testing
>>> record = resolve_secret("api.key", secrets={"api.key": "test-value"})
>>> record.source
<SecretSource.KWARGS: 'kwargs'>

get_secret_resolver

kstlib.secrets.get_secret_resolver(config: 'Mapping[str, Any] | None' = None, *, secrets: 'Mapping[str, Any] | None' = None) 'SecretResolver' -> SecretResolver[source]

Build a resolver from configuration mapping.

When no explicit provider list is given, the default cascade is: (KwargsProvider if secrets) -> EnvironmentProvider -> KeyringProvider -> (SOPSProvider if configured).

Parameters:
  • config (Mapping[str, Any] | None) – Optional mapping with providers list and/or sops settings. When None, uses the default provider chain.

  • secrets (Mapping[str, Any] | None) – Optional mapping of secret overrides to inject via KwargsProvider.

Returns:

Configured SecretResolver instance.

Return type:

SecretResolver

Example

>>> from kstlib.secrets.resolver import get_secret_resolver
>>> resolver = get_secret_resolver()  # uses defaults
>>> resolver.name
'default'

SecretResolver

class kstlib.secrets.SecretResolver(providers, *, name=None)[source]

Bases: object

Resolve secrets by delegating to a sequence of providers.

The resolver iterates through providers in order until one returns a value. If no provider can resolve the secret and no default is given, a SecretNotFoundError is raised (when required=True).

Example

>>> from kstlib.secrets.resolver import SecretResolver
>>> from kstlib.secrets.providers import get_provider
>>> from kstlib.secrets.models import SecretRequest
>>> resolver = SecretResolver([get_provider("environment")], name="app")
>>> # resolver.resolve(SecretRequest(name="API_KEY"))
__init__(self, providers: 'Sequence[SecretProvider]', *, name: 'str | None' = None) 'None' -> None[source]

Initialise the resolver with a provider cascade.

Parameters:
  • providers (Sequence[SecretProvider]) – Ordered sequence of secret providers to query.

  • name (str | None) – Human-readable name for this resolver (used in error messages).

property name: str

Return the resolver name.

resolve(self, request: 'SecretRequest') 'SecretRecord' -> SecretRecord[source]

Resolve the secret using the configured provider cascade.

Parameters:

request (SecretRequest) – The secret request to resolve.

Returns:

A SecretRecord with the resolved value and metadata.

Raises:

SecretNotFoundError – If the secret is not found and required=True.

Return type:

SecretRecord

async resolve_async(self, request: 'SecretRequest') 'SecretRecord' -> SecretRecord[source]

Async counterpart for resolve.

Parameters:

request (SecretRequest) – The secret request to resolve.

Returns:

A SecretRecord with the resolved value and metadata.

Raises:

SecretNotFoundError – If the secret is not found and required=True.

Return type:

SecretRecord


Models

SecretRecord

class kstlib.secrets.SecretRecord(value, source, metadata=<factory>)[source]

Bases: object

Represents the value returned by the resolver.

value

The secret itself.

Type:

Any

source

The origin of the secret.

Type:

kstlib.secrets.models.SecretSource

metadata

Provider specific metadata (e.g. timestamp, path, ttl).

Type:

Mapping[str, Any]

value: Any
source: SecretSource
metadata: Mapping[str, Any]
__init__(self, value: 'Any', source: 'SecretSource', metadata: 'Mapping[str, Any]' = <factory>) None -> None

SecretRequest

class kstlib.secrets.SecretRequest(name, scope=None, required=True, default=None, metadata=<factory>)[source]

Bases: object

Describes a secret lookup request.

name

Identifier of the secret (e.g. “smtp.password”).

Type:

str

scope

Optional scope that providers can exploit for namespacing.

Type:

str | None

required

Whether the resolver must raise if the secret is missing.

Type:

bool

default

Optional fallback value when the secret is not required.

Type:

Any | None

metadata

Arbitrary provider hints (e.g. keyring namespace).

Type:

MutableMapping[str, Any]

name: str
scope: str | None
required: bool
default: Any | None
metadata: MutableMapping[str, Any]
__init__(self, name: 'str', scope: 'str | None' = None, required: 'bool' = True, default: 'Any | None' = None, metadata: 'MutableMapping[str, Any]' = <factory>) None -> None

SecretSource

class kstlib.secrets.SecretSource(value)[source]

Bases: str, Enum

Enumerates the possible origins for a resolved secret.

KWARGS = 'kwargs'
ENVIRONMENT = 'environment'
KEYRING = 'keyring'
SOPS = 'sops'
KMS = 'kms'
DEFAULT = 'default'
__format__(self, format_spec)

Returns format using actual value type unless __str__ has been overridden.


Sensitive Decorator

sensitive

kstlib.secrets.sensitive(record: 'SecretRecord', *, providers: 'Sequence[CachePurgeProtocol] | None' = None) 'Iterator[Any]' -> Iterator[Any][source]

Temporarily expose a secret and scrub it afterwards.

The context manager yields the secret value so it can be used within the protected block. On exit it attempts to overwrite mutable buffers in place, clears provider caches when available, and replaces the value stored in the SecretRecord with None to break lingering references.

Example

>>> from kstlib.secrets.models import SecretRecord, SecretSource
>>> from kstlib.secrets.sensitive import sensitive
>>> record = SecretRecord(value=bytearray(b"api-token"), source=SecretSource.SOPS)
>>> with sensitive(record) as secret:
...     secret[:3] = b"***"  # handle the secret
>>> record.value is None
True
Parameters:
  • record (SecretRecord) – Secret wrapped in a SecretRecord.

  • providers (Sequence[CachePurgeProtocol] | None) – Optional providers whose caches should be purged after use.

Yields:

The decrypted secret value.

CachePurgeProtocol

class kstlib.secrets.CachePurgeProtocol(*args, **kwargs)[source]

Bases: Protocol

Protocol implemented by providers exposing a cache purge hook.

purge_cache(self, *, path: 'str | Path | None' = None) 'None' -> None[source]

Clear cached decrypted material.

__init__(self, *args, **kwargs)

Exceptions

SecretError

class kstlib.secrets.SecretError[source]

Bases: KstlibError, RuntimeError

Base class for all secrets related errors.

SecretNotFoundError

class kstlib.secrets.SecretNotFoundError[source]

Bases: SecretError

Raised when no provider can supply a requested secret.

SecretDecryptionError

class kstlib.secrets.SecretDecryptionError[source]

Bases: SecretError

Raised when a secret payload cannot be decrypted.