Skip to main content

qnsi - Python SDK for the Quantum-Native Security Infrastructure

PyPI version Python versions License

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 auth
  • qnsi.tenant - tenant CRUD, crypto-policy management, current-health, current-quotas
  • qnsi.access - RBAC roles, role assignments, check_permission
  • qnsi.billing - entitlements, usage meters (single + batch), invoice listing, credit balance
  • qnsi.crypto_inventory - Cryptographic Bill of Materials: assets, discovery runs, PQC readiness
  • qnsi.storage - PQC-encrypted object storage with SSE-X
  • qnsi.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 (requires qnsi[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.0
  • qnsi.parse_qnsi_webhook / qnsi.verify_qnsi_webhook_signature - HMAC-SHA-256 verify + replay protection
  • qnsi.QnsiClient - API-key activation with caching and 401 retry

What's coming

  • AsyncQnsiClient - native-async variants using httpx.AsyncClient
  • A pytest plugin 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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qnsi-0.4.3.tar.gz (26.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qnsi-0.4.3-py3-none-any.whl (37.6 kB view details)

Uploaded Python 3

File details

Details for the file qnsi-0.4.3.tar.gz.

File metadata

  • Download URL: qnsi-0.4.3.tar.gz
  • Upload date:
  • Size: 26.8 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for qnsi-0.4.3.tar.gz
Algorithm Hash digest
SHA256 f54b5a9a69f61c80a2476e15fe90b7cf2490403cf3a3a074de6aeea643de5d48
MD5 3a93e2fec715dd7c10413139ff1dca1e
BLAKE2b-256 fdeb2b640e6db3f4875bb8f67833f2472ce1b76e791e614614f45869885e0497

See more details on using hashes here.

File details

Details for the file qnsi-0.4.3-py3-none-any.whl.

File metadata

  • Download URL: qnsi-0.4.3-py3-none-any.whl
  • Upload date:
  • Size: 37.6 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for qnsi-0.4.3-py3-none-any.whl
Algorithm Hash digest
SHA256 bd35a009f4e50b018bb58fabdc7262627e7155bdbe1b31d4cd497fee86807d1c
MD5 521bce1a756eb82642148e77e1665684
BLAKE2b-256 1c8d7320a851c5b550c2ab0d597118853f189c075bad89c98510eece7fd23da8

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.4.3 This release

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page