Secret Stores๏ƒ

Secret store integration for Confii.

This module provides a pluggable architecture for integrating various secret storage systems (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, HashiCorp Vault, etc.) into your configuration management.

Example

>>> from confii import Config
>>> from confii.secret_stores import AWSSecretsManager, SecretResolver
>>>
>>> # Initialize secret store
>>> secret_store = AWSSecretsManager(region_name="us-east-1")
>>>
>>> # Use with Config
>>> config = Config(env="production", secret_resolver=SecretResolver(secret_store))
>>>
>>> # Secrets are automatically resolved from placeholders using the format
>>> # "$" + "{" + "secret" + ":" + "db/password" + "}"
>>> db_password = config.database.password
class confii.secret_stores.AWSSecretsManager(region_name: str = 'us-east-1', aws_access_key_id: str | None = None, aws_secret_access_key: str | None = None, aws_session_token: str | None = None, endpoint_url: str | None = None)[source]๏ƒ

Bases: SecretStore

AWS Secrets Manager secret store provider.

This provider integrates with AWS Secrets Manager to securely retrieve secrets. It supports: - JSON and plaintext secrets - Secret versioning - Automatic JSON parsing - Key-based access to JSON secrets - Caching through boto3

Prerequisites:

pip install boto3

AWS Credentials:

Credentials are resolved using boto3โ€™s standard credential chain: 1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) 2. ~/.aws/credentials file 3. IAM role (when running on EC2, ECS, Lambda, etc.)

Example

>>> from confii import Config
>>> from confii.secret_stores import AWSSecretsManager, SecretResolver
>>>
>>> # Initialize with region
>>> store = AWSSecretsManager(region_name="us-east-1")
>>>
>>> # Or with explicit credentials
>>> store = AWSSecretsManager(
...     region_name="us-east-1",
...     aws_access_key_id="YOUR_KEY",
...     aws_secret_access_key="YOUR_SECRET",
... )
>>>
>>> # Use with Config
>>> config = Config(secret_resolver=SecretResolver(store))
>>>
>>> # In config file: database.password = "${secret:prod/db/password}"
>>> # Or for JSON secrets: api.key = "${secret:prod/api:key}"
__init__(region_name: str = 'us-east-1', aws_access_key_id: str | None = None, aws_secret_access_key: str | None = None, aws_session_token: str | None = None, endpoint_url: str | None = None) None[source]๏ƒ

Initialize AWS Secrets Manager client.

Parameters:
  • region_name โ€“ AWS region name (default: โ€˜us-east-1โ€™).

  • aws_access_key_id โ€“ Optional AWS access key ID.

  • aws_secret_access_key โ€“ Optional AWS secret access key.

  • aws_session_token โ€“ Optional AWS session token for temporary credentials.

  • endpoint_url โ€“ Optional custom endpoint URL (for testing with LocalStack).

Raises:

ImportError โ€“ If boto3 is not installed.

Example

>>> # Use default credentials
>>> store = AWSSecretsManager(region_name="eu-west-1")
>>>
>>> # Use explicit credentials
>>> store = AWSSecretsManager(
...     region_name="us-east-1",
...     aws_access_key_id="AKIAIOSFODNN7EXAMPLE",
...     aws_secret_access_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
... )
>>>
>>> # Use with LocalStack for testing
>>> store = AWSSecretsManager(
...     region_name="us-east-1", endpoint_url="http://localhost:4566"
... )
__repr__() str[source]๏ƒ

String representation of the store.

delete_secret(key: str, **kwargs) None[source]๏ƒ

Delete a secret from AWS Secrets Manager.

Parameters:
  • key โ€“ The secret name to delete.

  • **kwargs โ€“ Additional parameters: - ForceDeleteWithoutRecovery: Skip recovery window (default: False) - RecoveryWindowInDays: Recovery period in days (7-30, default: 30)

Raises:
  • SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other AWS errors.

Example

>>> store = AWSSecretsManager(region_name="us-east-1")
>>>
>>> # Delete with recovery window
>>> store.delete_secret("old/api/key", RecoveryWindowInDays=7)
>>>
>>> # Force delete immediately (no recovery)
>>> store.delete_secret("temp/key", ForceDeleteWithoutRecovery=True)
get_secret(key: str, version: str | None = None, **kwargs) Any[source]๏ƒ

Retrieve a secret from AWS Secrets Manager.

Parameters:
  • key โ€“ The secret name or ARN. Can include โ€œ:json_keyโ€ suffix to extract a specific key from JSON secrets (e.g., โ€œmy-secret:database_passwordโ€).

  • version โ€“ Optional version ID or version stage (e.g., โ€œAWSCURRENTโ€, โ€œAWSPENDINGโ€).

  • **kwargs โ€“ Additional parameters for get_secret_value API call.

Returns:

The secret value. For JSON secrets, returns the parsed dict. For secrets with โ€œ:keyโ€ suffix, returns the specific value.

Raises:
  • SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other AWS errors.

Example

>>> store = AWSSecretsManager(region_name="us-east-1")
>>>
>>> # Get plaintext secret
>>> password = store.get_secret("prod/db/password")
>>>
>>> # Get JSON secret
>>> config = store.get_secret("prod/api/config")
>>> # Returns: {"key": "abc123", "endpoint": "https://api.example.com"}
>>>
>>> # Get specific version
>>> old_key = store.get_secret("api/key", version="v1-guid-here")
get_secret_metadata(key: str) Dict[str, Any][source]๏ƒ

Get metadata about a secret.

Parameters:

key โ€“ The secret name.

Returns:

Dictionary with secret metadata.

Raises:
  • SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

  • SecretAccessError โ€“ If thereโ€™s a permission error.

Example

>>> store = AWSSecretsManager(region_name="us-east-1")
>>> metadata = store.get_secret_metadata("prod/db/password")
>>> print(f"Created: {metadata['CreatedDate']}")
>>> print(f"Version: {metadata['VersionIdsToStages']}")
list_secrets(prefix: str | None = None, **kwargs) List[str][source]๏ƒ

List secrets in AWS Secrets Manager.

Parameters:
  • prefix โ€“ Optional name prefix to filter secrets.

  • **kwargs โ€“ Additional parameters for list_secrets API call.

Returns:

List of secret names.

Raises:
  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other AWS errors.

Example

>>> store = AWSSecretsManager(region_name="us-east-1")
>>>
>>> # List all secrets
>>> all_secrets = store.list_secrets()
>>>
>>> # List secrets with prefix
>>> prod_secrets = store.list_secrets(prefix="prod/")
set_secret(key: str, value: Any, **kwargs) None[source]๏ƒ

Create or update a secret in AWS Secrets Manager.

Parameters:
  • key โ€“ The secret name.

  • value โ€“ The secret value. Dicts will be JSON-encoded automatically.

  • **kwargs โ€“ Additional parameters for create_secret/update_secret: - Description: Secret description - KmsKeyId: KMS key for encryption - Tags: List of tags

Raises:
  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other AWS errors.

Example

>>> store = AWSSecretsManager(region_name="us-east-1")
>>>
>>> # Store plaintext secret
>>> store.set_secret("dev/api/key", "abc123")
>>>
>>> # Store JSON secret
>>> store.set_secret(
...     "dev/db/config",
...     {"host": "localhost", "port": 5432, "password": "secret"},
... )
class confii.secret_stores.AzureKeyVault(vault_url: str, credential: Any | None = None)[source]๏ƒ

Bases: SecretStore

Azure Key Vault secret store provider.

This provider integrates with Azure Key Vault to retrieve secrets.

Prerequisites:

pip install azure-keyvault-secrets azure-identity

Authentication:

Uses DefaultAzureCredential which supports: - Environment variables (AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET) - Managed Identity (when running in Azure) - Azure CLI credentials - Visual Studio Code credentials

Example

>>> from confii import Config
>>> from confii.secret_stores import AzureKeyVault, SecretResolver
>>>
>>> # Initialize with vault URL
>>> store = AzureKeyVault(vault_url="https://my-vault.vault.azure.net")
>>>
>>> # Use with Config
>>> config = Config(secret_resolver=SecretResolver(store))
>>>
>>> # In config file: database.password = "${"secret" + ":" + "db-password"}"
>>> # Note: Azure Key Vault secret names must match regex: ^[0-9a-zA-Z-]+$
__init__(vault_url: str, credential: Any | None = None) None[source]๏ƒ

Initialize Azure Key Vault client.

Parameters:
  • vault_url โ€“ Azure Key Vault URL (e.g., โ€˜https://my-vault.vault.azure.netโ€™).

  • credential โ€“ Optional Azure credential. If None, uses DefaultAzureCredential.

Raises:
  • ImportError โ€“ If Azure SDK is not installed.

  • SecretAccessError โ€“ If authentication fails.

Example

>>> # Use default credentials
>>> store = AzureKeyVault(vault_url="https://my-vault.vault.azure.net")
>>>
>>> # Use specific credential
>>> from azure.identity import ClientSecretCredential
>>> cred = ClientSecretCredential(
...     tenant_id="...", client_id="...", client_secret="..."
... )
>>> store = AzureKeyVault(
...     vault_url="https://my-vault.vault.azure.net", credential=cred
... )
__repr__() str[source]๏ƒ

String representation of the store.

delete_secret(key: str, **kwargs) None[source]๏ƒ

Delete a secret from Azure Key Vault.

This begins a deletion process. The secret is recoverable during the retention period.

Parameters:
  • key โ€“ The secret name to delete.

  • **kwargs โ€“ Additional parameters.

Raises:
  • SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

  • SecretAccessError โ€“ If thereโ€™s a permission error.

Example

>>> store = AzureKeyVault(vault_url="https://my-vault.vault.azure.net")
>>> store.delete_secret("old-api-key")
get_secret(key: str, version: str | None = None, **kwargs) Any[source]๏ƒ

Retrieve a secret from Azure Key Vault.

Parameters:
  • key โ€“ The secret name. Note: Azure Key Vault names must match ^[0-9a-zA-Z-]+$

  • version โ€“ Optional secret version ID. If None, gets latest.

  • **kwargs โ€“ Additional parameters.

Returns:

The secret value as a string.

Raises:
  • SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other Azure errors.

Example

>>> store = AzureKeyVault(vault_url="https://my-vault.vault.azure.net")
>>>
>>> # Get latest version
>>> password = store.get_secret("db-password")
>>>
>>> # Get specific version
>>> old_password = store.get_secret("db-password", version="abc123...")
get_secret_metadata(key: str) Dict[str, Any][source]๏ƒ

Get metadata about a secret.

Parameters:

key โ€“ The secret name.

Returns:

Dictionary with secret metadata.

Raises:

SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

Example

>>> store = AzureKeyVault(vault_url="https://my-vault.vault.azure.net")
>>> metadata = store.get_secret_metadata("db-password")
>>> print(f"Created: {metadata['created_on']}")
list_secrets(prefix: str | None = None, **kwargs) List[str][source]๏ƒ

List secrets in Azure Key Vault.

Parameters:
  • prefix โ€“ Optional prefix to filter secrets.

  • **kwargs โ€“ Additional parameters.

Returns:

List of secret names.

Example

>>> store = AzureKeyVault(vault_url="https://my-vault.vault.azure.net")
>>> all_secrets = store.list_secrets()
>>> prod_secrets = store.list_secrets(prefix="prod-")
set_secret(key: str, value: Any, **kwargs) None[source]๏ƒ

Store a secret in Azure Key Vault.

Parameters:
  • key โ€“ The secret name. Must match regex: ^[0-9a-zA-Z-]+$

  • value โ€“ The secret value (will be converted to string).

  • **kwargs โ€“ Additional parameters (tags, content_type, etc.).

Raises:
  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other Azure errors.

Example

>>> store = AzureKeyVault(vault_url="https://my-vault.vault.azure.net")
>>> store.set_secret("api-key", "abc123", tags={"env": "prod"})
class confii.secret_stores.DictSecretStore(secrets: Dict[str, Any] | None = None)[source]๏ƒ

Bases: SecretStore

In-memory dictionary-based secret store.

This is a simple secret store implementation that stores secrets in a Python dictionary. Itโ€™s ideal for: - Testing and development - Unit tests - Local development without external dependencies - Simple applications that donโ€™t need external secret management

Warning

This store keeps secrets in memory and provides no encryption or persistence. Do NOT use in production for sensitive secrets.

Example

>>> from confii.secret_stores import DictSecretStore, SecretResolver
>>> from confii import Config
>>>
>>> # Create store with secrets
>>> secrets = DictSecretStore(
...     {
...         "database/password": "dev-password",
...         "api/key": "test-api-key-123",
...         "redis/config": {
...             "host": "localhost",
...             "port": 6379,
...             "password": "redis-pass",
...         },
...     }
... )
>>>
>>> # Use with Config
>>> config = Config(env="development", secret_resolver=SecretResolver(secrets))
__contains__(key: str) bool[source]๏ƒ

Check if a secret exists in the store.

__init__(secrets: Dict[str, Any] | None = None) None[source]๏ƒ

Initialize the dictionary secret store.

Parameters:

secrets โ€“ Optional dictionary of secrets to initialize with. Keys are secret names, values are secret values.

Example

>>> store = DictSecretStore({"api/key": "abc123", "db/password": "secret"})
__len__() int[source]๏ƒ

Return the number of secrets in the store.

__repr__() str[source]๏ƒ

String representation of the store.

clear() None[source]๏ƒ

Clear all secrets from the store.

This is useful for testing scenarios where you want to reset state.

Example

>>> store = DictSecretStore({"key1": "value1"})
>>> store.clear()
>>> store.list_secrets()  # Returns []
delete_secret(key: str, **kwargs) None[source]๏ƒ

Delete a secret from the dictionary.

Parameters:
  • key โ€“ The secret key/name to delete.

  • **kwargs โ€“ Optional parameters: - delete_versions: If True, also deletes version history (default: True)

Raises:

SecretNotFoundError โ€“ If the secret key doesnโ€™t exist.

Example

>>> store = DictSecretStore({"api/key": "abc123"})
>>> store.delete_secret("api/key")
get_secret(key: str, version: str | None = None, **kwargs) Any[source]๏ƒ

Retrieve a secret from the dictionary.

Parameters:
  • key โ€“ The secret key/name.

  • version โ€“ Optional version number (0-indexed). If not provided, returns the latest version.

  • **kwargs โ€“ Ignored for DictSecretStore.

Returns:

The secret value.

Raises:

SecretNotFoundError โ€“ If the secret key doesnโ€™t exist or version is out of range.

Example

>>> store = DictSecretStore({"api/key": "abc123"})
>>> key = store.get_secret("api/key")
>>> print(key)  # "abc123"
get_secret_metadata(key: str) Dict[str, Any][source]๏ƒ

Get metadata about a secret.

Parameters:

key โ€“ The secret key/name.

Returns:

Dictionary with metadata including version count.

Raises:

SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

Example

>>> store = DictSecretStore({"api/key": "abc123"})
>>> metadata = store.get_secret_metadata("api/key")
>>> print(metadata["version_count"])
list_secrets(prefix: str | None = None, **kwargs) List[str][source]๏ƒ

List all secrets or secrets matching a prefix.

Parameters:
  • prefix โ€“ Optional prefix to filter secrets.

  • **kwargs โ€“ Ignored for DictSecretStore.

Returns:

List of secret keys.

Example

>>> store = DictSecretStore(
...     {
...         "database/password": "pass1",
...         "database/user": "admin",
...         "api/key": "key1",
...     }
... )
>>> all_secrets = store.list_secrets()
>>> db_secrets = store.list_secrets(prefix="database/")
set_secret(key: str, value: Any, **kwargs) None[source]๏ƒ

Store a secret in the dictionary.

Parameters:
  • key โ€“ The secret key/name.

  • value โ€“ The secret value to store.

  • **kwargs โ€“ Optional parameters: - keep_versions: If True, keeps previous versions (default: False)

Example

>>> store = DictSecretStore()
>>> store.set_secret("api/key", "new-key-123")
>>> store.set_secret("api/key", "newer-key-456", keep_versions=True)
update(secrets: Dict[str, Any]) None[source]๏ƒ

Bulk update secrets from a dictionary.

Parameters:

secrets โ€“ Dictionary of secrets to add/update.

Example

>>> store = DictSecretStore()
>>> store.update({"api/key": "abc123", "db/password": "secret"})
class confii.secret_stores.EnvSecretStore(prefix: str = '', suffix: str = '', transform_key: bool = True, case_sensitive: bool = False)[source]๏ƒ

Bases: SecretStore

Secret store that reads from environment variables.

This provider allows you to use environment variables as a secret source, which is useful for: - CI/CD pipelines - Container-based deployments (Docker, Kubernetes) - Local development - Integration with .env files via EnvFileLoader

The store can optionally transform secret keys before looking them up in the environment (e.g., converting โ€œapi/keyโ€ to โ€œAPI_KEYโ€).

Example

>>> import os
>>> from confii.secret_stores import EnvSecretStore, SecretResolver
>>> from confii import Config
>>>
>>> # Set environment variables
>>> os.environ["DB_PASSWORD"] = "secret123"
>>> os.environ["API_KEY"] = "abc123"
>>>
>>> # Create store with key transformation
>>> store = EnvSecretStore(
...     prefix="",
...     transform_key=True,  # "db/password" -> "DB_PASSWORD"
... )
>>>
>>> # Use with Config
>>> config = Config(secret_resolver=SecretResolver(store))
>>>
>>> # In config file, use format with placeholder syntax
>>> # Example: database.password = "$" + "{" + "db/password" + "}"
>>> # This resolves to os.environ['DB_PASSWORD']
__init__(prefix: str = '', suffix: str = '', transform_key: bool = True, case_sensitive: bool = False) None[source]๏ƒ

Initialize the environment variable secret store.

Parameters:
  • prefix โ€“ Prefix to prepend to all environment variable names. If prefix=โ€MYAPPโ€ is provided, it will look for โ€œMYAPP_API_KEYโ€.

  • suffix โ€“ Suffix to append to all environment variable names.

  • transform_key โ€“ If True, transforms keys for environment variable lookup. The transformation process: - Replaces โ€œ/โ€ and โ€œ.โ€ with โ€œ_โ€ - Converts to uppercase (if not case_sensitive) For example, โ€œapi/keyโ€ becomes โ€œAPI_KEYโ€. If False, uses the key as-is.

  • case_sensitive โ€“ If True, preserves case when looking up variables. Only applicable when transform_key=False (default: False).

Examples

>>> # Look for variables with a specific prefix
>>> store = EnvSecretStore(prefix="MYAPP")
>>>
>>> # Custom transformation
>>> store = EnvSecretStore(transform_key=True)
__repr__() str[source]๏ƒ

String representation of the store.

delete_secret(key: str, **kwargs) None[source]๏ƒ

Delete a secret from environment variables.

Parameters:
  • key โ€“ The secret key to delete.

  • **kwargs โ€“ Ignored for EnvSecretStore.

Raises:

SecretNotFoundError โ€“ If the environment variable doesnโ€™t exist.

Example

>>> store = EnvSecretStore()
>>> store.delete_secret("api/key")
>>> # Removes API_KEY from os.environ
get_secret(key: str, version: str | None = None, **kwargs) Any[source]๏ƒ

Retrieve a secret from environment variables.

Parameters:
  • key โ€“ The secret key, will be transformed based on store settings.

  • version โ€“ Ignored for EnvSecretStore (env vars donโ€™t have versions).

  • **kwargs โ€“ Ignored for EnvSecretStore.

Returns:

The environment variable value as a string.

Raises:

SecretNotFoundError โ€“ If the environment variable doesnโ€™t exist.

Example

>>> os.environ["API_KEY"] = "secret123"
>>> store = EnvSecretStore()
>>> key = store.get_secret("api/key")
>>> print(key)  # "secret123"
get_secret_metadata(key: str) Dict[str, Any][source]๏ƒ

Get metadata about an environment variable.

Parameters:

key โ€“ The secret key.

Returns:

Metadata dictionary with the environment variable name and value length.

Raises:

SecretNotFoundError โ€“ If the environment variable doesnโ€™t exist.

Example

>>> os.environ["API_KEY"] = "secret123"
>>> store = EnvSecretStore()
>>> metadata = store.get_secret_metadata("api/key")
>>> print(metadata)
{'key': 'api/key', 'env_var_name': 'API_KEY', 'value_length': 9}
list_secrets(prefix: str | None = None, **kwargs) List[str][source]๏ƒ

List all environment variables matching the pattern.

Parameters:
  • prefix โ€“ Optional prefix to filter results (applied AFTER transformation).

  • **kwargs โ€“ Ignored for EnvSecretStore.

Returns:

List of environment variable names.

Note

This returns the actual environment variable names, not the reverse-transformed secret keys. To get secret keys, you would need to implement reverse transformation.

Example

>>> os.environ["API_KEY"] = "value1"
>>> os.environ["DB_PASSWORD"] = "value2"
>>> store = EnvSecretStore(prefix="")
>>> secrets = store.list_secrets()
>>> # Returns all environment variables
set_secret(key: str, value: Any, **kwargs) None[source]๏ƒ

Store a secret as an environment variable.

Parameters:
  • key โ€“ The secret key, will be transformed based on store settings.

  • value โ€“ The secret value to store (will be converted to string).

  • **kwargs โ€“ Ignored for EnvSecretStore.

Example

>>> store = EnvSecretStore()
>>> store.set_secret("api/key", "new-secret")
>>> print(os.environ["API_KEY"])  # "new-secret"
class confii.secret_stores.GCPSecretManager(project_id: str, credentials: Any | None = None)[source]๏ƒ

Bases: SecretStore

Google Cloud Secret Manager secret store provider.

This provider integrates with GCP Secret Manager to retrieve secrets.

Prerequisites:

pip install google-cloud-secret-manager

Authentication:

Uses Application Default Credentials (ADC): - GOOGLE_APPLICATION_CREDENTIALS environment variable pointing to service account key - gcloud auth application-default login - Automatic when running on GCP (Compute Engine, Cloud Run, etc.)

Example

>>> from confii import Config
>>> from confii.secret_stores import GCPSecretManager, SecretResolver
>>>
>>> # Initialize with project ID
>>> store = GCPSecretManager(project_id="my-gcp-project")
>>>
>>> # Use with Config
>>> config = Config(secret_resolver=SecretResolver(store))
>>>
>>> # In config file: database.password = "${"secret" + ":" + "db-password"}"
>>> # Resolves to: projects/my-gcp-project/secrets/db-password/versions/latest
__init__(project_id: str, credentials: Any | None = None) None[source]๏ƒ

Initialize GCP Secret Manager client.

Parameters:
  • project_id โ€“ GCP project ID.

  • credentials โ€“ Optional google.auth.credentials.Credentials object. If None, uses Application Default Credentials.

Raises:
  • ImportError โ€“ If Google Cloud SDK is not installed.

  • SecretAccessError โ€“ If authentication fails.

Example

>>> # Use default credentials
>>> store = GCPSecretManager(project_id="my-project")
>>>
>>> # Use specific credentials
>>> from google.oauth2 import service_account
>>> creds = service_account.Credentials.from_service_account_file(
...     "path/to/key.json"
... )
>>> store = GCPSecretManager(project_id="my-project", credentials=creds)
__repr__() str[source]๏ƒ

String representation of the store.

delete_secret(key: str, **kwargs) None[source]๏ƒ

Delete a secret from GCP Secret Manager.

This permanently deletes the secret and all of its versions.

Parameters:
  • key โ€“ The secret name to delete.

  • **kwargs โ€“ Additional parameters.

Raises:
  • SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

  • SecretAccessError โ€“ If thereโ€™s a permission error.

Example

>>> store = GCPSecretManager(project_id="my-project")
>>> store.delete_secret("old-api-key")
get_secret(key: str, version: str | None = None, **kwargs) Any[source]๏ƒ

Retrieve a secret from GCP Secret Manager.

Parameters:
  • key โ€“ The secret name (not the full resource path).

  • version โ€“ Optional version identifier. Can be: - Version number (e.g., โ€œ1โ€, โ€œ2โ€) - โ€œlatestโ€ (default if not specified)

  • **kwargs โ€“ Additional parameters.

Returns:

The secret value as bytes or string.

Raises:
  • SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other GCP errors.

Example

>>> store = GCPSecretManager(project_id="my-project")
>>>
>>> # Get latest version
>>> password = store.get_secret("db-password")
>>>
>>> # Get specific version
>>> old_password = store.get_secret("db-password", version="1")
get_secret_metadata(key: str) Dict[str, Any][source]๏ƒ

Get metadata about a secret.

Parameters:

key โ€“ The secret name.

Returns:

Dictionary with secret metadata.

Raises:

SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

Example

>>> store = GCPSecretManager(project_id="my-project")
>>> metadata = store.get_secret_metadata("db-password")
>>> print(f"Created: {metadata['create_time']}")
>>> print(f"Labels: {metadata['labels']}")
list_secrets(prefix: str | None = None, **kwargs) List[str][source]๏ƒ

List secrets in GCP Secret Manager.

Parameters:
  • prefix โ€“ Optional prefix to filter secrets (applied client-side).

  • **kwargs โ€“ Additional parameters.

Returns:

List of secret names (without the full resource path).

Example

>>> store = GCPSecretManager(project_id="my-project")
>>> all_secrets = store.list_secrets()
>>> prod_secrets = store.list_secrets(prefix="prod-")
set_secret(key: str, value: Any, **kwargs) None[source]๏ƒ

Store a secret in GCP Secret Manager.

Parameters:
  • key โ€“ The secret name.

  • value โ€“ The secret value (will be converted to bytes).

  • **kwargs โ€“ Additional parameters: - labels: Dict of labels to attach to the secret

Raises:
  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other GCP errors.

Example

>>> store = GCPSecretManager(project_id="my-project")
>>>
>>> # Create or update secret
>>> store.set_secret("api-key", "abc123", labels={"env": "prod"})
class confii.secret_stores.HashiCorpVault(url: str = 'http://127.0.0.1:8200', token: str | None = None, role_id: str | None = None, secret_id: str | None = None, auth_method: Any | None = None, namespace: str | None = None, mount_point: str = 'secret', kv_version: int = 2, verify: bool = True)[source]๏ƒ

Bases: SecretStore

HashiCorp Vault secret store provider.

This provider integrates with HashiCorp Vault to retrieve secrets from: - KV v1 and KV v2 secrets engines - Dynamic secrets - Custom secrets engines

Prerequisites:

pip install hvac

Example

>>> from confii import Config
>>> from confii.secret_stores import HashiCorpVault, SecretResolver
>>>
>>> # Initialize with token authentication
>>> store = HashiCorpVault(
...     url="http://localhost:8200", token="your-vault-token"
... )
>>>
>>> # Or with AppRole authentication
>>> store = HashiCorpVault(
...     url="http://localhost:8200",
...     role_id="your-role-id",
...     secret_id="your-secret-id",
... )
>>>
>>> # Use with Config
>>> config = Config(secret_resolver=SecretResolver(store))
>>>
>>> # In config file:
>>> # database.password = "${secret:secret/data/db/password:password}"
>>> # For KV v2: secret/data/myapp/db
>>> # For KV v1: secret/myapp/db
__init__(url: str = 'http://127.0.0.1:8200', token: str | None = None, role_id: str | None = None, secret_id: str | None = None, auth_method: Any | None = None, namespace: str | None = None, mount_point: str = 'secret', kv_version: int = 2, verify: bool = True) None[source]๏ƒ

Initialize HashiCorp Vault client.

Parameters:
  • url โ€“ Vault server URL (default: โ€˜http://127.0.0.1:8200โ€™).

  • token โ€“ Vault authentication token (for token auth). Deprecated: use auth_method.

  • role_id โ€“ Role ID for AppRole authentication. Deprecated: use auth_method.

  • secret_id โ€“ Secret ID for AppRole authentication. Deprecated: use auth_method.

  • auth_method โ€“ VaultAuthMethod instance (recommended). Supports: - TokenAuth, AppRoleAuth, OIDCAuth, KerberosAuth, LDAPAuth - JWTAuth, KubernetesAuth, AWSAuth, AzureAuth, GCPAuth

  • namespace โ€“ Vault namespace (Vault Enterprise feature).

  • mount_point โ€“ KV secrets engine mount point (default: โ€˜secretโ€™).

  • kv_version โ€“ KV secrets engine version, 1 or 2 (default: 2).

  • verify โ€“ Verify SSL certificates (default: True).

Raises:
  • ImportError โ€“ If hvac is not installed.

  • SecretAccessError โ€“ If authentication fails.

Example

>>> # Token auth (legacy)
>>> store = HashiCorpVault(
...     url="https://vault.example.com",
...     token="s.1234567890",
...     mount_point="myapp",
...     kv_version=2,
... )
>>>
>>> # AppRole auth (legacy)
>>> store = HashiCorpVault(
...     url="https://vault.example.com",
...     role_id="role-id-here",
...     secret_id="secret-id-here",
... )
>>>
>>> # OIDC with Kerberos (recommended)
>>> from confii.secret_stores.vault_auth import OIDCAuth
>>> auth = OIDCAuth(role="myapp-role", use_kerberos=True)
>>> store = HashiCorpVault(
...     url="https://vault.example.com", auth_method=auth
... )
>>>
>>> # LDAP with PIN+Token
>>> from confii.secret_stores.vault_auth import LDAPAuth
>>> def get_pin_token():
...     pin = getpass.getpass("PIN: ")
...     token = getpass.getpass("Token: ")
...     return pin + token
>>> auth = LDAPAuth(username="user", password_provider=get_pin_token)
>>> store = HashiCorpVault(
...     url="https://vault.example.com", auth_method=auth
... )
__repr__() str[source]๏ƒ

String representation of the store.

delete_secret(key: str, **kwargs) None[source]๏ƒ

Delete a secret from Vault.

For KV v2, this performs a soft delete (can be recovered). Use destroy_secret() for permanent deletion.

Parameters:
  • key โ€“ The secret path to delete.

  • **kwargs โ€“ Additional parameters: - versions: For KV v2, list of versions to delete (default: [latest])

Raises:
  • SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other Vault errors.

Example

>>> store = HashiCorpVault(url="http://localhost:8200", token="...")
>>>
>>> # Delete latest version
>>> store.delete_secret("myapp/old-key")
>>>
>>> # Delete specific versions (KV v2)
>>> store.delete_secret("myapp/secret", versions=[1, 2, 3])
get_secret(key: str, version: str | None = None, **kwargs) Any[source]๏ƒ

Retrieve a secret from Vault.

Parameters:
  • key โ€“ The secret path. For KV v2, use format: โ€œpath/to/secret:fieldโ€ to extract a specific field. For example: - โ€œmyapp/database:passwordโ€ extracts the โ€˜passwordโ€™ field - โ€œmyapp/databaseโ€ returns the entire secret dict

  • version โ€“ For KV v2, specify version number. If None, gets latest.

  • **kwargs โ€“ Additional parameters for read operation.

Returns:

The secret value or dict of values.

Raises:
  • SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other Vault errors.

Example

>>> store = HashiCorpVault(url="http://localhost:8200", token="...")
>>>
>>> # Get entire secret
>>> secret = store.get_secret("myapp/database")
>>> # Returns: {"host": "localhost", "password": "secret"}
>>>
>>> # Get specific field
>>> password = store.get_secret("myapp/database:password")
>>> # Returns: "secret"
>>>
>>> # Get specific version (KV v2)
>>> old_secret = store.get_secret("myapp/database", version="2")
get_secret_metadata(key: str) Dict[str, Any][source]๏ƒ

Get metadata about a secret.

Only available for KV v2.

Parameters:

key โ€“ The secret path.

Returns:

Dictionary with secret metadata.

Raises:
  • NotImplementedError โ€“ For KV v1.

  • SecretNotFoundError โ€“ If the secret doesnโ€™t exist.

  • SecretAccessError โ€“ If thereโ€™s a permission error.

Example

>>> store = HashiCorpVault(
...     url="http://localhost:8200", token="...", kv_version=2
... )
>>> metadata = store.get_secret_metadata("myapp/database")
>>> print(f"Current version: {metadata['current_version']}")
>>> print(f"Created: {metadata['created_time']}")
list_secrets(prefix: str | None = None, **kwargs) List[str][source]๏ƒ

List secrets in Vault.

Parameters:
  • prefix โ€“ Optional path prefix to list from.

  • **kwargs โ€“ Additional parameters for list operation.

Returns:

List of secret paths.

Raises:
  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other Vault errors.

Example

>>> store = HashiCorpVault(url="http://localhost:8200", token="...")
>>>
>>> # List all secrets at root
>>> all_secrets = store.list_secrets()
>>>
>>> # List secrets under a path
>>> app_secrets = store.list_secrets(prefix="myapp/")
set_secret(key: str, value: Any, **kwargs) None[source]๏ƒ

Store a secret in Vault.

Parameters:
  • key โ€“ The secret path.

  • value โ€“ The secret value. For KV engines, should be a dict. If a string/other type is provided, it will be wrapped as {โ€œvalueโ€: value}.

  • **kwargs โ€“ Additional parameters for create/update operation.

Raises:
  • SecretAccessError โ€“ If thereโ€™s a permission error.

  • SecretStoreError โ€“ For other Vault errors.

Example

>>> store = HashiCorpVault(url="http://localhost:8200", token="...")
>>>
>>> # Store dict secret
>>> store.set_secret(
...     "myapp/database",
...     {"host": "localhost", "port": 5432, "password": "secret"},
... )
>>>
>>> # Store simple value
>>> store.set_secret("myapp/api-key", "abc123")
>>> # Stored as: {"value": "abc123"}
class confii.secret_stores.MultiSecretStore(stores: List[SecretStore], fail_on_missing: bool = True, write_to_first: bool = True)[source]๏ƒ

Bases: SecretStore

Composite secret store that chains multiple secret stores.

This allows you to configure a fallback hierarchy of secret stores. When resolving a secret, it tries each store in order until the secret is found.

This is useful for scenarios like: - Primary secrets in AWS Secrets Manager, fallback to environment variables - Development secrets in local dict, production secrets in Vault - Secrets split across multiple cloud providers - Testing with mocked stores that override production stores

Example

>>> from confii.secret_stores import (
...     MultiSecretStore,
...     DictSecretStore,
...     AWSSecretsManager,
...     SecretResolver,
... )
>>>
>>> # Create a multi-store with fallback hierarchy
>>> store = MultiSecretStore(
...     [
...         DictSecretStore({"local/override": "dev-value"}),  # Checked first
...         AWSSecretsManager(region_name="us-east-1"),  # Fallback to AWS
...     ]
... )
>>>
>>> # Use with Config
>>> config = Config(secret_resolver=SecretResolver(store))
__init__(stores: List[SecretStore], fail_on_missing: bool = True, write_to_first: bool = True) None[source]๏ƒ

Initialize the multi-store.

Parameters:
  • stores โ€“ List of secret stores in priority order. The first store is checked first, then the second, etc.

  • fail_on_missing โ€“ If True, raises SecretNotFoundError if secret not found in any store. If False, returns None (default: True).

  • write_to_first โ€“ When setting secrets, write to the first store only (default: True). If False, writes to all stores.

Example

>>> primary = AWSSecretsManager(region="us-east-1")
>>> fallback = DictSecretStore({"default/key": "value"})
>>> store = MultiSecretStore([primary, fallback])
__repr__() str[source]๏ƒ

String representation of the multi-store.

delete_secret(key: str, **kwargs) None[source]๏ƒ

Delete a secret from one or all stores.

Parameters:
  • key โ€“ The secret key/name to delete.

  • **kwargs โ€“ Additional provider-specific parameters.

Example

>>> store = MultiSecretStore([store1, store2])
>>> store.delete_secret("old/secret")
get_secret(key: str, version: str | None = None, **kwargs) Any[source]๏ƒ

Retrieve a secret by trying each store in order.

Parameters:
  • key โ€“ The secret key/name.

  • version โ€“ Optional version identifier.

  • **kwargs โ€“ Additional provider-specific parameters.

Returns:

The secret value from the first store that has it.

Raises:

SecretNotFoundError โ€“ If fail_on_missing=True and secret not found in any store.

Example

>>> store = MultiSecretStore([store1, store2, store3])
>>> secret = store.get_secret("api/key")
>>> # Tries store1, then store2, then store3
get_secret_metadata(key: str) Dict[str, Any][source]๏ƒ

Get metadata from the first store that has the secret.

Parameters:

key โ€“ The secret key/name.

Returns:

Metadata dictionary from the first store that has the secret.

Raises:

SecretNotFoundError โ€“ If secret not found in any store.

Example

>>> store = MultiSecretStore([store1, store2])
>>> metadata = store.get_secret_metadata("api/key")
get_store_for_secret(key: str) SecretStore | None[source]๏ƒ

Find which store contains a specific secret.

Parameters:

key โ€“ The secret key/name.

Returns:

The first store that contains the secret, or None if not found.

Example

>>> store = MultiSecretStore([aws_store, vault_store, dict_store])
>>> source = store.get_store_for_secret("api/key")
>>> print(f"Secret found in: {source.__class__.__name__}")
list_secrets(prefix: str | None = None, **kwargs) List[str][source]๏ƒ

List all unique secrets across all stores.

Parameters:
  • prefix โ€“ Optional prefix to filter secrets.

  • **kwargs โ€“ Additional provider-specific parameters.

Returns:

Combined list of unique secret keys from all stores.

Example

>>> store = MultiSecretStore([store1, store2])
>>> all_secrets = store.list_secrets()
>>> # Returns unique secrets from both stores
secret_exists(key: str) bool[source]๏ƒ

Check if a secret exists in any store.

Parameters:

key โ€“ The secret key/name.

Returns:

True if the secret exists in at least one store.

Example

>>> store = MultiSecretStore([store1, store2])
>>> if store.secret_exists("api/key"):
...     print("Secret found")
set_secret(key: str, value: Any, **kwargs) None[source]๏ƒ

Store a secret in one or all stores.

Parameters:
  • key โ€“ The secret key/name.

  • value โ€“ The secret value.

  • **kwargs โ€“ Additional provider-specific parameters.

Example

>>> store = MultiSecretStore([store1, store2])
>>> store.set_secret("new/secret", "value")
>>> # Writes to store1 only (if write_to_first=True)
class confii.secret_stores.SecretResolver(secret_store: SecretStore, cache_enabled: bool = True, fail_on_missing: bool = True, prefix: str | None = None, cache_ttl: float | None = None)[source]๏ƒ

Bases: object

Resolves secret placeholders in configuration values.

This class integrates secret stores with Confii by automatically resolving secret placeholders in configuration values. Placeholders follow the format: ${secret:key} or ${secret:key:json_path}

secret_store๏ƒ

The secret store instance to use for resolving secrets.

pattern๏ƒ

Regex pattern for matching secret placeholders.

cache_enabled๏ƒ

Whether to cache resolved secrets.

fail_on_missing๏ƒ

Whether to raise an error if a secret is not found.

Example

>>> from confii import Config
>>> from confii.secret_stores import DictSecretStore, SecretResolver
>>>
>>> # Create a secret store
>>> secrets = DictSecretStore(
...     {
...         "db/password": "super-secret",
...         "api/credentials": {"key": "abc123", "secret": "xyz789"},
...     }
... )
>>>
>>> # Create resolver
>>> resolver = SecretResolver(secrets)
>>>
>>> # Use with Config
>>> config = Config(env="prod", secret_resolver=resolver)
>>>
>>> # In config file: database.password = "${"secret" + ":" + "db/password"}"
>>> # Automatically resolves to: "super-secret"
SECRET_PATTERN = re.compile('\\$\\{secret:([^}:]+)(?::([^}:]+))?(?::([^}]+))?\\}')๏ƒ
__init__(secret_store: SecretStore, cache_enabled: bool = True, fail_on_missing: bool = True, prefix: str | None = None, cache_ttl: float | None = None) None[source]๏ƒ

Initialize the secret resolver.

Parameters:
  • secret_store โ€“ The secret store instance to use for resolving secrets.

  • cache_enabled โ€“ Enable caching of resolved secrets (default: True). Caching improves performance but secrets wonโ€™t be refreshed until config reload or TTL expiry.

  • fail_on_missing โ€“ Raise an error if a secret is not found (default: True). If False, leaves the placeholder unchanged.

  • prefix โ€“ Optional prefix to prepend to all secret keys. Useful for namespacing secrets per environment.

  • cache_ttl โ€“ Optional cache time-to-live in seconds. If set, cached secrets expire after this duration and are re-fetched on next access. If None (default), cache entries never expire.

Example

>>> resolver = SecretResolver(
...     secret_store=my_store,
...     cache_enabled=True,
...     fail_on_missing=True,
...     prefix="prod/",
...     cache_ttl=300,  # 5 minutes
... )
property cache_stats: Dict[str, Any]๏ƒ

Get cache statistics.

Returns:

Dictionary with cache information including size and keys.

Example

>>> resolver = SecretResolver(my_store)
>>> stats = resolver.cache_stats
>>> print(f"Cached secrets: {stats['size']}")
clear_cache() None[source]๏ƒ

Clear the secret cache.

Useful when you want to force re-fetching of secrets, for example after rotating credentials.

Example

>>> resolver = SecretResolver(my_store)
>>> # ... some time passes, secrets are rotated ...
>>> resolver.clear_cache()
>>> config.reload()  # Secrets will be re-fetched
hook(value: Any) Any[source]๏ƒ

Hook method for integration with Configโ€™s HookProcessor.

This is a convenience method that simply calls resolve().

Parameters:

value โ€“ The configuration value to process.

Returns:

The value with secret placeholders resolved.

Example

>>> from confii import Config
>>> resolver = SecretResolver(my_store)
>>> config = Config(env="prod")
>>> config.hook_processor.register_global_hook(resolver.hook)
prefetch_secrets(keys: list) None[source]๏ƒ

Prefetch and cache multiple secrets at once.

This can improve performance when you know which secrets will be needed.

Parameters:

keys โ€“ List of secret keys to prefetch.

Example

>>> resolver = SecretResolver(my_store, cache_enabled=True)
>>> resolver.prefetch_secrets(["database/password", "api/key", "redis/url"])
resolve(value: Any) Any[source]๏ƒ

Resolve secret placeholders in a value.

This method is designed to be used as a Config hook. It processes string values and replaces secret placeholders with actual secret values.

Parameters:

value โ€“ The configuration value to process. Only strings containing secret placeholders are modified.

Returns:

The value with all secret placeholders resolved.

Raises:
  • SecretNotFoundError โ€“ If fail_on_missing=True and a secret is not found.

  • SecretAccessError โ€“ If thereโ€™s a permission or authentication error.

  • SecretStoreError โ€“ For other secret store errors.

Example

>>> resolver = SecretResolver(my_store)
>>> result = resolver.resolve("${secret:api/key}")
>>> # Returns the actual secret value
class confii.secret_stores.SecretStore[source]๏ƒ

Bases: ABC

Abstract base class for secret store providers.

This class defines the interface that all secret store implementations must follow. Users can create custom secret store providers by subclassing this class and implementing the required methods.

Example

>>> class CustomSecretStore(SecretStore):
...     def __init__(self, api_key: str):
...         self.api_key = api_key
...
...     def get_secret(self, key: str, version: Optional[str] = None) -> Any:
...         # Your implementation here
...         return my_custom_api.fetch_secret(key, version)
...
...     def set_secret(self, key: str, value: Any, **kwargs) -> None:
...         # Your implementation here
...         my_custom_api.store_secret(key, value)
...
...     def delete_secret(self, key: str) -> None:
...         # Your implementation here
...         my_custom_api.remove_secret(key)
...
...     def list_secrets(self, prefix: Optional[str] = None) -> list:
...         # Your implementation here
...         return my_custom_api.list_all_secrets(prefix)
abstractmethod delete_secret(key: str, **kwargs) None[source]๏ƒ

Delete a secret from the secret store.

Parameters:
  • key โ€“ The unique identifier for the secret to delete.

  • **kwargs โ€“ Additional provider-specific parameters such as: - force: Force deletion without recovery period - recovery_window: Days before permanent deletion

Raises:
  • SecretNotFoundError โ€“ If the secret does not exist.

  • SecretAccessError โ€“ If thereโ€™s a permission or authentication error.

  • SecretStoreError โ€“ For other errors communicating with the secret store.

Example

>>> store = CustomSecretStore()
>>> store.delete_secret("old/api-key")
abstractmethod get_secret(key: str, version: str | None = None, **kwargs) Any[source]๏ƒ

Retrieve a secret value from the secret store.

Parameters:
  • key โ€“ The unique identifier for the secret. Format may vary by provider. Examples: - AWS: โ€œmy-secret-nameโ€ or โ€œmy-secret-name:keyโ€ - Azure: โ€œmy-secret-nameโ€ - Vault: โ€œsecret/data/myapp/db-passwordโ€

  • version โ€“ Optional version identifier for the secret. If not provided, retrieves the latest version.

  • **kwargs โ€“ Additional provider-specific parameters.

Returns:

The secret value. Can be a string, dict, or other type depending on the secret store and how the secret was stored.

Raises:
  • SecretNotFoundError โ€“ If the secret does not exist.

  • SecretAccessError โ€“ If thereโ€™s a permission or authentication error.

  • SecretStoreError โ€“ For other errors communicating with the secret store.

Example

>>> store = CustomSecretStore()
>>> password = store.get_secret("database/password")
>>> api_key = store.get_secret("api/key", version="v2")
get_secret_metadata(key: str) Dict[str, Any][source]๏ƒ

Get metadata about a secret without retrieving the value.

This is an optional method that providers can override to provide metadata such as creation date, version info, tags, etc.

Parameters:

key โ€“ The unique identifier for the secret.

Returns:

Dictionary containing metadata about the secret.

Raises:

NotImplementedError โ€“ If the provider doesnโ€™t support metadata retrieval.

Example

>>> store = CustomSecretStore()
>>> metadata = store.get_secret_metadata("database/password")
>>> print(f"Created: {metadata['created_date']}")
abstractmethod list_secrets(prefix: str | None = None, **kwargs) list[source]๏ƒ

List all secrets or secrets matching a prefix.

Parameters:
  • prefix โ€“ Optional prefix to filter secrets. If None, lists all secrets.

  • **kwargs โ€“ Additional provider-specific parameters.

Returns:

List of secret keys/names available in the store.

Raises:
  • SecretAccessError โ€“ If thereโ€™s a permission or authentication error.

  • SecretStoreError โ€“ For other errors communicating with the secret store.

Example

>>> store = CustomSecretStore()
>>> all_secrets = store.list_secrets()
>>> db_secrets = store.list_secrets(prefix="database/")
secret_exists(key: str) bool[source]๏ƒ

Check if a secret exists in the store.

Parameters:

key โ€“ The unique identifier for the secret.

Returns:

True if the secret exists, False otherwise.

Example

>>> store = CustomSecretStore()
>>> if store.secret_exists("api/key"):
...     print("Secret exists")
abstractmethod set_secret(key: str, value: Any, **kwargs) None[source]๏ƒ

Store a secret value in the secret store.

Parameters:
  • key โ€“ The unique identifier for the secret.

  • value โ€“ The secret value to store. Can be a string, dict, or other serializable type.

  • **kwargs โ€“ Additional provider-specific parameters such as: - description: Human-readable description - tags: Metadata tags - kms_key: Encryption key identifier

Raises:
  • SecretAccessError โ€“ If thereโ€™s a permission or authentication error.

  • SecretStoreError โ€“ For other errors communicating with the secret store.

Example

>>> store = CustomSecretStore()
>>> store.set_secret("database/password", "super-secret-pass")
>>> store.set_secret(
...     "api/config",
...     {"key": "abc123", "endpoint": "https://api.example.com"},
... )