Metadata-Version: 2.4
Name: utic-invocation-settings
Version: 0.1.0
Summary: Public library for consuming encrypted Unstructured plugin invocation settings (AES-256-GCM + RSA-OAEP-256 envelope, decrypt + cache).
Requires-Python: >=3.11
Requires-Dist: cryptography<47.0.0,>=43.0.0
Requires-Dist: pydantic<3.0.0,>=2.12.5
Description-Content-Type: text/markdown

# utic-invocation-settings

Public library for **consuming encrypted Unstructured plugin invocation settings** — the plugin-side
half of the cellular-dataplane "settings in the invoke payload" design. It reads the v1 settings
envelope (RSA-OAEP-256-wrapped **AES-256-GCM**), decrypts **inside the plugin** at invoke time, and
caches only previously authenticated envelopes.

It is deliberately **self-contained on `cryptography` + `pydantic`** — no dependency on any
private-feed package — so it can be published to public PyPI and imported by external plugin authors.

## Why

Under the cellular dataplane, a shared pod may serve multiple tenants, so a plugin identity decrypts
settings routed to that plugin rather than a shared service handing out plaintext. Settings arrive as an
opaque ciphertext envelope; this library turns that envelope into a plain settings object, verifying
integrity and never logging secrets.

The wire format is frozen in `cellular-dataplane/docs/envelope-contract-v1.md`. The producer
(Secrets Provider / operator) emits exactly that shape; this library is the reference consumer.

## Usage

```python
from utic_invocation_settings import extract_envelope, decrypt_settings, TTLCache

_settings_cache = TTLCache(ttl_seconds=300)     # keyed by full-envelope fingerprint
_key_cache = TTLCache(ttl_seconds=3600)         # keyed by recipient + encryption-key digest

def load_private_key(kid: str):
    # Load the RSA private key for this plugin identity's certificate from the mounted secret.
    ...

def on_invoke(body: dict):
    env = extract_envelope(body)
    if env is None:
        return load_legacy_job_settings_file()   # transitional dual-path
    return decrypt_settings(
        env,
        private_key_loader=load_private_key,
        settings_cache=_settings_cache,
        key_cache=_key_cache,
    )
```

Every failure (unknown format, missing key, RSA/GCM failure, digest mismatch) raises a subclass of
`InvocationSettingsError` — it never returns partial or unverified plaintext.

## Develop

```bash
make install     # uv sync
make test        # unit tests + coverage
make check       # ruff
```

**Note:** published to public PyPI on merge to main (see repo README).
