Skip to main content

Official Python SDK for the QNSI Quantum-Native Security Infrastructure — post-quantum cryptography (ML-KEM, ML-DSA, SLH-DSA, Falcon via liboqs), PQC-encrypted vault, KMS, and immutable audit trails. Mirrors the @heossi/qnsi-* TypeScript SDKs.

Project description

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 @heossi/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 QnspClient

with QnspClient(api_key=os.environ["QNSP_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_qnsp_webhook, QnspWebhookError

app = FastAPI()

@app.post("/webhooks/qnsi")
async def receive(request: Request) -> dict:
    body = await request.body()
    try:
        event = parse_qnsp_webhook(
            body=body,
            signature_header=request.headers.get("x-qnsp-signature", ""),
            timestamp_header=request.headers.get("x-qnsp-timestamp"),
            secret=os.environ["QNSP_WEBHOOK_SECRET"],
        )
    except QnspWebhookError 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 qnsp.QnspError:

Class When
QnspNetworkError DNS, TLS, timeout, or connection failure
QnspAuthError API key rejected at activation
QnspApiError A service returned 4xx/5xx with a structured body
QnspWebhookError HMAC mismatch, expired timestamp, malformed body, etc.
from qnsi import QnspApiError, QnspNetworkError

try:
    qnsi.vault.get_secret("missing")
except QnspApiError as exc:
    print("HTTP", exc.status_code, exc.code, exc.body)
except QnspNetworkError as exc:
    print("Could not reach QNSI:", exc)

Activation + tier introspection

QnspClient 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
  • qnsp.parse_qnsp_webhook / qnsp.verify_qnsp_webhook_signature — HMAC-SHA-256 verify + replay protection
  • qnsp.QnspClient — API-key activation with caching and 401 retry

What's coming

  • AsyncQnspClient — 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.

Project details


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.3.1.tar.gz (25.0 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.3.1-py3-none-any.whl (35.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: qnsi-0.3.1.tar.gz
  • Upload date:
  • Size: 25.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for qnsi-0.3.1.tar.gz
Algorithm Hash digest
SHA256 b92690431f4b61ba8ace3e0b91ac034a678b0a1d3a554363fb89c8e9850527a3
MD5 aab1a4333fee7678514ee25fa22f069a
BLAKE2b-256 4167ad5ef404e98d84e5c170bb809158259b181381eab7293863dd0c16428594

See more details on using hashes here.

File details

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

File metadata

  • Download URL: qnsi-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 35.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for qnsi-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5d49956f32725434a137f25c655886ae4aed978aef81c5da87dc512d43da538f
MD5 a8fdb07da1d62ba6600de077206e2c1a
BLAKE2b-256 87a755390ed854d26a1b2758691a2990c5a5a0b99376fe89b2da127d680f4af6

See more details on using hashes here.

Supported by

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