Python SDK reference¶
Audience: Python engineers integrating Nanorix into a healthcare-AI / claims / legal-AI / agent backend. Package:
nanorix(v0.5.0). The 1.0 contract freeze is planned; the current surface is stable and additive. Python: 3.9 – 3.13. Substrate framing. The SDK wraps the HTTP API documented in api-reference.md. Every public method maps 1:1 to one or more API endpoints. The SDK adds: typed exceptions, exponential-backoff retries, automatic environment auth, and helper modules for verification, signing, and compliance metadata.
Install¶
Or from source: github.com/nanorix-io/nanorix-sdk.
Dependencies: httpx>=0.25,<1.0, pydantic>=2.0,<3.0, cryptography>=41.0,<46.0.
Top-level surface¶
import nanorix
# Construction
client = nanorix.Client(api_key="nrx_live_...") # explicit
client = nanorix.Client() # auto-loads NANORIX_API_KEY
client = nanorix.Client(base_url="https://nanorix.io",
timeout=30.0, max_retries=4) # defaults
# Async variant — same surface, same method names; coroutines instead of plain returns
async_client = nanorix.AsyncClient()
# Resources
client.capsulefiles # Capsulefile lifecycle
client.capsules # capsule lifecycle + .run(), .batch(), .session()
client.cdp # AuditProof retrieval (alias: client.audit_proofs)
client.audit_proofs # alias of .cdp
client.egress_profiles # named egress allowlists
client.usage # usage summary, line-items, CSV export
client.webhooks # webhook endpoints + deliveries
# Subpackages
nanorix.helpers # signer, checkpoint_saver, activity_events
nanorix.verifier # verify_auditproof()
nanorix.compliance # framework-typed metadata builders (HIPAA, GDPR, SOC 2)
# Top-level
client.health() # GET /v1/health
client.close() # release HTTP transport
Client is also a context manager:
Exceptions¶
Full reference: error catalog.
from nanorix import (
NanorixError, # base
AuthenticationError, # 401
NotFoundError, # 404
ConflictError, # 409
BadRequestError, # 400
QuotaExceededError, # 402, 403 (quota)
PayloadTooLargeError, # 413
GoneError, # 410 (capsule destroyed)
RateLimitError, # 429 (after auto-retry budget exhausted)
UnprocessableEntityError, # 422 default
InputProvenanceRejectedError, # 422 input_provenance_rejected
SecretsInjectionRejectedError, # 422 secrets_injection_rejected
ExecutionError, # non-zero exit; .cdp attached if destroyed
CeilingError, # safety-ceiling destroy; .cdp attached
TimeoutError, # exec timeout; .capsule attached
)
Exceptions are importable from the package root (from nanorix import ...).
Every exception carries status_code: Optional[int] and body: Optional[Dict[str, Any]]. The body["error"]["request_id"] field is the support-ticket correlation id; quote it when filing.
client.capsulefiles¶
Class: Capsulefiles (sync) / AsyncCapsulefiles (async).
create(...)¶
def create(
self,
*,
name: str,
runtime: str,
entrypoint: str,
files: Optional[Union[List[str], Dict[str, bytes]]] = None,
data_classification: Optional[str] = None,
egress_declaration: Optional[List[str]] = None,
default_ttl: Optional[int] = None,
memory_mb: Optional[int] = None,
env_vars: Optional[Dict[str, str]] = None,
packages: Optional[Dict[str, List[str]]] = None,
finalize: bool = True,
) -> Capsulefile
Creates a Capsulefile, uploads source files, and (by default) finalizes it. Maps to POST /v1/capsulefiles + POST /v1/capsulefiles/:id/files + POST /v1/capsulefiles/:id/finalize.
| Argument | Type | Notes |
|---|---|---|
name |
str | Globally unique within your account. |
runtime |
str | e.g., python3.12, node20. |
entrypoint |
str | Shell command. |
files |
list[str] OR dict[str, bytes] | Either local paths to upload or in-memory {filename: bytes}. |
data_classification |
str | general, PHI, PII, financial, credentials. Customer declaration. |
egress_declaration |
list[str] | Hostnames / IPs / CIDRs allowlisted. |
default_ttl |
int | Seconds; capped by tier max_ttl_seconds. |
memory_mb |
int | Per-capsule memory ceiling. |
env_vars |
dict | Available inside the sealed sandbox. |
packages |
dict | e.g., {"python": ["anthropic==0.39.0"]}. |
finalize |
bool | Default True; set False to upload more files manually before finalizing. |
Returns: Capsulefile model with .id, .status, .content_hash, .name, etc.
Raises: BadRequestError (schema invalid), ConflictError (name in use), QuotaExceededError.
Example:
cf = client.capsulefiles.create(
name="med-ai-extraction",
runtime="python3.12",
entrypoint="python main.py",
files=["main.py"],
data_classification="PHI",
egress_declaration=["api.anthropic.com"],
default_ttl=1800,
memory_mb=2048,
env_vars={"ANTHROPIC_MODEL": "claude-3-5-sonnet-latest"},
packages={"python": ["anthropic==0.39.0", "pypdf==5.0.1"]},
)
print(cf.id, cf.content_hash)
list(...) / get(id) / delete(id)¶
Standard list/get/delete. Maps to GET /v1/capsulefiles, GET /v1/capsulefiles/:id, DELETE /v1/capsulefiles/:id.
for cf in client.capsulefiles.list():
print(cf.id, cf.name, cf.status)
cf = client.capsulefiles.get("cf_018f...")
client.capsulefiles.delete("cf_018f...")
update(id, **kwargs)¶
Mutates a draft Capsulefile (cannot mutate finalized). Returns the updated Capsulefile.
wait_until_finalized(id, timeout: float = 60.0)¶
Polls until status transitions from draft to finalized. Useful when finalize is async or running on the server.
upload_dir(id, dir_path)¶
Convenience: uploads every file under dir_path recursively. Returns the file count.
client.capsules¶
Class: Capsules (sync) / AsyncCapsules (async).
create(...) → LiveCapsule¶
def create(
self,
*,
capsulefile: Optional[str] = None,
max_lifetime_seconds: Optional[int] = None,
egress_profile: Optional[str] = None,
metadata: Optional[Dict[str, str]] = None,
) -> LiveCapsule
Creates a capsule. Returns a LiveCapsule with workspace methods (.upload(), .execute(), .list_outputs(), .download(), .destroy(), .refresh()).
metadata binds customer-declared context into the AuditProof's metadata_hash. Use nanorix.compliance to construct typed framework context (HIPAA, GDPR, SOC 2) or pass a plain Dict[str, str]. Server constraints: 16 keys max, 64-char keys, 256-char values, 4 KB total. The nanorix:* prefix is reserved.
Maps to: POST /v1/capsules.
run(...) → (outputs, cdp)¶
def run(
self,
*,
capsulefile: str,
inputs: Optional[Dict[str, Union[bytes, BinaryIO]]] = None,
command: Optional[str] = None,
timeout: Optional[int] = None,
max_lifetime_seconds: Optional[int] = None,
metadata: Optional[Dict[str, str]] = None,
) -> Tuple[Dict[str, bytes], CdpAccess]
One-shot lifecycle: create → upload → execute → download → destroy.
Returns: (outputs, cdp) where:
outputs—Dict[str, bytes]mapping output filename → content.cdp—CdpAccesswith.full()(the AuditRecord) and.verification()(the AuditProof projection).
Raises: ExecutionError if the customer entrypoint exits non-zero; the AuditProof is attached at .cdp because destruction still happened.
Example (Pattern 3, single-shot extraction):
outputs, cdp = client.capsules.run(
capsulefile="cf_018f...",
inputs={"input.pdf": open("test.pdf", "rb").read()},
metadata={"matter_id": "ACME-001"},
)
print(outputs["extraction.json"][:200])
print("AuditProof id:", cdp.full().id)
print("final_hash:", cdp.full().final_hash)
batch(...) → BatchResult¶
Anthropic-Batches-shape batch processing. Per-record custom_id discipline + RFC 6962 Merkle-root computed client-side for byte-equivalent verifier replay.
def batch(
self,
*,
capsulefile: str,
records: Iterable[Dict[str, Any]],
on_record: Optional[Callable[[BatchRecord], None]] = None,
record_id_field: str = "custom_id",
on_record_error: str = "fail_isolated", # or "abort_batch"
max_lifetime_seconds: Optional[int] = None,
metadata: Optional[Dict[str, str]] = None,
timeout: Optional[int] = None,
) -> BatchResult
Returns: BatchResult with .merkle_root, .audit_proof, .records (per-record outcomes), .success_count, .error_count.
The customer entrypoint reads /data/input/records.jsonl (one JSON per line, each carrying custom_id) and writes /data/output/results.jsonl with {custom_id, status: "succeeded"|"errored", output, error_code, error_message}.
Use for: Pattern 4 / 6 (claims, e-discovery, batch extraction).
session(...) → SessionResult¶
LangGraph-shape session-mode for stateful multi-capsule reasoning chains. Customer-managed encrypted state continuity via parent_cdp_id chain.
def session(
self,
*,
capsulefile: str,
session_id: str,
parent_cdp_id: Optional[str] = None,
initial_state: Optional[bytes] = None,
max_lifetime_seconds: Optional[int] = None,
metadata: Optional[Dict[str, str]] = None,
timeout: Optional[int] = None,
) -> SessionResult
Returns: SessionResult with .cdp, .state_output_bytes, .parent_cdp_id.
The customer entrypoint reads /data/input/state.bin and writes /data/output/state.bin. Customer is responsible for KMS-encryption of state bytes BEFORE upload and AFTER download. Use for: Pattern 2 (long-running agent), Pattern 4 (multi-step reasoning).
list() / get(id)¶
list() → CapsuleList (paginated). get(id) → Capsule model.
LiveCapsule workspace methods¶
LiveCapsule is the handle returned by create(). Methods:
| Method | Maps to |
|---|---|
.id (property) |
capsule id |
.status (property) |
current status |
.upload(filename, data) |
POST /v1/capsules/:id/upload?filename=... |
.execute(command=None, input_data=None, timeout=None) |
POST /v1/capsules/:id/exec |
.list_outputs() |
GET /v1/capsules/:id/output |
.download(filename) |
GET /v1/capsules/:id/output/:filename |
.destroy() → CdpAccess |
DELETE /v1/capsules/:id |
.refresh() → LiveCapsule |
GET /v1/capsules/:id |
Workspace-mode example (capsule stays alive across multiple exec calls):
capsule = client.capsules.create(capsulefile="cf_018f...")
capsule.upload("input.pdf", pdf_bytes)
result = capsule.execute(command="python main.py", timeout=60)
print(result.stdout)
# Iterate without re-uploading
result2 = capsule.execute(command="python main.py --verbose")
# Destroy and get the AuditProof
cdp = capsule.destroy()
print(cdp.full().final_hash)
client.cdp (alias: client.audit_proofs)¶
Class: CdpResource / AsyncCdpResource.
def get(self, capsule_id: str) -> CdpAccess
def verify(self, capsule_id: str) -> AuditProof # the verification proof object
get() retrieves the AuditRecord for a destroyed capsule (GET /v1/capsules/:id/cdp); verify() retrieves the shareable AuditProof projection (GET /v1/capsules/:id/cdp/verify).
CdpAccess exposes:
.full() → CdpDocument— the AuditRecord (private complete record)..verification() → AuditProof— the shareable verification proof object..id,.capsule_id,.final_hash,.created_at— top-level fields.
client.audit_proofs is the brand-aligned alias; both names point at the same resource and will coexist forever for forever-stability.
client.egress_profiles¶
Class: EgressProfiles / AsyncEgressProfiles. Tier: Team+.
client.egress_profiles.create(name="...", description="...")
client.egress_profiles.list()
client.egress_profiles.get(profile_id)
client.egress_profiles.update(profile_id, name=None, description=None)
client.egress_profiles.delete(profile_id)
client.egress_profiles.add_rule(profile_id, kind="domain", value="api.example.com", port=443)
client.egress_profiles.list_rules(profile_id)
client.egress_profiles.remove_rule(profile_id, rule_id)
Maps to: /v1/egress/profiles/....
client.webhooks¶
Class: Webhooks / AsyncWebhooks.
client.webhooks.create(url="https://...", events=["cdp.generated"], description="...")
client.webhooks.list()
client.webhooks.get(webhook_id)
client.webhooks.update(webhook_id, url=None, events=None, description=None, active=None)
client.webhooks.delete(webhook_id)
client.webhooks.test(webhook_id)
client.webhooks.deliveries(webhook_id)
The signing-secret returned from create() is shown once. Use nanorix.helpers.Webhook.verify_signature() (see below) in your handler.
from nanorix import Webhook
if not Webhook.verify_signature(
payload=request.body,
signature_header=request.headers["Nanorix-Webhook-Signature"],
signing_secret=os.environ["NANORIX_WEBHOOK_SECRET"],
):
return 401
Maps to: /v1/webhooks/.... Full event-type catalog at integrations/webhooks.md.
client.usage¶
Class: Usage / AsyncUsage.
client.usage.summary(billing_cycle: Optional[str] = None) -> UsageSummary
client.usage.estimate() -> Dict[str, Any]
client.usage.list_items(billing_cycle=None, **filters) -> Iterator[UsageItem]
client.usage.export_csv(billing_cycle=None, **filters) -> bytes
list_items() yields UsageItem records (paginated automatically). export_csv() returns the raw CSV bytes — same shape as the API's /v1/usage/items.csv.
Maps to: /v1/usage/....
nanorix.helpers¶
| Module | Exports |
|---|---|
nanorix.helpers.signer |
Signer — Ed25519 over capsule input bytes (input-provenance manifest signing) |
nanorix.helpers.checkpoint_saver |
LocalCheckpointSaver — local-disk fallback for client.capsules.session() flows |
nanorix.helpers.activity_events |
ActivityEvent parsers — typed accessors for AuditProof's activity trail |
from nanorix.helpers import Signer
signer = Signer.from_env() # reads NANORIX_SIGNING_KEY (PEM)
manifest_hash, manifest_signature = signer.sign_input(open("input.pdf", "rb").read())
nanorix.verifier¶
from nanorix.verifier import verify_auditproof, AuditProofVerificationResult, VerifierPolicy
with open("auditproof.json") as f:
proof = json.load(f)
result = verify_auditproof(
proof,
policy=VerifierPolicy(
reject_diagnostic=True,
required_region="us-central1",
required_authority_id=None,
),
)
if not result.valid:
print("failure:", result.failure_reason.type)
print("stage_reached:", result.stage_reached)
Cross-impl byte-equivalence: verify_auditproof() produces output byte-equivalent to the Rust nanorix-verify and Go auditproof-verifier-go CLIs on the 100-fixture reference corpus. The FailureReason enum is identical across all five paths (Rust, Go, Python, TypeScript, server-side POST /v1/verify).
The failure-reason enum is closed-set and byte-equivalent across all verifier implementations.
nanorix.compliance¶
Framework-typed metadata builders that produce Dict[str, str] payloads for metadata=... arguments above. Bind customer-declared context that lands in the AuditProof's metadata_hash.
from nanorix.compliance import HIPAAContext, GDPRContext, SOC2Context
metadata = HIPAAContext(
covered_entity_id="ce-001",
business_associate_id="ba-002",
treatment_purpose=True,
).to_metadata()
outputs, cdp = client.capsules.run(
capsulefile="cf_...",
inputs={"input.pdf": pdf_bytes},
metadata=metadata,
)
The contexts cover the framework-typed surface that procurement teams expect to see traced in the AuditProof. The metadata is customer-defined; the compliance helpers are Nanorix-published convenience builders, not Nanorix-imposed schemas.
Async client¶
nanorix.AsyncClient exposes the same surface as Client with async def coroutines and AsyncIterator for paginated endpoints:
import nanorix
async with nanorix.AsyncClient() as client:
cf = await client.capsulefiles.create(...)
outputs, cdp = await client.capsules.run(capsulefile=cf.id, inputs=...)
async for cap in client.capsules.list():
print(cap.id, cap.status)
End-to-end mapping to the API surface¶
Every Python SDK method maps to one or more API endpoints documented in api-reference.md:
| Python method | API endpoint |
|---|---|
Client.health() |
GET /v1/health |
client.capsulefiles.create() |
POST /v1/capsulefiles (+ files + finalize) |
client.capsulefiles.list() |
GET /v1/capsulefiles |
client.capsulefiles.get(id) |
GET /v1/capsulefiles/:id |
client.capsulefiles.delete(id) |
DELETE /v1/capsulefiles/:id |
client.capsules.create() |
POST /v1/capsules |
LiveCapsule.upload() |
POST /v1/capsules/:id/upload |
LiveCapsule.execute() |
POST /v1/capsules/:id/exec |
LiveCapsule.list_outputs() |
GET /v1/capsules/:id/output |
LiveCapsule.download() |
GET /v1/capsules/:id/output/:filename |
LiveCapsule.destroy() |
DELETE /v1/capsules/:id |
client.capsules.list() |
GET /v1/capsules |
client.capsules.get(id) |
GET /v1/capsules/:id |
client.cdp.get() / client.audit_proofs.get() |
GET /v1/capsules/:id/cdp |
client.cdp.verify() |
GET /v1/capsules/:id/cdp/verify |
client.egress_profiles.* |
/v1/egress/profiles/... |
client.webhooks.* |
/v1/webhooks/... |
client.usage.* |
/v1/usage/... |
nanorix.verifier.verify_auditproof() |
offline (no network) |
client.capsules.run(), .batch(), and .session() are SDK-side compositions over create + upload + exec + destroy, not separate endpoints.
Cross-references¶
- API reference — every endpoint with full request/response schemas.
- Error catalog — every error code, cause, and customer-side fix.
- TypeScript SDK reference — mirror surface in TypeScript.
- Webhooks — full HMAC verification recipe.
- AuditProof specification — full AuditProof / AuditRecord schema.
Forever-Standard discipline. Every public Python SDK method, every keyword argument, every exception class, every model field is permanent. New methods / args / classes / fields are additive only. The internal-vs-external naming alias (
client.cdp↔client.audit_proofs) is structural, not transitional — both names exist forever.