Confidentiality Add-Ons¶
Two separate installable distributions, neither imported by anyinfer core nor a
dependency of it. anyinfer_confidential implements Tier 1 (SealedTemplate) and Tier 2
(the AnyInfer Relay); anyinfer_shared holds the one composite type both sides report
into. See the confidentiality tiers guide for what
each tier guarantees, what it costs, and the ceiling it states.
from anyinfer_confidential import TemplateVault, seal_template
from anyinfer_confidential.relay import Relay, RelayRegistry, load_registry
from anyinfer_shared import ConfidentialityReport
Tier 1 — Sealed Templates¶
anyinfer_confidential.seal_template ¶
seal_template(
plaintext: str,
*,
key: bytes,
template_id: str,
key_id: str,
) -> EncryptedTemplate
Encrypt one template's plaintext into a shippable EncryptedTemplate asset.
This is the build-time step (anyinfer-confidential seal): plaintext never reaches
the shipped asset, only its ciphertext does.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
plaintext
|
str
|
The template source (e.g. containing |
required |
key
|
bytes
|
A 256-bit AES-GCM key, from |
required |
template_id
|
str
|
Identifier this template will be looked up by at render time. |
required |
key_id
|
str
|
Identifier for |
required |
Returns:
| Type | Description |
|---|---|
EncryptedTemplate
|
The sealed asset, safe to write to disk or bundle into a client build. |
anyinfer_confidential.EncryptedTemplate
dataclass
¶
EncryptedTemplate(
template_id: str,
key_id: str,
nonce: bytes,
ciphertext: bytes,
sealed_at: str,
)
One sealed template asset — safe to ship inside a client bundle as-is.
Attributes:
| Name | Type | Description |
|---|---|---|
template_id |
str
|
Caller-assigned identifier, unique within one vendor's asset set. |
key_id |
str
|
Which key in the |
nonce |
bytes
|
The AES-GCM nonce used for this specific seal (never reused per key). |
ciphertext |
bytes
|
The encrypted template text, including the GCM authentication tag. |
sealed_at |
str
|
ISO-8601 timestamp of when this asset was sealed, for audit trails. |
from_dict
classmethod
¶
from_dict(data: dict[str, Any]) -> EncryptedTemplate
Load an asset previously written by to_dict.
anyinfer_confidential.TemplateVault ¶
TemplateVault(
*,
key_ring: KeyRing,
license_public_key: bytes,
license_blob: bytes,
revocation_checker: RevocationChecker | None = None,
revocation_fail_closed: bool = False,
)
Decrypts a SealedTemplate on demand, renders once, and discards the plaintext.
No template cache in this class ever holds decrypted plaintext longer than one
render() call — that is the whole confidentiality property Tier 1 offers, and it is
enforced structurally (the decrypted buffer is a local variable, best-effort-zeroed
before the call returns), not by convention.
Construct a vault bound to one deployment's license and key material.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key_ring
|
KeyRing
|
Decryption keys, keyed by |
required |
license_public_key
|
bytes
|
The vendor's Ed25519 public key, for verifying
|
required |
license_blob
|
bytes
|
The signed, time-boxed entitlement blob for this deployment
(see |
required |
revocation_checker
|
RevocationChecker | None
|
Optional online revocation check (deny-list by license
id). When |
None
|
revocation_fail_closed
|
bool
|
When a |
False
|
renders_may_block
property
¶
renders_may_block: bool
Whether render can block on something other than CPU.
Exposed as a capability rather than left for a caller to infer from a private attribute, because exactly one caller needs it and the answer decides whether that caller offloads to a thread. The crypto path — license verification, AES-GCM decryption, formatting — is sub-millisecond and belongs on the event loop; a network-backed revocation checker does not, since one synchronous round trip there stalls every concurrent request in the process.
render ¶
render(template: EncryptedTemplate, **slots: object) -> str
Decrypt template, render it against slots, and discard the plaintext.
Raises:
| Type | Description |
|---|---|
LicenseError
|
The bound license is missing, malformed, unsigned by the expected key, or expired. |
RevokedLicenseError
|
Online revocation checking is enabled and the license
id is on the deny-list (or, under |
TemplateDecryptionError
|
|
anyinfer_confidential.KeyRing ¶
KeyRing(keys: dict[str, bytes] | None = None)
Maps key_id to decryption key — the mechanism key rotation is built on.
A compromised historical build's key is removed from the ring (or simply not
provisioned to new deployments); templates still sealed under that key_id then
stop decrypting, which is the intended effect of rotation, not a bug to work around.
anyinfer_confidential.generate_key ¶
generate_key() -> bytes
Generate a fresh 256-bit AES-GCM key for sealing templates.
Licensing¶
The license gate is enforcement in TemplateVault.render's code path, not a cryptographic
lock on the ciphertext — the guide's
Tier 1 section states the
ceiling precisely.
anyinfer_confidential.generate_signing_keypair ¶
generate_signing_keypair() -> tuple[bytes, bytes]
Generate an Ed25519 keypair for license issuance.
Returns:
| Type | Description |
|---|---|
bytes
|
|
bytes
|
key is the vendor's signing secret — it never ships in a client bundle; only |
tuple[bytes, bytes]
|
|
anyinfer_confidential.issue_license ¶
issue_license(
deployment_id: str,
*,
private_key: bytes,
valid_days: int,
) -> bytes
Issue a signed, time-boxed license blob for one deployment.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
deployment_id
|
str
|
Identifier the issued license is bound to. |
required |
private_key
|
bytes
|
The vendor's Ed25519 private key ( |
required |
valid_days
|
int
|
How many days from now the license remains valid. |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
An opaque signed blob: pass it to the deployment for |
anyinfer_confidential.verify_license ¶
verify_license(
blob: bytes, *, public_key: bytes
) -> LicenseBlob
Verify a license blob's signature and expiry.
Raises:
| Type | Description |
|---|---|
LicenseError
|
The blob is malformed, its signature does not verify against
|
anyinfer_confidential.LicenseBlob
dataclass
¶
LicenseBlob(
deployment_id: str, issued_at: str, expires_at: str
)
A verified license's contents, returned by verify_license on success.
Attributes:
| Name | Type | Description |
|---|---|---|
deployment_id |
str
|
The vendor-assigned identifier this license was issued for — the
key |
issued_at |
str
|
ISO-8601 issuance timestamp. |
expires_at |
str
|
ISO-8601 expiry timestamp; |
anyinfer_confidential.license.RevocationChecker ¶
Bases: Protocol
Checks whether a deployment's license has been revoked.
Returns True (not revoked), False (revoked), or raises when the check could not
be completed at all (network unreachable, etc.) — TemplateVault treats a raised
exception the same as an unreachable check, never as "not revoked."
__call__ ¶
__call__(deployment_id: str) -> bool
Return whether deployment_id is currently entitled (not revoked).
Tier 2 — The Relay¶
build_app requires a token-to-tenant mapping and has no unauthenticated mode: the
response body is the decrypted, assembled prompt.
anyinfer_confidential.relay.Relay ¶
Relay(
*,
vault: TemplateVault,
registry: RelayRegistry,
pacing: PacingPool | None = None,
admission: AdmissionController | None = None,
)
Assembles (and optionally forwards) one request per call, retaining nothing.
Every handle() call is independent: no cache, no session, no store of prior
requests. The zero-retention contract is structural, not a policy applied on top —
there is simply nowhere in this class that a request or response body could persist
past the return.
Bind a relay to its vault and routes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vault
|
TemplateVault
|
Decrypts and renders this deployment's sealed templates. |
required |
registry
|
RelayRegistry
|
Tenant-scoped routes. |
required |
pacing
|
PacingPool | None
|
Optional pacing state shared across calls. |
None
|
admission
|
AdmissionController | None
|
Optional per-tenant concurrency bounds. |
None
|
handle
async
¶
handle(
*,
tenant_id: str,
routing_key: str,
slots: dict[str, object],
mode: Literal["assemble", "forward"] = "assemble",
provider_settings: ProviderSettings | None = None,
) -> RelayResult
Assemble a request, and optionally forward it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant_id
|
str
|
Which vendor's route namespace to resolve |
required |
routing_key
|
str
|
Selects the |
required |
slots
|
dict[str, object]
|
Non-proprietary slot-fill values the client supplies; these are the only caller-controlled content that reaches the template render. |
required |
mode
|
Literal['assemble', 'forward']
|
|
'assemble'
|
provider_settings
|
ProviderSettings | None
|
Required when |
None
|
Raises:
| Type | Description |
|---|---|
RelayError
|
|
admission ¶
admission() -> AdmissionController
This relay's admission controller, for provisioning and for reading budgets.
anyinfer_confidential.relay.RelayRoute
dataclass
¶
RelayRoute(
routing_key: str,
template: EncryptedTemplate,
target: str,
)
One vendor-configured orchestration route.
Attributes:
| Name | Type | Description |
|---|---|---|
routing_key |
str
|
What a client request selects this route by. |
template |
EncryptedTemplate
|
The sealed template this route renders. |
target |
str
|
The |
anyinfer_confidential.relay.RelayResult
dataclass
¶
RelayResult(
assembled_prompt: str,
generation_text: str | None,
target: str,
latency_ms: float,
)
What one Relay call returns — held in memory only, never written to disk here.
Attributes:
| Name | Type | Description |
|---|---|---|
assembled_prompt |
str
|
The rendered prompt text, present for both modes (a caller in
|
generation_text |
str | None
|
The provider's response text, only in |
target |
str
|
The target the request was (or would be) dispatched to. |
latency_ms |
float
|
Time spent inside |
anyinfer_confidential.relay.RelayRegistry ¶
RelayRegistry()
Tenant-scoped route storage — the structural boundary multi-tenant isolation rests on.
set_limits ¶
set_limits(tenant_id: str, limits: TenantLimits) -> None
Record one tenant's admission bounds alongside its routes.
Held here rather than only on the controller because this file is the provisioning path: a deployment that describes its tenants in one document should describe their capacity there too, not in a second script that has to be kept in step.
limits ¶
limits() -> dict[str, TenantLimits]
Every provisioned tenant's bounds, for a Relay to install.
register ¶
register(tenant_id: str, route: RelayRoute) -> None
Provision one route under one tenant.
resolve ¶
resolve(tenant_id: str, routing_key: str) -> RelayRoute
Look up a route, scoped strictly to tenant_id.
Raises:
| Type | Description |
|---|---|
RelayError
|
No route named |
anyinfer_confidential.relay.load_registry ¶
load_registry(path: str | Path) -> RelayRegistry
Build a RelayRegistry from a JSON provisioning file.
The registry is in-memory and has no persistence of its own, which left every deployment hand-writing the same registration loop. This is that loop, so a self-hosted relay can be provisioned from a file under configuration management rather than from a bespoke script.
The file holds sealed templates — ciphertext — so it is not itself secret material
and can live in a config repository. Decryption still requires the deployment's
TemplateVault, its key ring, and a valid license.
Expected shape::
{
"tenants": {
"acme": {
"limits": {"max_in_flight": 8, "max_waiting": 32},
"routes": [
{
"routing_key": "summarize",
"target": "anthropic:claude-sonnet-4-5",
"template": { ... EncryptedTemplate.to_json() payload ... }
}
]
}
}
}
A tenant may also map directly to a bare list of routes, which is the original shape and means routes-only. Both are supported permanently: this file is a provisioning input under configuration management, and silently requiring a rewrite of every deployment's file to add an optional block would be a poor trade for the tidier grammar.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
The JSON provisioning file. |
required |
Returns:
| Type | Description |
|---|---|
RelayRegistry
|
A registry with every listed route provisioned under its tenant, and every stated |
RelayRegistry
|
limit recorded on it. |
Raises:
| Type | Description |
|---|---|
RelayError
|
The file is malformed, or a route entry is missing a required field. |
anyinfer_confidential.app.build_app ¶
build_app(
relay: Relay,
*,
tokens: Mapping[str, str],
max_request_bytes: int = DEFAULT_MAX_REQUEST_BYTES,
) -> Any
Build a Starlette app exposing relay at POST /v1/relay/assemble.
A vendor's own script constructs the bound Relay (its TemplateVault and
RelayRegistry are deployment-specific) and serves the result with any standard ASGI
server, e.g. uvicorn.run(build_app(my_relay, tokens=my_tokens), ...) — there is no
bundled zero-configuration entry point, since the route registry always needs real
provisioning first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
relay
|
Relay
|
The bound |
required |
max_request_bytes
|
int
|
Refuse a request body larger than this with 413. Enforced
while reading rather than from |
DEFAULT_MAX_REQUEST_BYTES
|
tokens
|
Mapping[str, str]
|
Maps bearer token to the |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Note
mode="forward" is not reachable over HTTP. Forwarding needs short-lived
provider credentials that this endpoint deliberately does not accept on the wire;
a forward-mode request is answered with 400. Call Relay.handle in-process for
that mode.
Composing a Report¶
anyinfer_shared.ConfidentialityReport
dataclass
¶
ConfidentialityReport(
template_sealed: bool | None = None,
relay_used: bool | None = None,
relay_self_hosted: bool | None = None,
execution_attested: bool | None = None,
cpu_tee: CpuTeeKind | None = None,
model_verified: bool | None = None,
notes: tuple[str, ...] = (),
)
A composite record of every confidentiality guarantee that applied to one call.
Every field is optional: a caller using only one tier's package leaves the other
tier's fields None rather than fabricating a false negative. None always means
"not evaluated," never "not confidential" — a caller distinguishing "we didn't check"
from "we checked and it failed" reads the difference directly off which fields are
populated versus False.
Attributes:
| Name | Type | Description |
|---|---|---|
template_sealed |
bool | None
|
Tier 1 — the prompt was assembled from a |
relay_used |
bool | None
|
Tier 2 — request assembly happened inside an |
relay_self_hosted |
bool | None
|
Whether the Relay instance was self-hosted by the caller's own
organization, when |
execution_attested |
bool | None
|
Tier 3 — mirrors
|
cpu_tee |
CpuTeeKind | None
|
The detected CPU TEE family backing |
model_verified |
bool | None
|
Tier 4 — the executing model's weights were checked against a
vendor-signed manifest inside the same attested boundary |
notes |
tuple[str, ...]
|
Human-readable caveats — e.g. why a field could not be evaluated — attributable to a specific tier without inventing new typed fields for every possible caveat. |
from_status
classmethod
¶
from_status(
status: _ExecutionStatus,
*,
template_sealed: bool | None = None,
relay_used: bool | None = None,
relay_self_hosted: bool | None = None,
include_detail: bool = True,
) -> ConfidentialityReport
Compose a report from a core attestation status plus the Tier 1-2 facts.
The one demonstrated producer of this type. Tier 3 and Tier 4 come off status
— the object anyinfer.local.attestation.confidential_execution_status() returns
— while Tiers 1 and 2 are facts only the calling application knows, because
whether a prompt came from a sealed template or through a Relay is not something
the local runtime can observe.
status is taken structurally, so this package still never imports anyinfer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
status
|
_ExecutionStatus
|
A |
required |
template_sealed
|
bool | None
|
Tier 1 — did this call render from a |
None
|
relay_used
|
bool | None
|
Tier 2 — was assembly done by an |
None
|
relay_self_hosted
|
bool | None
|
Whether that Relay was the caller's own deployment. |
None
|
include_detail
|
bool
|
Carry |
True
|
Returns:
| Type | Description |
|---|---|
ConfidentialityReport
|
A populated report. Tier 3/4 fields always reflect |
ConfidentialityReport
|
reflect exactly what the caller passed, |
to_dict ¶
to_dict() -> dict[str, Any]
A plain JSON-safe mapping, for logging or a compliance-mapping artifact.
from_dict
classmethod
¶
from_dict(data: dict[str, Any]) -> ConfidentialityReport
Round-trip counterpart to to_dict; unknown keys are ignored (forward-safe).
Errors¶
anyinfer_confidential.ConfidentialError ¶
Bases: Exception
Base class for every error this package raises.
anyinfer_confidential.SealError ¶
Bases: ConfidentialError
A sealed-template asset is malformed or could not be produced.
anyinfer_confidential.TemplateDecryptionError ¶
Bases: ConfidentialError
A sealed template could not be decrypted: wrong key, or tampered ciphertext.
anyinfer_confidential.LicenseError ¶
Bases: ConfidentialError
A license blob is missing, malformed, unsigned by the expected key, or expired.
anyinfer_confidential.RevokedLicenseError ¶
Bases: ConfidentialError
Revocation checking found the license revoked, or a fail-closed check failed.
anyinfer_confidential.relay.RelayError ¶
Bases: Exception
Base class for Relay-specific errors (unknown route, wrong tenant, etc.).