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
from utic_invocation_settings import (
TTLCache, decrypt_settings, dimensions, extract_context, extract_envelope,
)
_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):
envelope = extract_envelope(body) # None only when the caller sent no settings
context = extract_context(body) # None only when the caller sent no context
if envelope is None:
settings = load_legacy_job_settings_file() # transitional: older caller
else:
settings = decrypt_settings( # an envelope that arrived is the
envelope, # only settings source for this
private_key_loader=load_private_key, # request — never fall back here
settings_cache=_settings_cache,
key_cache=_key_cache,
)
bind_dimensions(dimensions(context)) # e.g. utic-instrumentation; {} for an older caller
return do_work(settings)
The fallback belongs on the absent branch only. Falling back after a failed decrypt would answer a request configured for one tenant with whatever the pod booted with.
Every failure (unknown format, missing key, RSA/GCM failure, digest mismatch) raises a subclass of
InvocationSettingsError — it never returns partial or unverified plaintext.
The two reserved fields
invocation_settings carries what to configure; invocation_context carries who the invocation
is for — the identity facets a shared pod can no longer read from its process environment. Both are
reserved, out-of-schema fields of the /invoke body, extracted directly from the parsed body rather
than declared as handler parameters, so nothing here depends on the serving wrapper.
Both fail closed on a present-but-invalid value: only an absent field signals "older caller,
use the legacy path". invocation_context.schema_version is validated, so an incompatible producer
surfaces at the first request instead of as quietly missing telemetry.
The context deliberately carries no filesystem paths. Where a plugin scratches to disk is its
own implementation detail — tempfile or uuid-named paths under a base the plugin chooses — never
caller-supplied input. A caller-supplied write path is a path-traversal surface that then needs
bounding, symlink re-checks, and escape tests; not shipping the knob is cheaper than hardening it.
Security and cache notes
- This is encryption and integrity for the recipient, not producer authentication. A holder of the recipient's public key can create an envelope, so deployments must deliver envelopes over an authenticated control-plane path.
expires_atandcredential_versionare plaintext advisory metadata outside the authenticated header. Use them for scheduling/freshness hints, never authorization decisions.settings_digestis a public, unsalted SHA-256 value and can link identical settings or support low-entropy guess confirmation. Treat envelopes as sensitive metadata.- A decrypted-settings cache is keyed by the full authenticated-envelope fingerprint; the AES-key
cache is keyed by
(kid, encryption_key_digest).clear()is not a revocation barrier for work already decrypting concurrently.
Develop
make install # uv sync
make test # unit tests + coverage
make check # ruff
Note: published to public PyPI on merge to main (see repo README).
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file utic_invocation_settings-0.2.1-py3-none-any.whl.
File metadata
- Download URL: utic_invocation_settings-0.2.1-py3-none-any.whl
- Upload date:
- Size: 18.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c664fb1694c3b9f57002d076064e28e44d4bcbdef840521811365e9bfac4fbd
|
|
| MD5 |
d56ef2cccda43b0f76416aa666c44e07
|
|
| BLAKE2b-256 |
cf52ad4761e222728caa73179910ff61df9b678c6fc083d6532cb3e3a55ab6f2
|