Metadata-Version: 2.4
Name: infraclass
Version: 1.0.2
Summary: Infraclass is a lightweight, zero-dependency, and highly secure hierarchical inventory compiler for Python automation engines (like ansible and pyinfra). It allows you to build inventories using a top-down class inheritance layout, natively supporting encrypted secrets using age.
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pyyaml>=6.0

# Infraclass

Infraclass is a lightweight, zero-dependency, and highly secure hierarchical inventory compiler for Python automation engines (like Ansible and pyinfra). It allows you to build inventories using a top-down class inheritance layout, natively supporting encrypted secrets using `age`.

## Project Directory Structure

Infraclass separates the core execution configuration from your static data inventory. Rename your data directory to `infraclass/` to align with the framework:

```text
automation/
├── inventory.py               # Calls infraclass to generate the pyinfra inventory
├── deploy.py                  # Your execution playbooks
└── infraclass/                # Your hierarchical data directory
    ├── classes/               # Reusable configuration blueprints
    │   ├── components/
    │   ├── platform/
    │   │   └── init.yml       # Standard shared properties & secrets
    │   └── roles/
    └── nodes/                 # Machine-specific inventory targets
        └── node1.example.com.yml
```

## Setup & Accessibility

To make the infraclass binary globally active and accessible to your local execution pass:

### Install the Package
Install infraclass cleanly using uv (or pip):

uv tool install infraclass --with PyYAML

### Verify Default Path Alignment
By default, infraclass checks for an encrypted key at ~/.age/identity.age. Ensure your encrypted private key file sits exactly at that path.

### Optional Environment Overrides
If you need to map to a different key file variant, export the override directly in your shell profile (~/.zshrc, ~/.bashrc etc):
```text
export INFRACLASS_AGE_KEY_FILE="$HOME/.age/identity.age"
```
## Systemd / Credentials Directory Resolution

When operating inside automated environments (such as a CI/CD runner executing via systemd service managers), infraclass safely handles dynamic credential mounting.

If the vault key is dynamically provided to the systemd unit through LoadCredentialEncrypted, the engine automatically scans the transient security memory landscape. To ensure robust compatibility across different key generation techniques (like post-quantum envelopes or standard key profiles) without requiring forced codebase modifications, the engine deterministically parses the temporary path context:

```text
import os
import glob

def _get_age_key_path():
    """Resolves the age encryption key path based on systemic environment tags."""
    creds_dir = os.environ.get("CREDENTIALS_DIRECTORY")
    if creds_dir:
        # Scan strictly for files starting with 'age-' followed by 'k' or 'p'
        # e.g., matches both 'age-key' and 'age-pq-key' safely while avoiding greedy wildcards
        pattern = os.path.join(creds_dir, "age-[kp]*")
        matches = glob.glob(pattern)
        
        if matches:
            return sorted(matches)[0]

    return os.environ.get("INFRACLASS_AGE_KEY_FILE", os.path.expanduser("~/.age/identity.age"))
```

## "Out of the Box" Verification Tests

Once these parameters are lined up, you have two native testing options to confirm the engine is happy:

### Test 1: Standard Standalone CLI Output
```text
infraclass node1.example.com
```
Success Criteria: Full flat hierarchical YAML prints straight to stdout, with all custom !secret blocks transformed into decrypted, human-readable strings.

### Test 2: In-Memory Python Engine Integration

If you are writing custom automation wrappers or dynamic scripts rather than using the standalone CLI, you can import the compiler directly into your Python code:
```text
from infraclass.compiler import compile_node_data

node_data = compile_node_data("node1.example.com")

# Read variable directly out of compiled parameters mapping tree
secret_pass = node_data["parameters"]["platform"]["psql"]["database-name"]["password"]
print(f"Decrypted password token: {secret_pass}")
```
