# Confii

<p align="center">
  <img src="https://raw.githubusercontent.com/confiify/confii-py/main/art/confii-py.png" alt="Confii Logo" />
</p>

[![Python Version](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org)
[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE.txt)
[![CI Status](https://img.shields.io/github/actions/workflow/status/confiify/confii-py/ci.yml?branch=main)](https://github.com/confiify/confii-py/actions)
[![PyPI Version](https://img.shields.io/pypi/v/confii)](https://pypi.org/project/confii/)
[![Downloads](https://img.shields.io/pypi/dm/confii)](https://pypi.org/project/confii/)
[![Coverage](https://img.shields.io/codecov/c/github/confiify/confii-py)](https://codecov.io/gh/confiify/confii-py)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![Typed](https://img.shields.io/badge/typed-mypy-blue)](https://mypy-lang.org/)
[![Pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)
[![Docs](https://img.shields.io/github/actions/workflow/status/confiify/confii-py/docs.yml?branch=main&label=docs)](https://confiify.github.io/confii-py/)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/confiify/confii-py/badge)](https://scorecard.dev/viewer/?uri=github.com/confiify/confii-py)
<!-- [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/PROJECT_ID/badge)](https://www.bestpractices.dev/projects/PROJECT_ID) -->

## A comprehensive, production-ready configuration management library for Python

[Installation](#installation) • [Quick Start](#quick-start) • [Features](#features) • [Examples](#examples) • [Documentation](#documentation)

---

## 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

```bash
# 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](examples/basic_usage.py) for getting started**

---

## Quick Start

### Basic Usage

```python
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

```python
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)

```python
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](examples/typed_config.py)**

### With Secret Stores

```python
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](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

```python
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](examples/validation_example.py)**

#### Dynamic Reloading

> Requires `pip install confii[watch]` for file watching.

```python
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

```python
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

```python
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

```python
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

```python
# 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](examples/introspection_api.py)**

#### Configuration Overrides

```python
# 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:

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

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

app:
  name: MyApp
```

```python
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:

```python
# 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
```

```bash
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:

```python
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:

```python
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:

```python
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:

```python
# 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:

```python
@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:

```yaml
# 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
```

```toml
# Or in pyproject.toml
[tool.confii]
default_environment = "production"
deep_merge = true
use_type_casting = true
```

```python
# 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:

```python
# 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:

```python
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:

```yaml
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](examples/secret_store_example.py) and [docs/SECRET_STORES.md](docs/SECRET_STORES.md)**

### 🚀 Advanced Features

#### Async/Await Support

```python
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](examples/async_example.py)**

#### Configuration Versioning

```python
# 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

```python
# 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

```python
# 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

```python
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](examples/advanced_features.py)**

### 🛠️ Developer Experience

#### Debug Mode & Source Tracking

```python
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

```python
# 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

```python
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:

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

### Example Files

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

Run any example:

```bash
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

- **[Config Class](src/confii/config.py)** - Main configuration class
- **[Loaders](src/confii/loaders/)** - Configuration loaders
- **[Secret Stores](src/confii/secret_stores/)** - Secret management
- **[Validators](src/confii/validators/)** - Schema validation
- **[CLI](src/confii/cli.py)** - Command-line interface

### Guides

- **[Secret Stores Guide](docs/SECRET_STORES.md)** - Complete secret store documentation
- **[Vault Authentication](docs/VAULT_AUTHENTICATION.md)** - HashiCorp Vault authentication methods
- **[Migration Guide](docs/MIGRATION_GUIDE.md)** - Migrating from other libraries

---

## 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

```python
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:

```bash
# 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](CONTRIBUTING.md) for details.

```bash
# 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](LICENSE.txt) for details.

---

## Links

- 📖 **Documentation**: [GitHub Repository](https://github.com/confiify/confii-py)
- 📦 **PyPI Package**: [confii on PyPI](https://pypi.org/project/confii)
- 🐛 **Issue Tracker**: [GitHub Issues](https://github.com/confiify/confii-py/issues)
- 📝 **Changelog**: [CHANGELOG.md](CHANGELOG.md)

---

Built with ❤️ by [confiify](https://github.com/confiify) and contributors.

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