Skip to main content

A flexible configuration management library for Python applications

Project description

Confii

Confii Logo

Python Version License CI Status PyPI Version Downloads Coverage Ruff Typed Pre-commit Docs OpenSSF Scorecard

A comprehensive, production-ready configuration management library for Python

InstallationQuick StartFeaturesExamplesDocumentation


Overview

Confii is a modern, feature-rich configuration management library designed for Python applications. It provides a unified interface for loading, merging, validating, and accessing configuration from multiple sources with enterprise-grade features like secret management, schema validation, observability, versioning, and async support.

Why Confii?

  • 🎯 Attribute-Style Access - config.database.host instead of config["database"]["host"]
  • 🔐 Secret Management - ${secret:key} resolved from AWS, Vault (9 auth methods), Azure, GCP
  • Type Safety - Config[T] generics with Pydantic for full IDE autocomplete
  • 🧩 Config Composition - _include and _defaults directives (Hydra-like)
  • 🔄 Dynamic Reloading - Hot reload with incremental updates and dry-run
  • 🪝 Hook System - 4 hook types to transform values on access
  • 🧊 Freeze & Override - Immutable configs, scoped overrides with override() context manager
  • 📊 Observability - Access metrics, event emission, change callbacks
  • 🔍 Introspection - explain(), layers, source tracking, drift detection
  • 🏢 Enterprise Ready - Versioning with rollback, 6 merge strategies, async support

Installation

# Basic installation
pip install confii

# With optional dependencies
pip install confii[watch]         # File watching for dynamic reloading
pip install confii[cli]           # CLI tools (confii command)
pip install confii[validation]    # Schema validation (Pydantic, JSON Schema)
pip install confii[cloud]         # Cloud storage support (AWS S3, Azure, GCP)
pip install confii[secrets]       # Secret store support (AWS, Azure, GCP, Vault)
pip install confii[all]           # All features

Requirements: Python 3.9+

📖 See examples/basic_usage.py for getting started


Quick Start

Basic Usage

from confii import Config
from confii.loaders import YamlLoader

# Load from a single file
config = Config(loaders=[YamlLoader("config.yaml")])

# Access configuration values with attribute-style syntax
print(config.database.host)
print(config.database.port)

Multiple Sources

from confii import Config
from confii.loaders import YamlLoader, EnvironmentLoader

# Load from multiple sources (later sources override earlier ones)
config = Config(
    env="production",
    loaders=[
        YamlLoader("config/base.yaml"),
        YamlLoader("config/production.yaml"),
        EnvironmentLoader("APP"),  # APP_* environment variables
    ]
)

# Access values directly as attributes
print(config.database.host)       # from production.yaml (overrides base)
print(config.database.port)       # from base.yaml (not overridden)
print(config.app.debug)           # False in production

Type-Safe Config (North Star Feature)

from pydantic import BaseModel
from confii import Config
from confii.loaders import YamlLoader

class DatabaseConfig(BaseModel):
    host: str
    port: int = 5432
    ssl: bool = False

class AppConfig(BaseModel):
    database: DatabaseConfig
    debug: bool = False

# Config[AppConfig] gives you full type safety
config = Config[AppConfig](
    loaders=[YamlLoader("config.yaml")],
    schema=AppConfig,
    validate_on_load=True,
)

# Full IDE autocomplete and mypy/pyright type checking:
config.typed.database.host   # IDE knows: str
config.typed.database.port   # IDE knows: int
config.typed.database.ssl    # IDE knows: bool
config.typed.nonexistent     # mypy error at dev time!

Load from any source (YAML, JSON, env vars, SSM, Vault) — access with full type safety.

📖 See examples/typed_config.py

With Secret Stores

from confii import Config
from confii.secret_stores import AWSSecretsManager, SecretResolver
from confii.loaders import YamlLoader

# Initialize secret store
secret_store = AWSSecretsManager(region_name='us-east-1')

# Use with Config
config = Config(
    loaders=[YamlLoader("config.yaml")],
    secret_resolver=SecretResolver(secret_store)
)

# Secrets in config.yaml like "${secret:db/password}" are automatically resolved
print(config.database.password)  # Resolved from AWS Secrets Manager

📖 See examples/basic_usage.py for more examples


Features

🎯 Core Features

Multiple Configuration Sources

  • Files: JSON, YAML, TOML, INI, .env
  • Cloud Storage: AWS S3, Azure Blob, Google Cloud Storage, IBM COS
  • AWS Services: SSM Parameter Store, Secrets Manager
  • Remote: HTTP/HTTPS endpoints, Git repositories
  • Environment Variables: System environment with prefix support
  • Secret Stores: AWS, Azure, GCP, HashiCorp Vault
  • Custom Loaders: Extensible loader interface

Schema Validation

from confii import Config
from confii.loaders import YamlLoader
from pydantic import BaseModel

class DatabaseConfig(BaseModel):
    host: str
    port: int = 5432

config = Config(
    loaders=[YamlLoader("config.yaml")],
    schema=DatabaseConfig,
    validate_on_load=True
)

# Validated — safe to access directly
print(config.database.host)     # guaranteed to be str
print(config.database.port)     # guaranteed to be int, defaults to 5432

📖 See examples/validation_example.py

Dynamic Reloading

Requires pip install confii[watch] for file watching.

config = Config(
    loaders=[YamlLoader("config.yaml")],
    dynamic_reloading=True  # Watches files for changes
)

# Config automatically reloads when files change
# Or manually reload with incremental updates (no watchdog needed)
config.reload(incremental=True)  # Only reloads changed files

AWS SSM Parameter Store

from confii import Config
from confii.loaders import SSMLoader, YamlLoader

# Load base config from file, override with SSM parameters
config = Config(
    env="production",
    loaders=[
        YamlLoader("config.yaml"),
        SSMLoader("/myapp/production/", decrypt=True),
    ],
)

# /myapp/production/database/host → config.database.host
print(config.database.host)       # from SSM (overrides YAML)
print(config.database.port)       # from YAML (not in SSM)

SSM loader handles pagination automatically (works beyond 10 parameters), decrypts SecureString values, and auto-converts types ("5432"5432).

Environment Variables with Nesting

from confii.loaders import EnvironmentLoader

# APP_DATABASE__HOST=db.example.com → config.database.host
# APP_DATABASE__PORT=5432           → config.database.port (auto-typed to int)
config = Config(loaders=[EnvironmentLoader("APP", separator="__")])

print(config.database.host)       # "db.example.com"
print(config.database.port)       # 5432 (int, not string)

Smart .env Loading

from confii.loaders import EnvFileLoader

# .env file supports dotted keys, type coercion, inline comments, and escapes
# database.host=localhost       → nested: config.database.host
# database.port=5432            → auto-typed: int
# debug=true                    → auto-typed: bool
# api.key=abc123  # my comment  → inline comment stripped
# message="hello\nworld"        → escape sequences in double quotes

config = Config(loaders=[EnvFileLoader(".env")])
print(config.database.host)       # "localhost"
print(config.database.port)       # 5432 (int)
print(config.debug)               # True (bool)

Introspection API

# Access values directly — the primary way to use Confii
host = config.database.host           # "db.example.com"
port = config.database.port           # 5432
debug = config.app.debug              # False

# get() is available when you need defaults or dynamic keys
value = config.get("database.host", "localhost")

# Query and explore your configuration
keys = config.keys()                  # all leaf keys
exists = config.has("database.host")  # True
schema = config.schema("database")    # type info
info = config.explain("database.host") # source, value, override history

📖 See examples/introspection_api.py

Configuration Overrides

# Programmatic overrides
config.set("database.host", "remote.db.example.com")
config.set("database.port", 3306)

# Access the overridden values directly
print(config.database.host)    # "remote.db.example.com"
print(config.database.port)    # 3306

# Scoped overrides — automatically restored on exit
with config.override({"database.host": "temp.db.example.com"}):
    print(config.database.host)  # "temp.db.example.com"
print(config.database.host)      # back to "remote.db.example.com"

Configuration Composition

Build complex configurations from reusable pieces — like Hydra, but built-in:

# config/base.yaml
_defaults:
  - database: postgres
  - cache: redis

_include:
  - config/shared.yaml
  - config/secrets.yaml

app:
  name: MyApp
config = Config(loaders=[YamlLoader("config/base.yaml")])
print(config.app.name)           # "MyApp"
print(config.database.host)      # from included postgres defaults
print(config.cache.ttl)          # from included redis defaults

Circular includes are automatically detected and prevented.

Environment Switching

Switch environments dynamically without code changes:

# Read environment name from an OS variable
config = Config(
    env_switcher="APP_ENV",       # reads os.environ["APP_ENV"]
    loaders=[YamlLoader("config.yaml")],
)

# config.yaml has default:, production:, staging: sections
# The right one is selected automatically based on APP_ENV
print(config.database.host)       # from the active environment
APP_ENV=production python app.py  # uses production section
APP_ENV=staging python app.py     # uses staging section

System Environment Fallback

Missing a config key? Confii can fall back to OS environment variables:

config = Config(
    loaders=[YamlLoader("config.yaml")],
    sysenv_fallback=True,
    env_prefix="MYAPP",
)

# If database.host is not in config.yaml, Confii checks MYAPP_DATABASE_HOST
print(config.database.host)       # from file or env var — transparent

Hook System

Transform values on access with 4 types of hooks:

config = Config(loaders=[YamlLoader("config.yaml")])

# Key hook — transform specific keys
config.register_key_hook("database.password", lambda v: decrypt(v))

# Value hook — transform specific values
config.register_value_hook("PLACEHOLDER", lambda v: os.environ.get("REAL_VALUE"))

# Condition hook — transform values matching a condition
config.register_condition_hook(
    lambda key, value: key.endswith("_url"),
    lambda value: value.rstrip("/")
)

# Global hook — applied to every value
config.register_global_hook(lambda key, value: value.strip() if isinstance(value, str) else value)

# Hooks fire automatically on attribute access
print(config.database.password)   # decrypted via key hook
print(config.api.base_url)        # trailing slash stripped via condition hook

Freeze for Production Safety

Make your config immutable after loading — no accidental mutations:

config = Config(
    loaders=[YamlLoader("config.yaml")],
    freeze_on_load=True,          # immutable from the start
)

print(config.database.host)       # reads work
config.set("database.host", "x") # raises ConfiiError!
config.reload()                   # raises ConfiiError!

# Or freeze manually after setup
config = Config(loaders=[YamlLoader("config.yaml")])
config.set("debug", True)        # mutation allowed
config.freeze()                   # lock it down
config.set("debug", False)       # raises ConfiiError!

Dry-Run Reload

Validate a reload before applying it:

# Test what would change without actually changing anything
config.reload(dry_run=True)       # validates — no side effects

# Incremental reload — only re-reads changed files
config.reload(incremental=True)   # fast for large configs

Change Callbacks

React when configuration values change:

@config.on_change
def handle_change(old_config, new_config):
    if old_config.get("database.host") != new_config.get("database.host"):
        reconnect_database()

config.reload()  # triggers callback if values changed

Self-Configuration

Configure Confii itself from a file — no code needed for defaults:

# confii.yaml (or .confii.yaml, confii.json, confii.toml)
default_environment: production
default_files:
  - config.yaml
  - config.local.yaml
dynamic_reloading: false
deep_merge: true
use_type_casting: true
# Or in pyproject.toml
[tool.confii]
default_environment = "production"
deep_merge = true
use_type_casting = true
# Config picks up settings automatically — no kwargs needed
config = Config(loaders=[YamlLoader("config.yaml")])

Generate Documentation

Auto-generate a configuration reference from your live config:

# Markdown documentation of all config keys
docs = config.generate_docs("markdown")
print(docs)  # | Key | Value | Type | Source |

# JSON format for tooling
docs = config.generate_docs("json")

🔐 Secret Store Integration

Comprehensive secret management with automatic resolution:

from confii.secret_stores import (
    AWSSecretsManager,
    HashiCorpVault,
    AzureKeyVault,
    GCPSecretManager,
    SecretResolver
)

# AWS Secrets Manager
store = AWSSecretsManager(region_name='us-east-1')

# HashiCorp Vault with OIDC + Kerberos (SSO)
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)

# Multi-store fallback
from confii.secret_stores import MultiSecretStore, DictSecretStore
store = MultiSecretStore([
    DictSecretStore({"local/key": "override"}),  # Highest priority
    AWSSecretsManager(region_name='us-east-1'),  # Production
])

config = Config(
    loaders=[YamlLoader("config.yaml")],
    secret_resolver=SecretResolver(store)
)

Configuration file with secret placeholders:

database:
  host: prod-db.example.com
  password: "${secret:prod/database/password}"
api:
  key: "${secret:prod/api/key:api_key}"  # JSON path extraction

Supported Secret Stores:

  • AWS Secrets Manager - Native AWS secret management
  • HashiCorp Vault - Enterprise secrets with 10+ auth methods
    • OIDC with Kerberos (SSO, no browser if kinit done)
    • LDAP with PIN+Token (complex password policies)
    • JWT, Kubernetes, AWS, Azure, GCP, AppRole, Token
  • Azure Key Vault - Azure native secret management
  • GCP Secret Manager - Google Cloud secret management
  • Environment Variables - Simple env-based secrets
  • Multi-Store - Combine multiple stores with fallback hierarchy

📖 See examples/secret_store_example.py and docs/SECRET_STORES.md

🚀 Advanced Features

Async/Await Support

from confii.async_config import AsyncConfig, AsyncYamlLoader

async def main():
    loader = AsyncYamlLoader("config.yaml")
    config = await AsyncConfig.create(loaders=[loader])
    value = await config.get_async("database.host")

📖 See examples/async_example.py

Configuration Versioning

# Enable versioning
version_manager = config.enable_versioning()

# Save current configuration as a version
version = config.save_version(metadata={
    "author": "user@example.com",
    "message": "Updated database config"
})

# Rollback to a previous version
config.rollback_to_version(version.version_id)

# List all versions
versions = version_manager.list_versions(limit=10)

Configuration Diff & Drift Detection

# Compare two configurations
config1 = Config(loaders=[YamlLoader("dev.yaml")])
config2 = Config(loaders=[YamlLoader("prod.yaml")])
diffs = config1.diff(config2)

for diff in diffs:
    print(f"{diff.path}: {diff.diff_type.value}")

# Detect configuration drift
intended = Config(loaders=[YamlLoader("intended.yaml")])
actual = Config(loaders=[YamlLoader("actual.yaml")])
drift = actual.detect_drift(intended)

Observability & Metrics

# Enable metrics collection
observer = config.enable_observability()

# Use configuration normally
host = config.database.host

# Get metrics statistics
metrics = config.get_metrics()
print(f"Config accessed {metrics['accessed_keys']} times")
print(f"Reload count: {metrics['reload_count']}")

# Enable event emission
emitter = config.enable_events()

@emitter.on("reload")
def handle_reload(new_config, duration):
    print(f"Config reloaded in {duration}s")

# Or register without decorator
emitter.on("change", lambda old, new: print("Config changed"))

Advanced Merging Strategies

from confii.merge_strategies import AdvancedConfigMerger, MergeStrategy

merger = AdvancedConfigMerger(MergeStrategy.MERGE)
merger.set_strategy("database", MergeStrategy.REPLACE)  # Replace entire section
merger.set_strategy("app.debug", MergeStrategy.REPLACE)  # Replace specific key

result = merger.merge(base_config, override_config)

📖 See examples/advanced_features.py

🛠️ Developer Experience

Debug Mode & Source Tracking

config = Config(debug_mode=True, loaders=[YamlLoader("config.yaml")])

# Use config normally — attribute access works the same
print(config.database.host)       # "db.example.com"
print(config.database.port)       # 5432

# But now you can also trace where each value came from
info = config.get_source_info("database.port")
print(f"Value: {info.value}")               # 5432
print(f"Source: {info.source_file}")         # "config.yaml"
print(f"Override count: {info.override_count}") # 0

# Export full debug report
config.export_debug_report("config_debug.json")

IDE Support

# Auto-generates type stubs for IDE autocomplete
config = Config(
    loaders=[YamlLoader("config.yaml")],
    enable_ide_support=True,
    ide_stub_path=".confii/stubs.pyi"
)

# Your IDE now autocompletes attribute access!
config.database.host    # IDE suggests: host, port, ssl, password
config.app.debug        # IDE knows this exists

ConfigBuilder Pattern

from confii import ConfigBuilder
from confii.loaders import YamlLoader

config = (ConfigBuilder()
    .with_env("production")
    .add_loader(YamlLoader("config.yaml"))
    .enable_deep_merge()
    .with_schema(AppConfig, validate_on_load=True)
    .build())

# Same attribute-style access
print(config.database.host)
print(config.app.debug)

CLI Tools

Requires pip install confii[cli]

Confii includes a CLI for common operations:

# Validate configuration
confii validate production --loader yaml:config.yaml

# Lint configuration for best practices
confii lint production --loader yaml:config.yaml

# Export configuration
confii export production --format=json --output=config.json

# Show configuration sources
confii debug production --key=database.host

# Explain how a configuration value was resolved
confii explain production --key=database.host

# Compare two configurations
confii diff development production \
    --loader1 yaml:dev.yaml \
    --loader2 yaml:prod.yaml

# Migrate from other tools
confii migrate dotenv .env --output config.yaml
confii migrate dynaconf settings.yaml --output config.yaml
confii migrate hydra conf/config.yaml --output config.yaml

# Load with overrides
confii load production \
    --loader yaml:config.yaml \
    --override "database.host=remote.db.example.com" \
    --override "database.port=3306"

Examples

We provide comprehensive, runnable examples for every feature. Each file is self-contained — creates temp files, demonstrates the feature, and cleans up.

Quick Reference

Feature What You'll Learn Example File
YAML / JSON loading Load config from YAML and JSON files basic_usage.py
TOML loading Load config from TOML files with sections loaders_showcase.py
INI loading Load config from INI files, section→dict mapping loaders_showcase.py
.env file loading Comments, quotes, dot-notation, type coercion loaders_showcase.py
Environment variables EnvironmentLoader with prefix and nesting loaders_showcase.py
HTTP remote loading HTTPLoader with auth and headers loaders_showcase.py
AWS SSM Parameter Store SSMLoader with path prefix and decryption loaders_showcase.py
Deep merge vs shallow How multi-source merge preserves/replaces keys loaders_showcase.py
Environment resolution default + production section merging basic_usage.py
env_prefix auto-override Config(env_prefix="MYAPP") env var overrides config_management.py
merge_strategy Per-path REPLACE/MERGE/APPEND strategies config_management.py
Config reload Manual config.reload(), incremental, dry_run config_management.py
on_change callbacks React to config changes during reload config_management.py
freeze() Make config immutable after loading config_management.py
Versioning Save, list, rollback config versions config_management.py
Config diff Compare two configs, see added/removed/modified config_management.py
Drift detection Detect actual vs intended config drift config_management.py
config.layers Inspect source precedence stack config_management.py
Custom hooks Global, key-specific, and condition hooks extensibility.py
Custom loaders Extend Loader base class for custom sources extensibility.py
Observability / metrics Access counts, reload stats, performance extensibility.py
Event emission @emitter.on("reload") decorator pattern extensibility.py
Composition _include and _defaults directives extensibility.py
Export config.export("json"), YAML, TOML extensibility.py
Standalone validate() Post-hoc validation with Pydantic models extensibility.py
Introspection API keys(), has(), get(), schema(), explain() introspection_api.py
Pydantic validation Model-based schema validation validation_example.py
JSON Schema validation Dictionary schema with defaults validation_example.py
Secret stores DictSecretStore, EnvSecretStore, MultiSecretStore secret_store_example.py
Vault authentication OIDC, LDAP, Kerberos, K8s, AWS, JWT, AppRole vault_auth_examples.py
Async config AsyncConfig, AsyncYamlLoader, async reload async_example.py
ConfigBuilder Fluent builder pattern basic_usage.py
Config[T] typed access Full IDE autocomplete via Pydantic models typed_config.py
End-to-end demo Complete working application working_demo.py

Example Files

File Focus Area Examples
loaders_showcase.py All loader types TOML, INI, .env, env vars, HTTP, SSM, deep/shallow merge
config_management.py Config lifecycle Reload, freeze, versioning, diff, drift, callbacks, layers
extensibility.py Customization Hooks, custom loaders, observability, events, composition, export
basic_usage.py Getting started YAML/JSON, env resolution, ConfigBuilder
introspection_api.py Querying config keys, has, get, schema, explain, set
validation_example.py Validation Pydantic models, JSON Schema, defaults
secret_store_example.py Secrets Multiple providers, fallback chains, caching
vault_auth_examples.py Vault auth 10+ authentication methods
async_example.py Async support Async loading, parallel sources, async validation
typed_config.py Type safety Config[T], nested models, multi-source typed
working_demo.py End-to-end Complete real-world application

Run any example:

python examples/loaders_showcase.py      # All loader types
python examples/config_management.py     # Reload, freeze, versioning, diff
python examples/extensibility.py         # Hooks, custom loaders, events
python examples/basic_usage.py           # Getting started

Documentation

API Reference

Guides


Feature Comparison

Feature Confii python-dotenv Dynaconf Hydra OmegaConf pydantic-settings
Multiple file formats ✅✅ Limited
Source tracking ✅✅
Cloud storage ✅✅
Secret stores ✅✅ ⚠️
Schema validation ⚠️ ✅✅
Type safety ✅✅
Configuration composition ✅✅
Override system ✅✅ ⚠️ ✅✅ ✅✅
Introspection API ✅✅
Incremental reloading ✅✅
Async/await support
Configuration versioning
Drift detection
Observability/metrics
IDE autocomplete ✅✅
Hot reload
Type casting

Legend: ✅✅ = Strong/Unique feature, ✅ = Supported, ⚠️ = Partial support, ❌ = Not supported


Testing

import pytest
from confii import Config
from confii.loaders import YamlLoader

@pytest.fixture
def test_config():
    return Config(loaders=[
        YamlLoader("tests/fixtures/test_config.yaml")
    ])

def test_database_config(test_config):
    assert test_config.database.host == "localhost"
    assert test_config.database.port == 5432

Run tests:

# Run all tests
pytest

# Run with coverage
pytest --cov=confii

# Run specific test file
pytest tests/test_config.py

Contributing

We welcome contributions! Please see our Contributing Guide for details.

# Clone repository
git clone https://github.com/confiify/confii-py.git
cd confii-py

# Install development dependencies
make setup

# Run tests
make test

# Run all checks (lint, type check, tests)
make check

License

MIT License. See LICENSE.txt for details.


Links


Built with ❤️ by confiify and contributors.

Made for developers who value type safety, observability, and developer experience.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

confii-2026.3.30.4.tar.gz (17.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

confii-2026.3.30.4-py3-none-any.whl (172.6 kB view details)

Uploaded Python 3

File details

Details for the file confii-2026.3.30.4.tar.gz.

File metadata

  • Download URL: confii-2026.3.30.4.tar.gz
  • Upload date:
  • Size: 17.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for confii-2026.3.30.4.tar.gz
Algorithm Hash digest
SHA256 817c51d3d961cacaef52cc6908671970f9b9a7760161824803d1743fea95f63a
MD5 f250b6fcb4a0aa5547c12460da728a74
BLAKE2b-256 a7030c4ba170d9f23f190cf19c77a0ed0d3adc11ffbc534594096af3365db35e

See more details on using hashes here.

Provenance

The following attestation bundles were made for confii-2026.3.30.4.tar.gz:

Publisher: release.yml on confiify/confii-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file confii-2026.3.30.4-py3-none-any.whl.

File metadata

  • Download URL: confii-2026.3.30.4-py3-none-any.whl
  • Upload date:
  • Size: 172.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for confii-2026.3.30.4-py3-none-any.whl
Algorithm Hash digest
SHA256 7cd8b8b0d4196cb04b9034232e6b510e7640d2aaa1475eacb17a936f2a13bdab
MD5 4125cf9c687620255834b0840c3b2105
BLAKE2b-256 23e5ffd2d9cc9a7f7d569fd0e043c1bc2786fcb72782ddf015955d54427c46f4

See more details on using hashes here.

Provenance

The following attestation bundles were made for confii-2026.3.30.4-py3-none-any.whl:

Publisher: release.yml on confiify/confii-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page