qnsi - Python SDK for the Quantum-Native Security Infrastructure
Typed Python client for QNSI - post-quantum cryptography (ML-KEM, ML-DSA, SLH-DSA, Falcon via liboqs), PQC-encrypted vault, server-side KMS, immutable audit trails. Same wire contracts as the official @heossihq/qnsi-* TypeScript SDKs - pick whichever language fits your stack and the byte-for-byte outputs round-trip.
Free tier available. Free-forever account at https://cloud.qnsi.heossi.com/auth - 60-second signup, no credit card. Includes 10 GB PQC storage, 50 000 API calls/month, 20 KMS keys, 25 vault secrets.
Installation
Base install (HTTP clients for vault, KMS, audit):
pip install qnsi
With local PQC primitives (qnsi.crypto - wraps liboqs-python 0.12.0):
pip install 'qnsi[crypto]'
liboqs-python requires the liboqs C library available on the host. Easiest paths:
| Platform | Command |
|---|---|
| macOS | brew install liboqs |
| Debian/Ubuntu | apt install liboqs-dev |
| From source | cmake -DBUILD_SHARED_LIBS=ON ... - see https://github.com/open-quantum-safe/liboqs |
(A v0.3.x release will ship cibuildwheel-built wheels that bundle a self-contained liboqs binary, removing the system prerequisite.)
Requires Python 3.10+ and httpx. Tested on CPython 3.10, 3.11, 3.12, 3.13.
Quick start
import os
import base64
from qnsi import QnsiClient
with QnsiClient(api_key=os.environ["QNSI_API_KEY"]) as qnsi:
# ── Vault - PQC-encrypted secret storage ─────────────────────────
secret = qnsi.vault.create_secret(
name="openai-api-key",
payload_b64=base64.b64encode(b"sk-...").decode(),
algorithm="ml-kem-768",
)
fresh = qnsi.vault.get_secret(secret["id"])
# ── KMS - server-side PQC keys ──────────────────────────────────
key = qnsi.kms.create_key(algorithm="ml-dsa-65", purpose="signing")
signature = qnsi.kms.sign(key["keyId"], data=b"hello")
assert qnsi.kms.verify(key["keyId"], data=b"hello", signature=signature)
# ── Audit - immutable, hash-chained event log ───────────────────
qnsi.audit.log_event(
event_type="model.inference",
payload={"modelId": "gpt-4o", "latencyMs": 412},
)
# ── New in 0.3.0 - full parity with Go and Rust SDKs ────────────
qnsi.tenant.get_tenant(qnsi.tenant_id)
qnsi.access.check_permission(subject_id="user-1", permission="vault.read")
qnsi.billing.get_entitlements()
qnsi.crypto_inventory.get_readiness_score(qnsi.tenant_id)
qnsi.storage.put_object("uploads", "report.pdf", data=b"...")
qnsi.search.query("docs", vector=[0.1] * 768, top_k=5)
qnsi.ai.invoke_inference(model_id="gpt-4o", input={"prompt": "..."})
qnsi.auth.login(email="user@example.com", password="...", tenant_id=qnsi.tenant_id)
Local PQC primitives
qnsi.crypto wraps liboqs-python so you don't have to write oqs calls yourself, and so the algorithm-name surface matches the rest of the QNSI ecosystem (TypeScript, Go, Rust):
from qnsi.crypto import MlKem, MlDsa, SlhDsa, Falcon
# Module-Lattice KEM (FIPS 203)
kem = MlKem("ML-KEM-768")
pk, sk = kem.keygen()
enc = kem.encapsulate(pk)
recovered = kem.decapsulate(enc.ciphertext, sk)
assert recovered == enc.shared_secret
# Module-Lattice signatures (FIPS 204)
sig = MlDsa("ML-DSA-65")
sig_pk, sig_sk = sig.keygen()
signature = sig.sign(b"hello", sig_sk)
assert sig.verify(b"hello", signature, sig_pk)
# Stateless hash-based signatures (FIPS 205) - conservative, no lattice assumption
slh = SlhDsa("SLH-DSA-SHA2-128f")
# Compact lattice signatures (NIST PQC selection)
fal = Falcon("Falcon-512")
Sizes match the FIPS specs exactly (the SDK reads them from the linked liboqs build, so no inline literals drift).
Verifying inbound webhooks
QNSI signs every webhook with HMAC-SHA-256. Verify the raw body before parsing JSON:
from fastapi import FastAPI, Request, HTTPException
from qnsi import parse_qnsi_webhook, QnsiWebhookError
app = FastAPI()
@app.post("/webhooks/qnsi")
async def receive(request: Request) -> dict:
body = await request.body()
try:
event = parse_qnsi_webhook(
body=body,
signature_header=request.headers.get("x-qnsp-signature", ""),
timestamp_header=request.headers.get("x-qnsp-timestamp"),
secret=os.environ["QNSI_WEBHOOK_SECRET"],
)
except QnsiWebhookError as exc:
raise HTTPException(400, str(exc))
if event.event_type == "key.rotated":
...
return {"ok": True}
The verifier runs HMAC comparison in constant time, rejects timestamps older than 5 minutes by default (replay protection), and refuses payloads missing required fields.
Error handling
All errors descend from qnsi.QnsiError:
| Class | When |
|---|---|
QnsiNetworkError |
DNS, TLS, timeout, or connection failure |
QnsiAuthError |
API key rejected at activation |
QnsiApiError |
A service returned 4xx/5xx with a structured body |
QnsiWebhookError |
HMAC mismatch, expired timestamp, malformed body, etc. |
from qnsi import QnsiApiError, QnsiNetworkError
try:
qnsi.vault.get_secret("missing")
except QnsiApiError as exc:
print("HTTP", exc.status_code, exc.code, exc.body)
except QnsiNetworkError as exc:
print("Could not reach QNSI:", exc)
Activation + tier introspection
QnsiClient performs a one-shot handshake against /billing/v1/sdk/activate on first use. The result is cached in memory; subsequent calls reuse it until ~1 minute before expiry. You can inspect the current activation:
qnsi.tenant_id # resolved tenant
qnsi.tier # plan tier
qnsi.limits # full limits dict
qnsi.has_feature("sseEnabled") # convenience boolean
If the activation token is rotated server-side, the SDK invalidates its cache and retries the originating request once on a 401.
What's covered today (v0.3.0 - full parity with Go and Rust SDKs)
Customer-facing service modules - every QNSI service callable through the edge gateway:
qnsi.vault- secrets management (create / get / get-version / rotate / delete / list-versions)qnsi.kms- server-side PQC keys (create / list / get / rotate / delete / sign / verify / wrap / unwrap)qnsi.audit- immutable hash-chained event log (log-event / ingest-events / list-events)qnsi.auth- login, refresh, revoke, WebAuthn passkeys, MFA, SAML/OIDC federation, risk-based authqnsi.tenant- tenant CRUD, crypto-policy management, current-health, current-quotasqnsi.access- RBAC roles, role assignments,check_permissionqnsi.billing- entitlements, usage meters (single + batch), invoice listing, credit balanceqnsi.crypto_inventory- Cryptographic Bill of Materials: assets, discovery runs, PQC readinessqnsi.storage- PQC-encrypted object storage with SSE-Xqnsi.search- encrypted vector search (index lifecycle,upsert_vectors,query)qnsi.ai- model registry, AI workloads with enclave attestation,invoke_inference, artifacts
Local primitives + integration:
qnsi.crypto(requiresqnsi[crypto]) - ML-KEM (512/768/1024), ML-DSA (44/65/87), SLH-DSA (8 variants), Falcon (512/1024), plus BIKE, FrodoKEM, Classic-McEliece, MAYO, CROSS - every FIPS 203/204/205 finalist exposed by liboqs 0.12.0qnsi.parse_qnsi_webhook/qnsi.verify_qnsi_webhook_signature- HMAC-SHA-256 verify + replay protectionqnsi.QnsiClient- API-key activation with caching and 401 retry
What's coming
AsyncQnsiClient- native-async variants usinghttpx.AsyncClient- A
pytestplugin that mocks the QNSI API for tests in your codebase - Generated typed responses (currently
dict[str, Any]) for every method
License
Apache-2.0. See LICENSE.
Metadata
Release files for qnsi 0.4.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| qnsi-0.4.3.tar.gz | 26.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qnsi-0.4.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 64.4 kB
Release files / qnsi-0.4.3.tar.gz
| Download URL | qnsi-0.4.3.tar.gz |
|---|---|
| Size | 26.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f54b5a9a69f61c80a2476e15fe90b7cf2490403cf3a3a074de6aeea643de5d48
|
|
BLAKE2b-256 checksum How to use checksums |
fdeb2b640e6db3f4875bb8f67833f2472ce1b76e791e614614f45869885e0497
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / qnsi-0.4.3-py3-none-any.whl
| Download URL | qnsi-0.4.3-py3-none-any.whl |
|---|---|
| Size | 37.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bd35a009f4e50b018bb58fabdc7262627e7155bdbe1b31d4cd497fee86807d1c
|
|
BLAKE2b-256 checksum How to use checksums |
1c8d7320a851c5b550c2ab0d597118853f189c075bad89c98510eece7fd23da8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|